From dcabdde51c3a4b18bfd87e60fc280237b2da677b Mon Sep 17 00:00:00 2001 From: Adr Date: Sun, 8 Jan 2023 23:13:17 +0800 Subject: [PATCH 001/243] minor changes momentary_rot_button: added p2 to solidbsp logic_playerproxy: added OnCoopPing --- fgd/brush/momentary_rot_button.fgd | 2 +- fgd/point/logic/logic_playerproxy.fgd | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fgd/brush/momentary_rot_button.fgd b/fgd/brush/momentary_rot_button.fgd index bff1bccb4..bba75aa6a 100644 --- a/fgd/brush/momentary_rot_button.fgd +++ b/fgd/brush/momentary_rot_button.fgd @@ -98,7 +98,7 @@ 1: "Backward" ] - solidbsp[ASW](boolean) : "Solid BSP" : 0 : "If set, use the SOLID_BSP collision type." + solidbsp[ASW, P2](boolean) : "Solid BSP" : 0 : "If set, use the SOLID_BSP collision type." glow[L4D](target_destination) : "Glow Entity" : : "The name of an entity that will get the +use glow for this button." diff --git a/fgd/point/logic/logic_playerproxy.fgd b/fgd/point/logic/logic_playerproxy.fgd index 234d71bdc..bc7ccc3cc 100644 --- a/fgd/point/logic/logic_playerproxy.fgd +++ b/fgd/point/logic/logic_playerproxy.fgd @@ -78,6 +78,7 @@ output OnPrimaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the primary portal." output OnSecondaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the secondary portal." + output OnCoopPing[P2](void) : "Fired when a coop ping is sent." output OnDuck[since_P2](void) : "Fired when a player starts to duck." output OnUnDuck[since_P2](void) : "Fired when a player releases the duck button." output OnJump[since_P2](void) : "Fired when a player jumps." From 857d0f9a8f079f960f91ac2f4c8abaca4921bb56 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 29 Jan 2023 12:34:58 +1000 Subject: [PATCH 002/243] Clarify how OnTrigger works on trigger_multiple. --- fgd/bases/TriggerOnce.fgd | 3 ++- fgd/brush/trigger/trigger_multiple.fgd | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fgd/bases/TriggerOnce.fgd b/fgd/bases/TriggerOnce.fgd index ab442a4f3..10834abc0 100644 --- a/fgd/bases/TriggerOnce.fgd +++ b/fgd/bases/TriggerOnce.fgd @@ -52,7 +52,8 @@ output OnTouching(void) : "Fired when the TouchTest input is called, and an entity is touching this. Does not call activators." output OnNotTouching(void) : "Fired when the TouchTest input is called, and no entity is touching this. Does not call activators." - // This I/O does actually exist, but it couldn't ever be used usefully. + // This I/O does actually exist, but it couldn't ever be used usefully. + // Entities that can use them should inherit Trigger instead. input EndTouch[engine](void) input DisableAndEndTouch[TF2, engine](void) output OnStartTouchAll[engine](void) // Always fires along with OnStartTouch, so useless. diff --git a/fgd/brush/trigger/trigger_multiple.fgd b/fgd/brush/trigger/trigger_multiple.fgd index d37e9b7ba..e2ec1e9db 100644 --- a/fgd/brush/trigger/trigger_multiple.fgd +++ b/fgd/brush/trigger/trigger_multiple.fgd @@ -1,10 +1,12 @@ @SolidClass base(Trigger) = trigger_multiple: "A trigger volume that can be triggered multiple times." [ - wait[-KZ](float) : "Delay Before Reset" : 1 : "Amount of time, in seconds, after the trigger_multiple has triggered before it can be triggered again. " + - "If set to -1, it will never trigger again (in which case you should just use a trigger_once). If set to 0, it will be reset to 0.2 seconds." - wait[KZ](float) : "Delay Before Reset" : 0.1 : "Amount of time, in seconds, after the trigger_multiple has triggered before it can be triggered again. " + - "If set to -1, it will never trigger again (in which case you should just use a trigger_once). If set to 0, it will be reset to 0.2 seconds." + wait[-KZ](float) : "Delay Before Reset" : 1 : "Amount of time, in seconds, after an initial touch that the trigger_multiple will fire OnTrigger again (if they're still touching). " + + "If set to -1, it will never trigger again (in which case you should just use a trigger_once). If set to 0, it will be reset to 0.2 seconds. " + + "This only affects the OnTrigger output, not OnStartTouch/OnEndTouch and friends." + wait[KZ](float) : "Delay Before Reset" : 0.1 : "Amount of time, in seconds, after the initial touch that the trigger_multiple will fire OnTrigger again. " + + "If set to -1, it will never trigger again (in which case you should just use a trigger_once). If set to 0, it will be reset to 0.2 seconds." + + "This only affects the OnTrigger output, not OnStartTouch/OnEndTouch and friends." entireteam[engine](integer) : "Entire Team Number" : 0 entireteam[L4D, L4D2](choices) : "Entire Team Number" : 0 : "If the entire team is touching, fire OnEntireTeamStartTouch." = @@ -23,8 +25,9 @@ ] // Outputs + output OnTrigger(void) : "Fired repeatedly whenever the trigger is activated, and the wait time has expired." output OnEntireTeamStartTouch[L4D, L4D2](void) : "Fired when an entire team starts touching the trigger." output OnEntireTeamEndTouch[L4D, L4D2](void) : "Fired when an entire team stops touching the trigger." - + @resources [] ] From b96c7ccbbf8b23839e25a2cc6de579a9681f02d7 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 29 Jan 2023 12:35:09 +1000 Subject: [PATCH 003/243] func_wall is a visible brush --- fgd/brush/func/func_wall.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/brush/func/func_wall.fgd b/fgd/brush/func/func_wall.fgd index 0d45f1584..afb78cf07 100644 --- a/fgd/brush/func/func_wall.fgd +++ b/fgd/brush/func/func_wall.fgd @@ -1,4 +1,4 @@ -@SolidClass base(BaseEntityBrush) +@SolidClass base(BaseEntityVisBrush) = func_wall: "Legacy support for Half-Life. Use func_brush instead. A general brush entity." [ @resources [] From 7bb002cba60c5268dec5064e2a4796d1da69b546 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 8 Feb 2023 13:42:03 +1000 Subject: [PATCH 004/243] Use Self instead of explicit typevar --- src/hammeraddons/mdl_compiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hammeraddons/mdl_compiler.py b/src/hammeraddons/mdl_compiler.py index 1dc29bc72..8a0bb2b0f 100644 --- a/src/hammeraddons/mdl_compiler.py +++ b/src/hammeraddons/mdl_compiler.py @@ -4,6 +4,7 @@ We can then reuse already compiled versions. """ from typing import Any, Awaitable, Callable, Generic, Hashable, List, Set, Tuple, TypeVar +from typing_extensions import Self from pathlib import Path import os import pickle @@ -24,7 +25,6 @@ ModelKey = TypeVar('ModelKey', bound=Hashable) InT = TypeVar('InT') OutT = TypeVar('OutT') -ModelCompilerT = TypeVar('ModelCompilerT', bound='ModelCompiler') # TODO: Replace by Self class GenModel(Generic[OutT]): @@ -88,7 +88,7 @@ def use_count(self) -> int: """Return the number of used models.""" return sum(1 for _, mdl in self._built_models if mdl.used) - def __enter__(self: ModelCompilerT) -> ModelCompilerT: + def __enter__(self) -> Self: """Load the previously compiled models and prepare for compiles.""" # Ensure the folder exists. os.makedirs(self.model_folder_abs, exist_ok=True) From 9d1942657b520d08d4bfe0fdf2401c1b7fd3f758 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 11 Feb 2023 11:20:54 +1000 Subject: [PATCH 005/243] Change comp_vactube_start's timer mode to a 3-way field This allows it to be set to start disabled. --- fgd/point/comp/comp_vactube_start.fgd | 9 ++++++++- transforms/vactubes/__init__.py | 2 +- transforms/vactubes/nodes.py | 7 +++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/fgd/point/comp/comp_vactube_start.fgd b/fgd/point/comp/comp_vactube_start.fgd index aef8b8cf3..3d10ad2be 100644 --- a/fgd/point/comp/comp_vactube_start.fgd +++ b/fgd/point/comp/comp_vactube_start.fgd @@ -9,7 +9,14 @@ speed(float) : "Object Speed" : 800.0 : "Set the speed of the objects produced from here, in units per second." seed(string) : "Rotation Seed" : : "If set, consistently produce the same rotation pattern as the seed. " + "If not set, a random seed will be selected (and printed to the compile log)." - timer(boolean) : "Trigger automatically" : 1 : "If true, generate a logic_timer to automatically trigger with a random time." + + timer[engine](integer) : "Activation Mode" : 1 + timer(choices) : "Activation Mode" : 1 : "Controls whether a logic_timer will be generated to automatically trigger with a random time." = + [ + 0: "No Timer (ForceSpawn input required)" + 1: "Auto Timer" + 2: "Auto Timer, starts disabled" + ] time_min(float) : "Minimum Time" : 0.15 : "The minimum time between objects." time_max(float) : "Maximum Time" : 0.5 : "The maximum time between objects." diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index 49279ff3d..244236c92 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -318,7 +318,7 @@ async def vactube_transform(ctx: Context) -> None: 'logic_timer', targetname=spawn_name + '_timer', origin=start_node.origin, - startdisabled='0', + startdisabled=start_node.timer_start_disabled, userandomtime='1', lowerrandombound=start_node.time_min, upperrandombound=start_node.time_max, diff --git a/transforms/vactubes/nodes.py b/transforms/vactubes/nodes.py index 1df8990a3..961d96b88 100644 --- a/transforms/vactubes/nodes.py +++ b/transforms/vactubes/nodes.py @@ -205,10 +205,11 @@ def __init__(self, ent: Entity) -> None: super().__init__(ent) self.group = ent['group'].casefold().strip() self.speed = srctools.conv_float(ent['speed'], 800.0) - self.is_auto = srctools.conv_bool(ent['timer']) + timer_int = srctools.conv_int(ent['timer'], 1) + self.is_auto = timer_int != 0 self.seed = ent['seed'] if not self.seed: - # Generate a new seed, and notify the user so they can copy it down + # Generate a new seed, and notify the user, so they can copy it down # if they want to use it themselves. self.seed = format(random.getrandbits(64), '08X') LOGGER.info('Spawner "{}" using random seed "{}"', self.name, self.seed) @@ -216,8 +217,10 @@ def __init__(self, ent: Entity) -> None: if self.is_auto: self.time_min = srctools.conv_float(ent['time_min'], 0.5) self.time_max = srctools.conv_float(ent['time_max'], 1.0) + self.timer_start_disabled = timer_int == 2 else: self.time_min = self.time_max = 0.0 + self.timer_start_disabled = True # Strip these keyvalues. del ent['speed'], ent['timer'], ent['time_min'], ent['time_max'] From c07e0442ce15163e55935f4549b3d334dbc93f2b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 11 Feb 2023 11:49:41 +1000 Subject: [PATCH 006/243] OnProxyRelay outputs are engine-only, they shouldn't be in the FGD --- fgd/point/func/func_instance_io_proxy.fgd | 58 +++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/fgd/point/func/func_instance_io_proxy.fgd b/fgd/point/func/func_instance_io_proxy.fgd index 03e9c23a6..259116375 100644 --- a/fgd/point/func/func_instance_io_proxy.fgd +++ b/fgd/point/func/func_instance_io_proxy.fgd @@ -33,21 +33,21 @@ input OnProxyRelay13[engine](string) : "This input will cause the corresponding output to be fired." input OnProxyRelay14[engine](string) : "This input will cause the corresponding output to be fired." input OnProxyRelay15[engine](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay16[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay17[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay18[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay19[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay20[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay21[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay22[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay23[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay24[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay25[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay26[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay27[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay28[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay29[engine, since_P2](string) : "This input will cause the corresponding output to be fired." - input OnProxyRelay30[engine, since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay16[engine](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay17[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay18[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay19[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay20[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay21[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay22[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay23[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay24[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay25[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay26[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay27[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay28[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay29[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." + input OnProxyRelay30[engine, +since_P2](string) : "This input will cause the corresponding output to be fired." output OnProxyRelay1[engine](string) : "This will be fired when the corresponding input is triggered." output OnProxyRelay2[engine](string) : "This will be fired when the corresponding input is triggered." @@ -65,20 +65,20 @@ output OnProxyRelay14[engine](string) : "This will be fired when the corresponding input is triggered." output OnProxyRelay15[engine](string) : "This will be fired when the corresponding input is triggered." output OnProxyRelay16[engine](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay17[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay18[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay19[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay20[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay21[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay22[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay23[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay24[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay25[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay26[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay27[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay28[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay29[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." - output OnProxyRelay30[engine, since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay17[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay18[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay19[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay20[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay21[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay22[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay23[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay24[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay25[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay26[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay27[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay28[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay29[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." + output OnProxyRelay30[engine, +since_P2](string) : "This will be fired when the corresponding input is triggered." @resources [] ] From 5ddde02fc3a443bc372f9a50936406126592c267 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 11 Feb 2023 11:52:43 +1000 Subject: [PATCH 007/243] Fix #175: Strip sound characters from precached sounds --- src/hammeraddons/bsp_transform/packing.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hammeraddons/bsp_transform/packing.py b/src/hammeraddons/bsp_transform/packing.py index 511cba893..69985d94c 100644 --- a/src/hammeraddons/bsp_transform/packing.py +++ b/src/hammeraddons/bsp_transform/packing.py @@ -5,6 +5,7 @@ from srctools import Entity from srctools.logger import get_logger from srctools.packlist import FileType, unify_path +from srctools.sndscript import SND_CHARS from hammeraddons.bsp_transform import Context, check_control_enabled, trans @@ -63,7 +64,8 @@ def make_precache_prop(ent: Entity) -> None: @trans('comp_precache_sound', priority=100) def comp_precache_sound(ctx: Context): """Force precaching a set of sounds.""" - sounds = set() + # Match normalised sound to the original filename. + sounds: dict[str, str] = {} for ent in ctx.vmf.by_class['comp_precache_sound']: ent.remove() if not check_control_enabled(ent): @@ -72,29 +74,28 @@ def comp_precache_sound(ctx: Context): for key, sound in ent.items(): if not key.startswith('sound'): continue - sound = sound.casefold().replace('\\', '/') - if sound.endswith(('.wav', '.mp3')) and not sound.startswith('sound/'): - sound = 'sound/' + sound + sound_key = sound.casefold().replace('\\', '/').lstrip(SND_CHARS) + if sound_key.endswith(('.wav', '.mp3')) and not sound_key.startswith('sound/'): + sound_key = 'sound/' + sound_key # Precaching implies packing it. - ctx.pack.pack_file(sound, FileType.GAME_SOUND) + ctx.pack.pack_file(sound_key, FileType.GAME_SOUND) - sounds.add(sound) + sounds.setdefault(sound_key, sound) if not sounds: return # This VScript function forces a script to be precached. lines = SND_CACHE_FUNC % '\n'.join([ - f'\tself.PrecacheSoundScript("{snd}")' - for snd in sorted(sounds) + f'\tself.PrecacheSoundScript("{snd.lstrip(SND_CHARS)}")' + for snd in sorted(sounds.values()) ]) ctx.vmf.create_ent( 'info_target', targetname='@precache', origin='-15872 -15872 -15872', # Should be outside the map. - # We don't include scripts/vscripts in the filename. vscripts=ctx.pack.inject_vscript(lines), ) From 1949c02f9031071c406fa6f77e8ff9be7a0a79d8 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 10 Feb 2023 19:38:07 -0800 Subject: [PATCH 008/243] Add helper to info_player_deathmatch for blocked radius --- fgd/point/info/info_player_deathmatch.fgd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fgd/point/info/info_player_deathmatch.fgd b/fgd/point/info/info_player_deathmatch.fgd index 4d89881f9..8fddf7ebf 100644 --- a/fgd/point/info/info_player_deathmatch.fgd +++ b/fgd/point/info/info_player_deathmatch.fgd @@ -1,8 +1,12 @@ @PointClass base(BaseEntityPoint, PlayerClass) autovis(Point Entities, Globals, Player Spawn, Deathmatch Spawn) appliesto(HL2DM, Mesa, HL2, EP1, EP2, P1) - studio("models/editor/playerstart.mdl") = info_player_deathmatch: "Generic multiplayer spawn point, used in games which don't have their own." + studio("models/editor/playerstart.mdl") + sphere(_blocked_dist) = info_player_deathmatch: "Generic multiplayer spawn point, used in games which don't have their own." [ + _blocked_dist[!engine](integer) readonly : "Blocked Radius" : 128 : "If another player is within this radius, the spawn point is condsidered to be blocked"+ + " and players won't spawn here. This is a hardcoded value, listed here for visualization." + itemstogive[Mesa](string) : "List of items to spawn the player with." input Enable[Mesa](void) : "Enable this spawnpoint." From e52fba41b3ca047e55a5b83df6d121920c326498 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 11 Feb 2023 14:14:57 +1000 Subject: [PATCH 009/243] Fix missing KZ tag --- fgd/brush/trigger/trigger_teleport.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/brush/trigger/trigger_teleport.fgd b/fgd/brush/trigger/trigger_teleport.fgd index 85e74bb60..71ebab52a 100644 --- a/fgd/brush/trigger/trigger_teleport.fgd +++ b/fgd/brush/trigger/trigger_teleport.fgd @@ -58,8 +58,8 @@ input RemoveAllowForRunners[KZ](void) : "Disallow runners from using this teleporter if they were allowed through AllowForRunners (Tag Arena)." // Outputs - output OnTeleport(void) : "Outputted when a player teleports." - output OnTeleportNotAllowed(void) : "Outputted when a player tries to teleport, but can't because they are not allowed to use this teleporter." + output OnTeleport[KZ](void) : "Outputted when a player teleports." + output OnTeleportNotAllowed[KZ](void) : "Outputted when a player tries to teleport, but can't because they are not allowed to use this teleporter." @resources [] ] From aa6540afe64cf666f47a7a158d17664f5e426c64 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 11 Feb 2023 19:12:49 +1000 Subject: [PATCH 010/243] If required, add $mostlyopaque to propcombined props. --- CHANGELOG.md | 4 ++++ src/hammeraddons/propcombine.py | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b166e6ee2..dbffb3529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Version (dev) +* Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. +* Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. +* Allow `comp_vactube_start` to be set to have a timer which starts disabled. +* If required, add `$mostlyopaque` to propcombined props. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 676277bac..da827c4a3 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -25,7 +25,7 @@ from srctools.game import Game from srctools.logger import get_logger from srctools.math import Angle, Matrix, Vec, quickhull -from srctools.mdl import MDL_EXTS, Model +from srctools.mdl import MDL_EXTS, Model, Flags as ModelFlags from srctools.packlist import PackList from srctools.smd import Bone, Mesh, Triangle, Vertex from srctools.tokenizer import Token, Tokenizer @@ -281,7 +281,10 @@ async def compile_func( surfprops: Set[str] = set() cdmats: Set[str] = set() contents: Set[int] = set() + mostly_opaque = False + qc: QC + mdl: Model for prop in prop_pos: qc, mdl = lookup_model(prop.model) assert qc is not None, prop.model @@ -289,6 +292,8 @@ async def compile_func( surfprops.add(mdl.surfaceprop.casefold()) cdmats.update(mdl.cdmaterials) contents.add(mdl.contents) + if ModelFlags.translucent_twopass in mdl.flags: + mostly_opaque = True if len(surfprops) > 1: raise ValueError('Multiple surfaceprops? Should be filtered out.') @@ -373,6 +378,8 @@ async def compile_func( # 0 needs to produce this value. ]) or '"notsolid"', )) + if mostly_opaque: + f.write('$mostlyopaque\n') for mat in sorted(cdmats): f.write('$cdmaterials "{}"\n'.format(mat)) @@ -1034,7 +1041,6 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: for tex in model.iter_textures([prop.skin]) }), - model.flags.value, (prop.flags & relevant_flags).value, model.contents, model.surfaceprop, @@ -1114,7 +1120,7 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: map_name, folder_name='propcombine', version={ - 'ver': 1, + 'ver': 2, 'vol_tolerance': volume_tolerance, }, pack_models=pack_models, From b5df2870b3df845ff656d6d6aa7422dfa03e28d5 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 12 Feb 2023 17:32:22 +1000 Subject: [PATCH 011/243] Add a bunch of the other simple flags --- CHANGELOG.md | 2 +- src/hammeraddons/propcombine.py | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbffb3529..b535c3431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. * Allow `comp_vactube_start` to be set to have a timer which starts disabled. -* If required, add `$mostlyopaque` to propcombined props. +* If required, add various QC flags like `$mostlyopaque` to propcombined props. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index da827c4a3..3fe193359 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -281,7 +281,7 @@ async def compile_func( surfprops: Set[str] = set() cdmats: Set[str] = set() contents: Set[int] = set() - mostly_opaque = False + combined_flags = ModelFlags(0) qc: QC mdl: Model @@ -292,8 +292,7 @@ async def compile_func( surfprops.add(mdl.surfaceprop.casefold()) cdmats.update(mdl.cdmaterials) contents.add(mdl.contents) - if ModelFlags.translucent_twopass in mdl.flags: - mostly_opaque = True + combined_flags |= mdl.flags if len(surfprops) > 1: raise ValueError('Multiple surfaceprops? Should be filtered out.') @@ -378,9 +377,23 @@ async def compile_func( # 0 needs to produce this value. ]) or '"notsolid"', )) - if mostly_opaque: + # According to studiomdl, $opaque overrides $mostlyopaque. + if ModelFlags.force_opaque in combined_flags: + f.write('$opaque\n') + if ModelFlags.translucent_twopass in combined_flags: + LOGGER.warning( + 'Both $mostlyopaque and $opaque set with models: {}', + {prop.model for prop in prop_pos} + ) + elif ModelFlags.translucent_twopass in combined_flags: f.write('$mostlyopaque\n') + if ModelFlags.no_forced_fade in combined_flags: + f.write('$noforcedfade\n') + + if ModelFlags.ambient_boost in combined_flags: + f.write('$ambientboost\n') + for mat in sorted(cdmats): f.write('$cdmaterials "{}"\n'.format(mat)) From fa2bce332c483b59f2ded5961a0bcd547f5c461b Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Feb 2023 00:48:10 -0800 Subject: [PATCH 012/243] Default touch opens on doors to disabled --- fgd/bases/Door.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/bases/Door.fgd b/fgd/bases/Door.fgd index 9e5e62f48..d87f78948 100644 --- a/fgd/bases/Door.fgd +++ b/fgd/bases/Door.fgd @@ -42,7 +42,7 @@ 32: "Toggle" : 0 256: "Use Opens" : 0 512: "NPCs Can't" : 0 - 1024: "Touch Opens" : 1 + 1024: "Touch Opens" : 0 2048: "Starts locked" : 0 4096: "Door Silent" : 0 131072: "Block Infected nav when closed" : 0 [L4D, L4D2] From c6a27cf0539621472d6780bca2c53817d772ea2f Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 18 Feb 2023 10:49:10 -0800 Subject: [PATCH 013/243] Fix env_sprite defaulting to .spr extension No idea why this was never done earlier --- fgd/point/env/env_sprite.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/env/env_sprite.fgd b/fgd/point/env/env_sprite.fgd index 60c791366..ca9f6a20f 100644 --- a/fgd/point/env/env_sprite.fgd +++ b/fgd/point/env/env_sprite.fgd @@ -6,7 +6,7 @@ = env_sprite: "An entity that controls the drawing of a sprite in the world." [ framerate(float) : "Framerate" : "10.0" : "Rate at which the sprite should animate, if at all." - model(sprite) : "Sprite Name" : "sprites/glow01.spr" : "Material of the sprite to be drawn." + model(sprite) : "Sprite Name" : "sprites/glow01.vmt" : "Material of the sprite to be drawn." scale(float) : "Scale" : 0.25 : "Scale multiplier of the sprite." spawnflags(flags) = [ From 602aaa3b8b5a784ad5f9fdae37263d413364dfb7 Mon Sep 17 00:00:00 2001 From: ozxybox Date: Thu, 23 Feb 2023 23:27:59 -0600 Subject: [PATCH 014/243] Add support for non-uniform static prop scaling --- src/hammeraddons/propcombine.py | 42 ++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 3fe193359..e11605ff4 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -170,7 +170,11 @@ class PropPos: model: str checksum: bytes skin: int - scale: float + + scale_x: float + scale_y: float + scale_z: float + solidity: CollType @@ -240,7 +244,9 @@ async def combine_group( prop.model, mdl.checksum, prop.skin, - prop.scaling, + prop.scaling.x, + prop.scaling.y, + prop.scaling.z, coll, )) # We don't want to build collisions if it's not used. @@ -317,22 +323,42 @@ async def compile_func( child_ref = await _mesh_cache.fetch((qc, prop.skin), build_reference, prop, qc, mdl) child_coll = await _coll_cache.fetch(qc.phy_smd, build_collision, qc, prop, child_ref, volume_tolerance > 0) + scale = Vec(prop.scale_x, prop.scale_y, prop.scale_z) offset = Vec(prop.x, prop.y, prop.z) - matrix = Matrix.from_angle(prop.pit, prop.yaw, prop.rol) + rot_matrix = Matrix.from_angle(prop.pit, prop.yaw, prop.rol) + - ref_mesh.append_model(child_ref, matrix, offset, prop.scale * qc.ref_scale) + ref_mesh.append_model(child_ref, rot_matrix, offset, scale * qc.ref_scale) if has_coll and child_coll is not None: - scale = prop.scale * qc.phy_scale + phy_scale = scale * qc.phy_scale + + matrix = Matrix() + + # Set the scale + matrix[0,0] = phy_scale.x + matrix[1,1] = phy_scale.y + matrix[2,2] = phy_scale.z + + # Rotate the matrix + matrix @= rot_matrix + + # Secondary matrix for the normals + itm = matrix.inverse().transpose() + group = Mesh(coll_mesh.bones, coll_mesh.animation, []) for part in child_coll: for orig_tri in part.triangles: new_tri = orig_tri.copy() for vert in new_tri: vert.links[:] = bone_link - vert.norm @= matrix - vert.pos *= scale - vert.pos.localise(offset, matrix) + + # Transform the vertex + vert.norm @= itm + vert.norm = vert.norm.norm() + vert.pos @= matrix + vert.pos += offset + group.triangles.append(new_tri) if group.triangles: coll_groups[group] = group.compute_volume() From 631bf58b4079769f6e3e721e7d6e780f6eb63cb3 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 26 Feb 2023 09:56:47 +1000 Subject: [PATCH 015/243] Restrict CI to only have read access to the repo --- .github/workflows/build-fgds.yml | 5 +++++ .github/workflows/build-postcompiler.yml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/build-fgds.yml b/.github/workflows/build-fgds.yml index 8abc7b765..85126e99e 100644 --- a/.github/workflows/build-fgds.yml +++ b/.github/workflows/build-fgds.yml @@ -10,6 +10,11 @@ on: - 'fgd/**' workflow_dispatch: # Allow triggering manually whenever it's useful. + +permissions: + contents: read + pull-requests: read + jobs: build-fgds: runs-on: ubuntu-latest diff --git a/.github/workflows/build-postcompiler.yml b/.github/workflows/build-postcompiler.yml index 4eadb53b1..51488a215 100644 --- a/.github/workflows/build-postcompiler.yml +++ b/.github/workflows/build-postcompiler.yml @@ -7,6 +7,10 @@ on: workflow_dispatch: # Allow triggering manually whenever it's useful. +permissions: + contents: read + pull-requests: read + jobs: freeze: strategy: From faed24073f8f6bfd924a2c1659ebd6291dddd80e Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 28 Feb 2023 17:48:33 +1000 Subject: [PATCH 016/243] Add an entclass function to weapon entities, for finding the weapon script. --- fgd/bases/BaseMesaWeaponPickup.fgd | 8 ++++++++ fgd/bases/Weapon.fgd | 8 +++++++- fgd/point/item/item_mesa_weapons.fgd | 28 ++++++++++++++-------------- 3 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 fgd/bases/BaseMesaWeaponPickup.fgd diff --git a/fgd/bases/BaseMesaWeaponPickup.fgd b/fgd/bases/BaseMesaWeaponPickup.fgd new file mode 100644 index 000000000..f4fadc528 --- /dev/null +++ b/fgd/bases/BaseMesaWeaponPickup.fgd @@ -0,0 +1,8 @@ +@BaseClass base(BaseMesaPickup) appliesto(Mesa) = BaseMesaWeaponPickup + [ + @resources + [ + // These item_weapon_XXX ents should include the relevant weapon script. + func weapon_script + ] + ] diff --git a/fgd/bases/Weapon.fgd b/fgd/bases/Weapon.fgd index 6e2759e63..2c7d47aab 100644 --- a/fgd/bases/Weapon.fgd +++ b/fgd/bases/Weapon.fgd @@ -37,5 +37,11 @@ output OnPlayerPickup(void) : "Fires when the player picks up this weapon." output OnNPCPickup(void) : "Fires when an NPC picks up this weapon." output OnCacheInteraction(void) : "Fires when the player 'proves' they've found this weapon. " + - "Fires on: Player Touch, +USE pickup, Physcannon pickup, Physcannon pun" + "t." + "Fires on: Player Touch, +USE pickup, Physcannon pickup, Physcannon punt." + + @resources + [ + // Weapons have a script file defining sounds and models used. + func weapon_script + ] ] diff --git a/fgd/point/item/item_mesa_weapons.fgd b/fgd/point/item/item_mesa_weapons.fgd index bb597d39d..f2f37c7ed 100644 --- a/fgd/point/item/item_mesa_weapons.fgd +++ b/fgd/point/item/item_mesa_weapons.fgd @@ -1,14 +1,14 @@ -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_357.mdl") = item_weapon_357 : "357 Revolver" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_crowbar.mdl") = item_weapon_crowbar : "Crowbar" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_crossbow.mdl") = item_weapon_crossbow : "Crossbow" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_grenade.mdl") = item_weapon_frag : "Frag Grenade" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_glock.mdl") = item_weapon_glock : "Glock-18 Handgun" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_egon_pickup.mdl") = item_weapon_gluon : "Gluon Gun" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_hgun.mdl") = item_weapon_hivehand : "Hivearm" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_mp5.mdl") = item_weapon_mp5 : "MP5 Submachinegun" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_shotgun.mdl") = item_weapon_shotgun : "SPAS-12 Shotgun" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_rpg.mdl") = item_weapon_rpg : "RPG" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_satchel.mdl") = item_weapon_satchel : "Satchel Charge" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/xenians/snarknest.mdl") = item_weapon_snark : "Snarks" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_gauss.mdl") = item_weapon_tau : "Tau Cannon" [] -@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaPickup) studio("models/weapons/w_tripmine.mdl") = item_weapon_tripmine : "Tripmine" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_357.mdl") = item_weapon_357 : "357 Revolver" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_crowbar.mdl") = item_weapon_crowbar : "Crowbar" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_crossbow.mdl") = item_weapon_crossbow : "Crossbow" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_grenade.mdl") = item_weapon_frag : "Frag Grenade" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_glock.mdl") = item_weapon_glock : "Glock-18 Handgun" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_egon_pickup.mdl") = item_weapon_gluon : "Gluon Gun" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_hgun.mdl") = item_weapon_hivehand : "Hivearm" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_mp5.mdl") = item_weapon_mp5 : "MP5 Submachinegun" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_shotgun.mdl") = item_weapon_shotgun : "SPAS-12 Shotgun" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_rpg.mdl") = item_weapon_rpg : "RPG" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_satchel.mdl") = item_weapon_satchel : "Satchel Charge" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/xenians/snarknest.mdl") = item_weapon_snark : "Snarks" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_gauss.mdl") = item_weapon_tau : "Tau Cannon" [] +@PointClass appliesto(Mesa) autovis(Weapons, Mesa Weapons) base(BaseMesaWeaponPickup) studio("models/weapons/w_tripmine.mdl") = item_weapon_tripmine : "Tripmine" [] From 813ec17f2908b8334cb1414c90c7769971eebcf2 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 28 Feb 2023 19:40:01 -0800 Subject: [PATCH 017/243] Disable "vactube ent count" debug messages --- hammer/scripts/vscripts/srctools/vac_anim.nut | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/scripts/vscripts/srctools/vac_anim.nut b/hammer/scripts/vscripts/srctools/vac_anim.nut index 1be02c766..8926d2e97 100644 --- a/hammer/scripts/vscripts/srctools/vac_anim.nut +++ b/hammer/scripts/vscripts/srctools/vac_anim.nut @@ -168,7 +168,7 @@ function make_cube() { cargo = EntSet(cur_time + 3.0, mover, visual); ::vactube_objs.append(cargo); // For tracking spawning, set this. - printl("Vactube ent count: " + (::vactube_objs.len() * 2).tostring()); + // printl("Vactube ent count: " + (::vactube_objs.len() * 2).tostring()); } else { // Teleport to the right position. cargo.mover.SetAbsOrigin(self.GetOrigin()); From e10b0f8282d3a06306c5ae9fa4ad1d2eb5e8d025 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 6 Mar 2023 09:56:44 +1000 Subject: [PATCH 018/243] Set proper visgroups for contraption cubes and buttons --- fgd/point/prop/prop_contraption_cube.fgd | 1 - fgd/point/prop/prop_contraption_cube_button.fgd | 1 - fgd/point/prop/prop_monster_box.fgd | 1 - fgd/point/prop/prop_scaled_cube.fgd | 3 +-- fgd/point/prop/prop_weighted_cube.fgd | 1 - fgd/visgroups.cfg | 12 +++++++----- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/fgd/point/prop/prop_contraption_cube.fgd b/fgd/point/prop/prop_contraption_cube.fgd index 71d79ca23..81bd070cd 100644 --- a/fgd/point/prop/prop_contraption_cube.fgd +++ b/fgd/point/prop/prop_contraption_cube.fgd @@ -1,7 +1,6 @@ @PointClass base(BasePortalCube) studioprop("models/props/reflection_cube.mdl") appliesto(P2EDU) - autovis(Test Elements, Cubes, Normal) = prop_contraption_cube : "Contraption Cube" [ cube_mass(float) : "Mass" : 40 : "Mass of the cube (kg)" diff --git a/fgd/point/prop/prop_contraption_cube_button.fgd b/fgd/point/prop/prop_contraption_cube_button.fgd index 160309c49..82aff3f9c 100644 --- a/fgd/point/prop/prop_contraption_cube_button.fgd +++ b/fgd/point/prop/prop_contraption_cube_button.fgd @@ -1,6 +1,5 @@ @PointClass base(BasePortButton) appliesto(P2EDU) - autovis(Test Elements, P2 Buttons, Floor) studioprop("models/props_ingame/button_socket_contraption..mdl") = prop_contraption_cube_button: "A floor button which is activated by a prop_contraption_cube." [ diff --git a/fgd/point/prop/prop_monster_box.fgd b/fgd/point/prop/prop_monster_box.fgd index 5163b9738..a0e178045 100644 --- a/fgd/point/prop/prop_monster_box.fgd +++ b/fgd/point/prop/prop_monster_box.fgd @@ -1,6 +1,5 @@ @PointClass base(BaseEntityPhysics) appliesto(P2) - autovis(Test Elements, Cubes, Franken) studioprop() = prop_monster_box: "FrankenCubes, they walk about when oriented correctly but otherwise behave like normal Storage Cubes." [ startasbox(boolean) : "Start As Box" : 0 : "Start the FrankenTurret in cube form." diff --git a/fgd/point/prop/prop_scaled_cube.fgd b/fgd/point/prop/prop_scaled_cube.fgd index 176687ba9..9b222b3da 100644 --- a/fgd/point/prop/prop_scaled_cube.fgd +++ b/fgd/point/prop/prop_scaled_cube.fgd @@ -1,7 +1,6 @@ @PointClass base(BasePortalCube) studioprop("models/props/sixense/box/sixensebox.mdl") appliesto(P2SIXENSE) - autovis(Test Elements, Cubes, Scalable) = prop_scaled_cube : "Scalable Cube" [ -] \ No newline at end of file +] diff --git a/fgd/point/prop/prop_weighted_cube.fgd b/fgd/point/prop/prop_weighted_cube.fgd index 09327c2d8..7de432b44 100644 --- a/fgd/point/prop/prop_weighted_cube.fgd +++ b/fgd/point/prop/prop_weighted_cube.fgd @@ -1,6 +1,5 @@ @PointClass base(BasePortalCube) appliesto(P2) - autovis(Test Elements, Cubes, Normal) studioprop() = prop_weighted_cube: "Aperture Science Weighted Cube. Presses floor buttons, and can be moved around." [ cubetype[engine](integer) : "Cube Type" : 0 diff --git a/fgd/visgroups.cfg b/fgd/visgroups.cfg index 82030a0ce..43748d0bc 100644 --- a/fgd/visgroups.cfg +++ b/fgd/visgroups.cfg @@ -801,9 +801,10 @@ - Radiation Ammo Charger (`prop_radiation_charger`) - Headcrab Canister (`env_headcrabcanister`) - Cubes - - FrankenTurrets (`prop_monster_box`) - - Normal (`prop_weighted_cube`) - - Scaled Cube (`prop_scaled_cube`) + * `prop_contraption_cube` + * `prop_monster_box` + * `prop_scaled_cube` + * `prop_weighted_cube` - Excursion Funnels (`prop_tractor_beam`) - Fizzlers * `trigger_paint_cleanser` @@ -826,12 +827,13 @@ * `projected_wall_entity` - Excursion Funnels (`prop_tractor_beam`) - Buttons + * `prop_button` + * `prop_contraption_cube_button` * `prop_floor_ball_button` * `prop_floor_button` * `prop_floor_cube_button` - * `prop_under_floor_button` - * `prop_button` * `prop_under_button` + * `prop_under_floor_button` - Paint * `info_paint_sprayer` * `paint_sphere` From 48c499461db42b19256307b7b1c06c1e25e15ccb Mon Sep 17 00:00:00 2001 From: Adr Date: Mon, 6 Mar 2023 20:07:10 +0100 Subject: [PATCH 019/243] added extra globalstates to env_global, fixed missing kz tag on prop_static --- fgd/point/env/env_global.fgd | 2 ++ fgd/point/prop/prop_static.fgd | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fgd/point/env/env_global.fgd b/fgd/point/env/env_global.fgd index 160755c2b..ef649803c 100644 --- a/fgd/point/env/env_global.fgd +++ b/fgd/point/env/env_global.fgd @@ -18,6 +18,8 @@ "friendly_encounter": "Friendly encounter sequence (lower weapons, etc.)" "gordon_invulnerable": "Player is invulnerable" "no_seagulls_on_jeep": "Don't spawn seagulls on the jeep" + "citizens_passive": "Non-rebel citizens, doesn't work exactly like 'Gordon pre-criminal'" + "gordon_protect_driver": "Protect driver. Changes damage when in a vehicle and being crushed." "ep2_alyx_injured": "Episode 2: Alyx injured" [ep2] "ep_alyx_darknessmode": "Episode 1: Alyx darkness mode" [ep1] "hunters_to_run_over": "Ep2 Counter: Hunters to run over before they dodge" [ep2] diff --git a/fgd/point/prop/prop_static.fgd b/fgd/point/prop/prop_static.fgd index e31866ae5..b842a02b7 100644 --- a/fgd/point/prop/prop_static.fgd +++ b/fgd/point/prop/prop_static.fgd @@ -129,7 +129,7 @@ lightingorigin(target_destination) : "Lighting Origin" : : "Select an info_lighting to specify a location to sample lighting from, instead of using this entity's origin." - linedivider_lmap[TF2, MESA, !engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" + linedivider_lmap[TF2, MESA, KZ, !engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" generatelightmaps[TF2, MESA, KZ](boolean) : "Generate (and use) lightmaps for this static prop" : 0 lightmapresolutionx[TF2, MESA, KZ](integer) : "Lightmap Resolution X" : 32 : "The resolution of the generated lightmap in the X (or U) direction (only used if Generate Lightmaps is Yes) " From fa434d11be1cd6739d946080061b11ca8af0f484 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 9 Mar 2023 11:56:29 +1000 Subject: [PATCH 020/243] Actually update bbox for propcombine sets, log these when sets are unused --- src/hammeraddons/propcombine.py | 35 +++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index e11605ff4..1dd107fd7 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -102,20 +102,26 @@ def __init__(self, group_name: str, skinset: FrozenSet, origin: Vec) -> None: # For sorting. self.volume = 0.0 self.used = False - self.mins = Vec() - self.maxes = Vec() + self.mins: Optional[Vec] = None + self.maxes: Optional[Vec] = None # Each volume in the group, specifying its collision behaviour. self.collision: List[Callable[[Vec], bool]] = [] if group_name: - self.desc = f'group "{group_name}"' + self._desc_start = f'group "{group_name}"' else: - self.desc = f'at {origin}' + self._desc_start = f'at {origin}' def contains(self, point: Vec) -> bool: """Check if the volume contains this point.""" return any(coll(point) for coll in self.collision) + def __str__(self) -> str: + if self.mins is None or self.maxes is None: + return self._desc_start + else: + return f'{self._desc_start}, ({self.mins} : {self.maxes})' + def make_collision_bbox(origin: Vec, angles: Angle, mins: Vec, maxes: Vec) -> Callable[[Vec], bool]: """Produce a bounding box collision checker.""" @@ -839,6 +845,15 @@ def group_props_ent( Vec.from_str(ent['mins']), Vec.from_str(ent['maxs']), ) + if combine_set.mins is None: + combine_set.mins = origin + mins + else: + combine_set.mins.min(origin + mins) + if combine_set.maxes is None: + combine_set.maxes = origin + maxes + else: + combine_set.maxes.max(origin + maxes) + size = maxes - mins # Enlarge slightly to ensure it never has a zero area. # This ensures items on the edge are included. @@ -859,6 +874,14 @@ def group_props_ent( size = brush.maxes - brush.mins combine_set.volume += size.x * size.y * size.z combine_set.collision.append(make_collision_brush(origin, angles, brush)) + if combine_set.mins is None: + combine_set.mins = brush.origin + brush.mins + else: + combine_set.mins.min(brush.origin + brush.mins) + if combine_set.maxes is None: + combine_set.maxes = brush.origin + brush.maxes + else: + combine_set.maxes.max(brush.origin + brush.maxes) else: raise AssertionError(ent['classname']) @@ -897,7 +920,7 @@ def group_props_ent( for combine_set_list in sets_by_skin.values(): for combine_set in combine_set_list: if not combine_set.used: - LOGGER.warning('Unused comp_propcombine_set {}', combine_set.desc) + LOGGER.warning('Unused comp_propcombine_volume/_set {}', combine_set) def group_props_auto( @@ -1133,7 +1156,7 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: ) else: # No way provided to choose props. - LOGGER.info('No propcombine groups provided.') + LOGGER.info('No propcombine groups or range provided.') return # These are models we cannot merge no matter what - From 0d562bd88ce0aa1fd287e9121d315f08cae36778 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 9 Mar 2023 12:46:05 +1000 Subject: [PATCH 021/243] Limit propcombine groups to avoid hitting vertex limits --- CHANGELOG.md | 1 + src/hammeraddons/propcombine.py | 66 +++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b535c3431..525baa574 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. +* Limit the size of propcombined groups to avoid hitting vertex limits. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 1dd107fd7..fd7a9048e 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -69,9 +69,9 @@ class QC: ''' MAX_GROUP = 24 # StudioMDL doesn't allow more than this... -# Exceed this and StudioMDL cuts into multiple bodygroups. -# So at that point we should produce multiple grouped props. -MAX_VERTS = 65536//3 +# Exceed 65k triangles and StudioMDL cuts into multiple bodygroups. So at that point we should +# produce multiple grouped props. Shrink a little just for breathing room. +MAX_VERTS = 65536//3 - 64 # Cache of the SMD models we have already parsed, so we don't need @@ -910,7 +910,29 @@ def group_props_ent( found.append(prop) combine_set.used = True - actual = set(found).intersection(group) + if not found: # No point checking an empty list. + continue + + actual = [] + total_verts = 0 + for prop in found: + qc, mdl = get_model(prop.model) + assert mdl is not None + total_verts += mdl.total_verts + if total_verts > MAX_VERTS: + # Warn for groups since these were intentionally built. + LOGGER.warning( + 'Hit vert limit in group {} with models {}', combine_set, + {prop.model for prop in found}, + ) + # Output this prop, then start a new group. + if len(actual) >= min_cluster: + yield list(actual) + for sub_prop in actual: + group.remove(sub_prop) + actual.clear() + total_verts = mdl.total_verts + actual.append(prop) if len(actual) >= min_cluster: yield list(actual) for prop in actual: @@ -925,6 +947,7 @@ def group_props_ent( def group_props_auto( prop_groups: Dict[Optional[tuple], List[StaticProp]], + get_model: Callable[[str], Tuple[Optional[QC], Optional[Model]]], dist: float, min_cluster: int, ) -> Iterator[List[StaticProp]]: @@ -957,22 +980,39 @@ def group_props_auto( bbox_min, bbox_max = Vec.bbox(prop.origin for prop in cluster) center_pos = (bbox_min + bbox_max) / 2 - cluster_list: List[Tuple[StaticProp, float]] = [] + cluster_list: List[Tuple[StaticProp, int, float]] = [] for prop in cluster: prop_off = (center_pos - prop.origin).mag_sq() + qc, mdl = get_model(prop.model) + assert mdl is not None if prop_off <= dist_sq: - cluster_list.append((prop, prop_off)) - - cluster_list.sort(key=operator.itemgetter(1)) - selected_props = [ - prop for prop, off in - cluster_list[:MAX_GROUP] - ] + cluster_list.append((prop, mdl.total_verts, prop_off)) + + # Sort by the distance to the original, so we prefer closer models with more verts. + cluster_list.sort(key=lambda tup: tup[2] - 8.0 * tup[1]) + total_verts = 0 + selected_props: List[StaticProp] = [] + for prop, vert_count, off in cluster_list: + total_verts += vert_count + if total_verts > MAX_VERTS: + # Make this just info level, just might be props nearby. + LOGGER.info( + 'Hit vert limit for auto group @ {} with models {}', center_pos, + {prop.model for prop, vert, off in cluster_list}, + ) + break + elif len(selected_props) > MAX_GROUP: + break + else: + selected_props.append(prop) todo.difference_update(selected_props) if len(selected_props) >= min_cluster: yield selected_props + # Else, we grouped a too-small number of props - deliberately forget about them, they're + # likely isolated, meaning checking them in future is pointless. The main combine() + # function will re-add them to the map. async def combine( @@ -1135,6 +1175,7 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: ), group_props_auto( prop_groups, + get_model, auto_range, min_cluster, ) @@ -1151,6 +1192,7 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: LOGGER.info('Automatically finding propcombine sets...') grouper = group_props_auto( prop_groups, + get_model, auto_range, min_cluster, ) From 554a734c0f595ecce8252db5d7dddc60d9ea8368 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 9 Mar 2023 13:11:33 +1000 Subject: [PATCH 022/243] Bump srctools version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index df31fa5b1..47f10d60e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs >= 21.4.0 typing_extensions >= 4.2.0 -srctools >= 2.3.9 +srctools >= 2.3.10 trio >= 0.20.0 trio-typing >= 0.7.0 pyinstaller >= 5.7.0 From 1b264de067b7c3190f11710fd647e1245e87bccf Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 10 Mar 2023 08:07:41 +1000 Subject: [PATCH 023/243] solidBSP in momentary_rot_button is actually available in all games --- fgd/brush/momentary_rot_button.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/brush/momentary_rot_button.fgd b/fgd/brush/momentary_rot_button.fgd index bba75aa6a..6c70d2859 100644 --- a/fgd/brush/momentary_rot_button.fgd +++ b/fgd/brush/momentary_rot_button.fgd @@ -98,7 +98,7 @@ 1: "Backward" ] - solidbsp[ASW, P2](boolean) : "Solid BSP" : 0 : "If set, use the SOLID_BSP collision type." + solidbsp(boolean) : "Solid BSP" : 0 : "If set, use the SOLID_BSP collision type." glow[L4D](target_destination) : "Glow Entity" : : "The name of an entity that will get the +use glow for this button." From 609b9f7252c3683f4f8d6d241d13276209f2e843 Mon Sep 17 00:00:00 2001 From: TeamSpen210 Date: Fri, 10 Mar 2023 08:09:52 +1000 Subject: [PATCH 024/243] Hide levels divider on prop_static if the keys are hidden. --- fgd/point/prop/prop_static.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/prop/prop_static.fgd b/fgd/point/prop/prop_static.fgd index b842a02b7..d1f7f5e52 100644 --- a/fgd/point/prop/prop_static.fgd +++ b/fgd/point/prop/prop_static.fgd @@ -50,7 +50,7 @@ preventpropcombine[PROPCOMBINE](boolean) : "Disable Prop Combine" : 0 : "Prevent this static prop from combining with any other static props in vbsp." - linedivider_levels[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" + linedivider_levels[!engine, +complete](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" mincpulevel[engine](integer): "Minimum CPU Level": 0 maxcpulevel[engine](integer): "Maximum CPU Level": 0 From d73dda8d3306344b55da32840fa1908a2f4666f9 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 20:59:29 -0800 Subject: [PATCH 025/243] Improve info_player_teamspawn hammer display I still keep mistyping the name as teamspen --- fgd/point/info/info_player_teamspawn.fgd | 31 +++++++++++++++++- .../models/editor/playerstart_tf_blu.vmt | 4 +++ .../models/editor/playerstart_tf_blu.vtf | Bin 0 -> 22024 bytes .../models/editor/playerstart_tf_red.vmt | 4 +++ .../models/editor/playerstart_tf_red.vtf | Bin 0 -> 22024 bytes hammer/models/editor/playerstart_tf.dx80.vtx | Bin 0 -> 23430 bytes hammer/models/editor/playerstart_tf.dx90.vtx | Bin 0 -> 23430 bytes hammer/models/editor/playerstart_tf.mdl | Bin 0 -> 1952 bytes hammer/models/editor/playerstart_tf.sw.vtx | Bin 0 -> 23414 bytes hammer/models/editor/playerstart_tf.vvd | Bin 0 -> 129216 bytes 10 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 hammer/materials/models/editor/playerstart_tf_blu.vmt create mode 100644 hammer/materials/models/editor/playerstart_tf_blu.vtf create mode 100644 hammer/materials/models/editor/playerstart_tf_red.vmt create mode 100644 hammer/materials/models/editor/playerstart_tf_red.vtf create mode 100644 hammer/models/editor/playerstart_tf.dx80.vtx create mode 100644 hammer/models/editor/playerstart_tf.dx90.vtx create mode 100644 hammer/models/editor/playerstart_tf.mdl create mode 100644 hammer/models/editor/playerstart_tf.sw.vtx create mode 100644 hammer/models/editor/playerstart_tf.vvd diff --git a/fgd/point/info/info_player_teamspawn.fgd b/fgd/point/info/info_player_teamspawn.fgd index 32e03c67d..135301667 100644 --- a/fgd/point/info/info_player_teamspawn.fgd +++ b/fgd/point/info/info_player_teamspawn.fgd @@ -1,7 +1,7 @@ @PointClass base(BaseEntityPoint, PlayerClass, TeamNum, EnableDisable) autovis(Point Entities, Globals, Player Spawn) appliesto(TF2) - studio("models/editor/playerstart.mdl") + studio() line(255 0 0, targetname, round_redspawn) line(0 255 0, targetname, round_bluespawn) = info_player_teamspawn: "This entity marks the spawn point for Team Fortress players." @@ -41,6 +41,35 @@ round_bluespawn(target_destination) : "Blue spawn for round" : : "Blue spawn point when the associated round is being played." round_redspawn(target_destination) : "Red spawn for round" : : "Red spawn point when the associated round is being played." + + skin[!engine](choices) : "[H] Team" : 0 : "Team color to display in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + ] + + model[!engine](choices) : "[H] Model" : "models/editor/playerstart_tf.mdl" : "Model to display in Hammer" = + [ + "models/editor/playerstart_tf.mdl" : "Player Start" + "models/player/scout.mdl" : "Scout" + "models/player/soldier.mdl" : "Soldier" + "models/player/pyro.mdl" : "Pyro" + "models/player/demo.mdl" : "Demoman" + "models/player/heavy.mdl" : "Heavy" + "models/player/engineer.mdl" : "Engineer" + "models/player/medic.mdl" : "Medic" + "models/player/sniper.mdl" : "Sniper" + "models/player/spy.mdl" : "Spy" + "models/bots/scout/bot_scout.mdl" : "Robot Scout" + "models/bots/soldier/bot_soldier.mdl" : "Robot Soldier" + "models/bots/pyro/bot_pyro.mdl" : "Robot Pyro" + "models/bots/demo/bot_demo.mdl" : "Robot Demoman" + "models/bots/heavy/bot_heavy.mdl" : "Robot Heavy" + "models/bots/engineer/bot_engineer.mdl" : "Robot Engineer" + "models/bots/medic/bot_medic.mdl" : "Robot Medic" + "models/bots/sniper/bot_sniper.mdl" : "Robot Sniper" + "models/bots/spy/bot_spy.mdl" : "Robot Spy" + ] input RoundSpawn(void) : "Re-find control points." diff --git a/hammer/materials/models/editor/playerstart_tf_blu.vmt b/hammer/materials/models/editor/playerstart_tf_blu.vmt new file mode 100644 index 000000000..674a6772e --- /dev/null +++ b/hammer/materials/models/editor/playerstart_tf_blu.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/playerstart_tf_blu" +} \ No newline at end of file diff --git a/hammer/materials/models/editor/playerstart_tf_blu.vtf b/hammer/materials/models/editor/playerstart_tf_blu.vtf new file mode 100644 index 0000000000000000000000000000000000000000..bbd9dfbe9eda6935d45912563c20210ed23f5ec0 GIT binary patch literal 22024 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 literal 0 HcmV?d00001 diff --git a/hammer/materials/models/editor/playerstart_tf_red.vmt b/hammer/materials/models/editor/playerstart_tf_red.vmt new file mode 100644 index 000000000..4cd095c4a --- /dev/null +++ b/hammer/materials/models/editor/playerstart_tf_red.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/editor/playerstart_tf_red" +} \ No newline at end of file diff --git a/hammer/materials/models/editor/playerstart_tf_red.vtf b/hammer/materials/models/editor/playerstart_tf_red.vtf new file mode 100644 index 0000000000000000000000000000000000000000..dabe13e091bc00113fee38c2c5662ce6b6e75c72 GIT binary patch literal 22024 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 literal 0 HcmV?d00001 diff --git a/hammer/models/editor/playerstart_tf.dx90.vtx b/hammer/models/editor/playerstart_tf.dx90.vtx new file mode 100644 index 0000000000000000000000000000000000000000..72fff6abb699b2d7c3e4a6f5070a5cd5a88fecc0 GIT binary patch literal 23430 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 E1@CGB`2YX_ literal 0 HcmV?d00001 diff --git a/hammer/models/editor/playerstart_tf.sw.vtx b/hammer/models/editor/playerstart_tf.sw.vtx new file mode 100644 index 0000000000000000000000000000000000000000..4fc004c2da00a8d00dd401674128164e285696c9 GIT binary patch literal 23414 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 literal 0 HcmV?d00001 diff --git a/hammer/models/editor/playerstart_tf.vvd b/hammer/models/editor/playerstart_tf.vvd new file mode 100644 index 0000000000000000000000000000000000000000..05fe2716c260aad8480217f2a85a32458ba1251a GIT binary patch literal 129216 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 z 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 OKJ7UE3?)uG|Nj8$Opjv# literal 0 HcmV?d00001 From 79b7f3d80b9e3c2b93b5a271691caccfeb02990c Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 22:00:17 -0800 Subject: [PATCH 026/243] Prevent teamspawn model from being packed, in theory --- fgd/point/info/info_player_teamspawn.fgd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fgd/point/info/info_player_teamspawn.fgd b/fgd/point/info/info_player_teamspawn.fgd index 135301667..aff433f5a 100644 --- a/fgd/point/info/info_player_teamspawn.fgd +++ b/fgd/point/info/info_player_teamspawn.fgd @@ -48,7 +48,8 @@ 1 : "BLU" ] - model[!engine](choices) : "[H] Model" : "models/editor/playerstart_tf.mdl" : "Model to display in Hammer" = + model[engine](string) : "Model" : : "Doesn't exist in engine, this is a hack to prevent auto packing" + model(choices) : "[H] Model" : "models/editor/playerstart_tf.mdl" : "Model to display in Hammer" = [ "models/editor/playerstart_tf.mdl" : "Player Start" "models/player/scout.mdl" : "Scout" From 94a910cc3c3cea945db6a8760b61e77546b957d6 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 22:00:34 -0800 Subject: [PATCH 027/243] Use ficool2 icons for team_ entities --- fgd/point/team/team_control_point_master.fgd | 1 + fgd/point/team/team_control_point_round.fgd | 1 + fgd/point/team/team_round_timer.fgd | 1 + fgd/point/team/team_train_watcher.fgd | 1 + 4 files changed, 4 insertions(+) diff --git a/fgd/point/team/team_control_point_master.fgd b/fgd/point/team/team_control_point_master.fgd index 7f3bb18c6..a7b54fb37 100644 --- a/fgd/point/team/team_control_point_master.fgd +++ b/fgd/point/team/team_control_point_master.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/team_control_point_master") appliesto(TF2) = team_control_point_master: "Control Point Master" [ team_base_icon_2(material) : "Material for the RED Base icon" : "sprites/obj_icons/icon_base_red" diff --git a/fgd/point/team/team_control_point_round.fgd b/fgd/point/team/team_control_point_round.fgd index 89f78ca5c..a44c3f721 100644 --- a/fgd/point/team/team_control_point_round.fgd +++ b/fgd/point/team/team_control_point_round.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/team_control_point_round") appliesto(TF2) = team_control_point_round: "Control Point Round (you may have as many of these in the map as you would like)." [ cpr_printname(string) : "Print Name" : : "LOCALIZED name to print on the RoundInfo panel" diff --git a/fgd/point/team/team_round_timer.fgd b/fgd/point/team/team_round_timer.fgd index e3793c8f6..4d37d2b65 100644 --- a/fgd/point/team/team_round_timer.fgd +++ b/fgd/point/team/team_round_timer.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/team_round_timer") appliesto(TF2) = team_round_timer: "Round Timer. Timer starts paused by default." [ diff --git a/fgd/point/team/team_train_watcher.fgd b/fgd/point/team/team_train_watcher.fgd index f75917356..3aa62a4c4 100644 --- a/fgd/point/team/team_train_watcher.fgd +++ b/fgd/point/team/team_train_watcher.fgd @@ -2,6 +2,7 @@ @PointClass base(BaseEntityPoint, TeamNum, EnableDisable) appliesto(TF2) line(128 128 128, targetname, train) + iconsprite("editor/ficool2/team_train_watcher") = team_train_watcher: "Entity that tracks the train progress through train escort maps" [ train_can_recede(boolean) : "Can the train recede?" : "1" : "Used to hide the HUD countdown." From 4d22ec2ab6e2f5a54b41d099f05404d574bec26a Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 22:03:41 -0800 Subject: [PATCH 028/243] Convert scriptvar setter icon to VTF 7.4 for TF2 --- .../editor/comp_scriptvar_setter.vtf | Bin 22076 -> 22068 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/hammer/materials/editor/comp_scriptvar_setter.vtf b/hammer/materials/editor/comp_scriptvar_setter.vtf index 3ef99b004a47795f368b95b6078b03793fcc9af2..5d649a9a9d33ee4e0deacdd96e5eefba1cd20740 100644 GIT binary patch literal 22068 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 From 63689329f2174ca0d5ddfdadfe0f3cad57836173 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 23:18:41 -0800 Subject: [PATCH 029/243] Use ficool2 icon sprites for the rest of the TF entities --- fgd/point/bot/bot_action_point.fgd | 1 + fgd/point/bot/bot_controller.fgd | 1 + fgd/point/bot/bot_generator.fgd | 1 + fgd/point/bot/bot_hint_sentrygun.fgd | 2 +- fgd/point/bot/bot_hint_sniper_spot.fgd | 2 +- fgd/point/bot/bot_proxy.fgd | 1 + fgd/point/bot/bot_roster.fgd | 1 + fgd/point/entity/entity_spawn_manager.fgd | 1 + fgd/point/entity/entity_spawn_point.fgd | 1 + fgd/point/env/env_movieexplosion.fgd | 1 + fgd/point/filter/filter_activator_tfteam.fgd | 2 +- fgd/point/game/game_forcerespawn.fgd | 1 + fgd/point/game/game_round_win.fgd | 1 + fgd/point/halloween/halloween_souls_pack.fgd | 1 + fgd/point/hightower_teleport_vortex.fgd | 1 + fgd/point/point/point_intermission.fgd | 1 + fgd/point/point/point_populator_interface.fgd | 3 +- fgd/point/tf/tf_base_minigame.fgd | 1 + fgd/point/tf/tf_gamerules.fgd | 1 + fgd/point/tf/tf_glow.fgd | 1 + fgd/point/tf/tf_logic_arena.fgd | 1 + fgd/point/tf/tf_logic_competitive.fgd | 1 + fgd/point/tf/tf_logic_cp_timer.fgd | 1 + fgd/point/tf/tf_logic_holiday.fgd | 1 + fgd/point/tf/tf_logic_hybrid_ctf_cp.fgd | 1 + fgd/point/tf/tf_logic_koth.fgd | 1 + fgd/point/tf/tf_logic_mann_vs_machine.fgd | 1 + fgd/point/tf/tf_logic_mannpower.fgd | 1 + fgd/point/tf/tf_logic_medieval.fgd | 1 + fgd/point/tf/tf_logic_minigames.fgd | 1 + fgd/point/tf/tf_logic_multiple_escort.fgd | 1 + fgd/point/tf/tf_logic_on_holiday.fgd | 2 +- fgd/point/tf/tf_logic_player_destruction.fgd | 1 + fgd/point/tf/tf_logic_robot_destruction.fgd | 1 + fgd/point/tf/tf_logic_training_mode.fgd | 1 + fgd/point/tf/tf_point_nav_interface.fgd | 1 + fgd/point/tf/tf_projectiles.fgd | 60 ++++++++++--------- .../tf/tf_robot_destruction_robot_spawn.fgd | 2 +- .../tf/tf_robot_destruction_spawn_group.fgd | 1 + fgd/point/tf/tf_spawner.fgd | 1 + fgd/point/training/training_annotation.fgd | 2 +- 41 files changed, 73 insertions(+), 35 deletions(-) diff --git a/fgd/point/bot/bot_action_point.fgd b/fgd/point/bot/bot_action_point.fgd index 67641991d..b63e86adc 100644 --- a/fgd/point/bot/bot_action_point.fgd +++ b/fgd/point/bot/bot_action_point.fgd @@ -2,6 +2,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Action Point) + iconsprite("editor/ficool2/bot_action_point") sphere(desired_distance) = bot_action_point: "A potential destination for a bot" [ next_action_point(target_destination) : "Next Action Point" : : "The next Action Point to approach after performing this one." diff --git a/fgd/point/bot/bot_controller.fgd b/fgd/point/bot/bot_controller.fgd index ec89167b6..d740c432a 100644 --- a/fgd/point/bot/bot_controller.fgd +++ b/fgd/point/bot/bot_controller.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Bot Controller) + iconsprite("editor/ficool2/bot_controller") = bot_controller: "An entity used to create a bot, and then issue commands to it." [ teamnum[engine](integer) : "Team" : 2 diff --git a/fgd/point/bot/bot_generator.fgd b/fgd/point/bot/bot_generator.fgd index 05374ed2a..08d59e9a9 100644 --- a/fgd/point/bot/bot_generator.fgd +++ b/fgd/point/bot/bot_generator.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Bot Generator) + iconsprite("editor/ficool2/bot_generator") = bot_generator: "Entity spawns TFBots every seconds, with at most active at once" [ team[engine](string) : "Team" : "auto" diff --git a/fgd/point/bot/bot_hint_sentrygun.fgd b/fgd/point/bot/bot_hint_sentrygun.fgd index 6873d2d48..ead842b99 100644 --- a/fgd/point/bot/bot_hint_sentrygun.fgd +++ b/fgd/point/bot/bot_hint_sentrygun.fgd @@ -2,7 +2,7 @@ @PointClass base(BaseEntityPoint, BaseObject, EnableDisable) appliesto(TF2) autovis(TF2, TFBots, Bot Hint) - studio("models/buildables/sentry3.mdl") = bot_hint_sentrygun: "TF2 Sentry Gun Placement Hint for Bots" + studio("models/buildables/sentry1_blueprint.mdl") = bot_hint_sentrygun: "TF2 Sentry Gun Placement Hint for Bots" [ sequence(integer) : "Sequence" : 5 : "Default animation sequence for the model to be playing after spawning." sticky(boolean) : "Sticky" : "0" : "If set, Engineer bots using this hint will stay here instead of destroying their equipment and moving up as the scenario changes." diff --git a/fgd/point/bot/bot_hint_sniper_spot.fgd b/fgd/point/bot/bot_hint_sniper_spot.fgd index 7b68d342a..514212ab9 100644 --- a/fgd/point/bot/bot_hint_sniper_spot.fgd +++ b/fgd/point/bot/bot_hint_sniper_spot.fgd @@ -2,7 +2,7 @@ @PointClass base(BaseEntityPoint, BaseObject) appliesto(TF2) autovis(TF2, TFBots, Bot Hint) - studio("models/player/sniper.mdl") + studio("models/bots/sniper/bot_sniper.mdl") sphere(radius) = bot_hint_sniper_spot: "TF2 Sniper Spot Hint for Bots" [ diff --git a/fgd/point/bot/bot_proxy.fgd b/fgd/point/bot/bot_proxy.fgd index 88f779b59..1eaccccb7 100644 --- a/fgd/point/bot/bot_proxy.fgd +++ b/fgd/point/bot/bot_proxy.fgd @@ -2,6 +2,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Bot Proxy) + iconsprite("editor/ficool2/bot_proxy") = bot_proxy: "An entity that spawns a TFBot and relays events to/from it" [ bot_name(string) : "Bot Name" : "TFBot" : "The bot's player name" diff --git a/fgd/point/bot/bot_roster.fgd b/fgd/point/bot/bot_roster.fgd index 191da6c69..260b60eea 100644 --- a/fgd/point/bot/bot_roster.fgd +++ b/fgd/point/bot/bot_roster.fgd @@ -2,6 +2,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Bot Roster) + iconsprite("editor/ficool2/bot_roster") = bot_roster: "Entity specifies what classes TFBots can choose" [ team[engine](string) : "Team" : "auto" diff --git a/fgd/point/entity/entity_spawn_manager.fgd b/fgd/point/entity/entity_spawn_manager.fgd index 954b88356..00b7b3781 100644 --- a/fgd/point/entity/entity_spawn_manager.fgd +++ b/fgd/point/entity/entity_spawn_manager.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) autovis(Point Entities, Globals,Ent Spawn Manager) + iconsprite("editor/ficool2/entity_spawn_manager") = entity_spawn_manager: "An entity that spawns stuff at spawn points." [ entity_name(string) : "Entity Name" : : "Name of the entity class we are supposed to spawn." diff --git a/fgd/point/entity/entity_spawn_point.fgd b/fgd/point/entity/entity_spawn_point.fgd index d3ef02a92..b1da50f72 100644 --- a/fgd/point/entity/entity_spawn_point.fgd +++ b/fgd/point/entity/entity_spawn_point.fgd @@ -2,6 +2,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) line(128 128 0, targetname, spawn_manager_name) + iconsprite("editor/ficool2/entity_spawn_point") = entity_spawn_point: "A spawn location associated with a spawn manager." [ spawn_manager_name(string) : "Spawn Manager Name" : : "Name of the spawn manager entity we are associated with." diff --git a/fgd/point/env/env_movieexplosion.fgd b/fgd/point/env/env_movieexplosion.fgd index 72dd4473c..27bd40ade 100644 --- a/fgd/point/env/env_movieexplosion.fgd +++ b/fgd/point/env/env_movieexplosion.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) + iconsprite("editor/ficool2/env_movieexplosion") = env_movieexplosion : "" [ @resources diff --git a/fgd/point/filter/filter_activator_tfteam.fgd b/fgd/point/filter/filter_activator_tfteam.fgd index 0ad5a964d..aeaaf5f4c 100644 --- a/fgd/point/filter/filter_activator_tfteam.fgd +++ b/fgd/point/filter/filter_activator_tfteam.fgd @@ -1,7 +1,7 @@ @FilterClass base(filter_base, TeamNum) appliesto(TF2) autovis(Logic, Filters) - iconsprite("editor/filter_team.vmt") + iconsprite("editor/ficool2/filter_activator_tfteam.vmt") line(0 255 0, targetname, controlpoint) = filter_activator_tfteam: "A filter that filters by the team of the activator. " [ diff --git a/fgd/point/game/game_forcerespawn.fgd b/fgd/point/game/game_forcerespawn.fgd index adb8606c8..0e6d56dbb 100644 --- a/fgd/point/game/game_forcerespawn.fgd +++ b/fgd/point/game/game_forcerespawn.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/game_forcerespawn") appliesto(TF2) = game_forcerespawn: "Removes buildings, grenades, etc., from the world and forces all players to respawn (without being killed)." [ diff --git a/fgd/point/game/game_round_win.fgd b/fgd/point/game/game_round_win.fgd index 631377624..a0874fce8 100644 --- a/fgd/point/game/game_round_win.fgd +++ b/fgd/point/game/game_round_win.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2, Mesa) autovis(Point Entities, Globals, Game Round End) + iconsprite("editor/ficool2/game_round_win") = game_round_win: "Tells the game rules that the round has either been won (if a team is assigned) or (for TF2) enter Sudden Death mode (if no team is assigned)." [ teamnum[engine](integer) : "Team" : 0 diff --git a/fgd/point/halloween/halloween_souls_pack.fgd b/fgd/point/halloween/halloween_souls_pack.fgd index 14fc57841..8ad8c44ae 100644 --- a/fgd/point/halloween/halloween_souls_pack.fgd +++ b/fgd/point/halloween/halloween_souls_pack.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityAnimating, TeamNum) + iconsprite("editor/ficool2/halloween_souls_pack") appliesto(TF2) = halloween_souls_pack : "GARGOYLE SOUL" [ diff --git a/fgd/point/hightower_teleport_vortex.fgd b/fgd/point/hightower_teleport_vortex.fgd index 039a0835c..b235096c8 100644 --- a/fgd/point/hightower_teleport_vortex.fgd +++ b/fgd/point/hightower_teleport_vortex.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/hightower_teleport_vortex") appliesto(TF2) = hightower_teleport_vortex: "Halloween Vortex" [ target_base_name(string) : "Destination base name" : : "The base name of the destination. (ie. If using 'hell_winner' and 'hell_loser', then 'hell' is the base name.)" diff --git a/fgd/point/point/point_intermission.fgd b/fgd/point/point/point_intermission.fgd index 396eac5f3..7baef1722 100644 --- a/fgd/point/point/point_intermission.fgd +++ b/fgd/point/point/point_intermission.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/point_intermission") appliesto(TF2) = point_intermission: "Entity that ends the match and triggers an intermission." [ diff --git a/fgd/point/point/point_populator_interface.fgd b/fgd/point/point/point_populator_interface.fgd index 3fb451624..93a2a230d 100644 --- a/fgd/point/point/point_populator_interface.fgd +++ b/fgd/point/point/point_populator_interface.fgd @@ -1,4 +1,5 @@ -@PointClass base(BaseEntityPoint) +@PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/point_populator_interface") appliesto(TF2) = point_populator_interface: "Entity interface to the populator." [ diff --git a/fgd/point/tf/tf_base_minigame.fgd b/fgd/point/tf/tf_base_minigame.fgd index 1b5ad310a..0199d9531 100644 --- a/fgd/point/tf/tf_base_minigame.fgd +++ b/fgd/point/tf/tf_base_minigame.fgd @@ -3,6 +3,7 @@ appliesto(TF2) line(255 32 32, targetname, redspawn) line(32 32 255, targetname, bluespawn) + iconsprite("editor/ficool2/tf_base_minigame") = tf_base_minigame: "Base minigame" [ redspawn(target_source) : "Red Spawn Name" : : "The name of the spawnpoints for RED for this minigame" diff --git a/fgd/point/tf/tf_gamerules.fgd b/fgd/point/tf/tf_gamerules.fgd index 04b538562..d7cff9684 100644 --- a/fgd/point/tf/tf_gamerules.fgd +++ b/fgd/point/tf/tf_gamerules.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_gamerules") appliesto(TF2) = tf_gamerules: "Proxy entity for TF Gamerules" [ gamemode(string) : "Gamemode Type" : "" : "Server Tags." diff --git a/fgd/point/tf/tf_glow.fgd b/fgd/point/tf/tf_glow.fgd index 99e74d7b1..f5b1e0e49 100644 --- a/fgd/point/tf/tf_glow.fgd +++ b/fgd/point/tf/tf_glow.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_glow") appliesto(TF2) = tf_glow [ target(target_destination) : "Target - ONE TARGET ONLY" diff --git a/fgd/point/tf/tf_logic_arena.fgd b/fgd/point/tf/tf_logic_arena.fgd index fe8d92579..717605824 100644 --- a/fgd/point/tf/tf_logic_arena.fgd +++ b/fgd/point/tf/tf_logic_arena.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_arena") appliesto(TF2) = tf_logic_arena: "This activates Arena Mode, and controls round specific stuff." [ capenabledelay(float) : "Capture Point Enable Time" : 0 diff --git a/fgd/point/tf/tf_logic_competitive.fgd b/fgd/point/tf/tf_logic_competitive.fgd index 02c2a5953..4a5cd2dd2 100644 --- a/fgd/point/tf/tf_logic_competitive.fgd +++ b/fgd/point/tf/tf_logic_competitive.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_competitive") appliesto(TF2) = tf_logic_competitive: "Logic specifically for competitive mode." [ diff --git a/fgd/point/tf/tf_logic_cp_timer.fgd b/fgd/point/tf/tf_logic_cp_timer.fgd index 7c8cf05d9..cedc7c271 100644 --- a/fgd/point/tf/tf_logic_cp_timer.fgd +++ b/fgd/point/tf/tf_logic_cp_timer.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_cp_timer") appliesto(TF2) = tf_logic_cp_timer: "Control Point Timer Entity." [ controlpoint(target_destination) : "Control Point" : : "The team_control_point associated with this timer." diff --git a/fgd/point/tf/tf_logic_holiday.fgd b/fgd/point/tf/tf_logic_holiday.fgd index b1f193046..4a266edf7 100644 --- a/fgd/point/tf/tf_logic_holiday.fgd +++ b/fgd/point/tf/tf_logic_holiday.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_holiday") appliesto(TF2) = tf_logic_holiday: "Holiday Entity. This is used to detect a holiday map." [ holiday_type[engine](integer) : "Holiday" : 1 diff --git a/fgd/point/tf/tf_logic_hybrid_ctf_cp.fgd b/fgd/point/tf/tf_logic_hybrid_ctf_cp.fgd index 7444b7b4d..ed658c14d 100644 --- a/fgd/point/tf/tf_logic_hybrid_ctf_cp.fgd +++ b/fgd/point/tf/tf_logic_hybrid_ctf_cp.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_hybrid_ctf_cp") appliesto(TF2) = tf_logic_hybrid_ctf_cp: "When present, this activates Atack/Defend With Intelligence mode." [ ] diff --git a/fgd/point/tf/tf_logic_koth.fgd b/fgd/point/tf/tf_logic_koth.fgd index 5b3ec2026..e0ab604ae 100644 --- a/fgd/point/tf/tf_logic_koth.fgd +++ b/fgd/point/tf/tf_logic_koth.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_koth") appliesto(TF2) = tf_logic_koth: "King of the Hill Entity. This is used to detect a KOTH map." [ timer_length(integer) : "Timer length (in seconds)" : 180 : "Initial timer length for each team." diff --git a/fgd/point/tf/tf_logic_mann_vs_machine.fgd b/fgd/point/tf/tf_logic_mann_vs_machine.fgd index 0763fcf4e..87d7221a1 100644 --- a/fgd/point/tf/tf_logic_mann_vs_machine.fgd +++ b/fgd/point/tf/tf_logic_mann_vs_machine.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_mann_vs_machine") appliesto(TF2) = tf_logic_mann_vs_machine: "When present, this activates Mann VS Machine Mode." [ ] diff --git a/fgd/point/tf/tf_logic_mannpower.fgd b/fgd/point/tf/tf_logic_mannpower.fgd index f99d17ab3..f268c0b2b 100644 --- a/fgd/point/tf/tf_logic_mannpower.fgd +++ b/fgd/point/tf/tf_logic_mannpower.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_mannpower") appliesto(TF2) = tf_logic_mannpower: "When present, this activates Mannpower Mode." [ ] diff --git a/fgd/point/tf/tf_logic_medieval.fgd b/fgd/point/tf/tf_logic_medieval.fgd index 825cefa72..17b479f48 100644 --- a/fgd/point/tf/tf_logic_medieval.fgd +++ b/fgd/point/tf/tf_logic_medieval.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + iconsprite("editor/ficool2/tf_logic_medieval") appliesto(TF2) = tf_logic_medieval: "When present, this enables Medieval mode." [ ] diff --git a/fgd/point/tf/tf_logic_minigames.fgd b/fgd/point/tf/tf_logic_minigames.fgd index 2cacc8eaa..73c2e72a2 100644 --- a/fgd/point/tf/tf_logic_minigames.fgd +++ b/fgd/point/tf/tf_logic_minigames.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_minigames") appliesto(TF2) = tf_logic_minigames: "Minigame mode logic." [ // Inputs diff --git a/fgd/point/tf/tf_logic_multiple_escort.fgd b/fgd/point/tf/tf_logic_multiple_escort.fgd index 0c1f69907..f4b9d3159 100644 --- a/fgd/point/tf/tf_logic_multiple_escort.fgd +++ b/fgd/point/tf/tf_logic_multiple_escort.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_multiple_escort") appliesto(TF2) = tf_logic_multiple_escort: "When present, activates Playload Race Mode." [ ] diff --git a/fgd/point/tf/tf_logic_on_holiday.fgd b/fgd/point/tf/tf_logic_on_holiday.fgd index 8b8d93cb8..762404ff1 100644 --- a/fgd/point/tf/tf_logic_on_holiday.fgd +++ b/fgd/point/tf/tf_logic_on_holiday.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) - iconsprite("editor/logic_auto.vmt") + iconsprite("editor/ficool2/tf_logic_on_holiday") = tf_logic_on_holiday: "This entity allows you execute map actions on holidays. " + "Will send all relevant outputs every time the Fire input is called." [ diff --git a/fgd/point/tf/tf_logic_player_destruction.fgd b/fgd/point/tf/tf_logic_player_destruction.fgd index f8258045a..9bf33c1f4 100644 --- a/fgd/point/tf/tf_logic_player_destruction.fgd +++ b/fgd/point/tf/tf_logic_player_destruction.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_player_destruction") appliesto(TF2) = tf_logic_player_destruction: "Player Destruction Entity. This is used to detect a Player Destruction map." [ prop_model_name(studio) : "Prop Model Name" : "models/flag/flag.mdl" : "model of prop that drops from player on death" diff --git a/fgd/point/tf/tf_logic_robot_destruction.fgd b/fgd/point/tf/tf_logic_robot_destruction.fgd index 3a7a00f56..dafd13086 100644 --- a/fgd/point/tf/tf_logic_robot_destruction.fgd +++ b/fgd/point/tf/tf_logic_robot_destruction.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, RobotDestruction) + iconsprite("editor/ficool2/tf_logic_robot_destruction") appliesto(TF2) = tf_logic_robot_destruction: "Robot Destruction Entity. This is used to detect a Robot Destruction map." [ max_robots(float) : "Max Robots" : 0 : "How many Robot" diff --git a/fgd/point/tf/tf_logic_training_mode.fgd b/fgd/point/tf/tf_logic_training_mode.fgd index 15ef7381c..d849ca831 100644 --- a/fgd/point/tf/tf_logic_training_mode.fgd +++ b/fgd/point/tf/tf_logic_training_mode.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_logic_training_mode") appliesto(TF2) = tf_logic_training_mode: "Training logic entity. This is used to detect a training map." [ nextmap[engine](string) : "Next Map" diff --git a/fgd/point/tf/tf_point_nav_interface.fgd b/fgd/point/tf/tf_point_nav_interface.fgd index d8f0f4868..6e2babdf5 100644 --- a/fgd/point/tf/tf_point_nav_interface.fgd +++ b/fgd/point/tf/tf_point_nav_interface.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_point_nav_interface") appliesto(TF2) = tf_point_nav_interface: "Entity interface to the TF nav mesh." [ diff --git a/fgd/point/tf/tf_projectiles.fgd b/fgd/point/tf/tf_projectiles.fgd index 2c4b31525..fb4824170 100644 --- a/fgd/point/tf/tf_projectiles.fgd +++ b/fgd/point/tf/tf_projectiles.fgd @@ -1,120 +1,127 @@ @BaseClass base(BaseEntityAnimating, TeamNum) = BaseTFProjectile [] -@PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/props_halloween/hwn_spellbook_flying.mdl") -= tf_projectile_spellmeteorshower : "Meteor Spell Particle" [] - @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellmeteorshower") = tf_projectile_spellmeteorshower : "Meteor Spell Particle" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellfireball") = tf_projectile_spellfireball : "FireBall Spell Particle" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellspawnzombie") = tf_projectile_spellspawnzombie : "Skeleton Spell" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_lightningorb") = tf_projectile_lightningorb : "Lightning orb Spell" [] + @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_mechanicalarmorb") = tf_projectile_mechanicalarmorb : "Short Circuit orb" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_arrow.mdl") + studioprop("models/weapons/w_models/w_arrow.mdl") = tf_projectile_arrow : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/c_xms_festive_ornament.mdl") + studioprop("models/weapons/c_models/c_xms_festive_ornament.mdl") = tf_projectile_ball_ornament : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_balloffire") = tf_projectile_balloffire : "Dragon's Fury's Flame" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl") + studioprop("models/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl") = tf_projectile_cleaver : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_energy_ball") = tf_projectile_energy_ball : "Cow Mangler 5000 Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_energy_ring") = tf_projectile_energy_ring : "Righteous Bison Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_flaregun_shell.mdl") + studioprop("models/weapons/w_models/w_flaregun_shell.mdl") = tf_projectile_flare : "Flare Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/c_grappling_hook/c_grappling_hook.mdl") + studioprop("models/weapons/c_models/c_grappling_hook/c_grappling_hook.mdl") = tf_projectile_grapplinghook : "Grappling Hook Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_repair_claw.mdl") + studioprop("models/weapons/w_models/w_repair_claw.mdl") = tf_projectile_healing_bolt : "Rescue Ranger Arrow" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/urinejar.mdl") + studioprop("models/weapons/c_models/urinejar.mdl") = tf_projectile_jar : "Jarate" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/c_gascan/c_gascan.mdl") + studioprop("models/weapons/c_models/c_gascan/c_gascan.mdl") = tf_projectile_jar_gas : "Gas Passer Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/c_models/c_madmilk/c_madmilk.mdl") + studioprop("models/weapons/c_models/c_madmilk/c_madmilk.mdl") = tf_projectile_jar_milk : "Mad Milk Projectile" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_grenade_grenadelauncher.mdl") + studioprop("models/weapons/w_models/w_grenade_grenadelauncher.mdl") = tf_projectile_pipe : "Pipe Bomb" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_stickybomb.mdl") + studioprop("models/weapons/w_models/w_stickybomb.mdl") = tf_projectile_pipe_remote : "Sticky Bomb" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_rocket.mdl") + studioprop("models/weapons/w_models/w_rocket.mdl") = tf_projectile_rocket : "Rocket" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/buildables/sentry3_rockets.mdl") + studioprop("models/buildables/sentry3_rockets.mdl") = tf_projectile_sentryrocket : "Sentry Level 3 Rockets" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellbats") = tf_projectile_spellbats : "Bats spell" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellkartbats") = tf_projectile_spellkartbats : "Bumper Car's Bats spell" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellkartorb") = tf_projectile_spellkartorb : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellmirv") = tf_projectile_spellmirv : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spellpumpkin") = tf_projectile_spellpumpkin : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/props_mvm/mvm_human_skull_collide.mdl") + iconsprite("editor/ficool2/tf_projectile_spellspawnboss") = tf_projectile_spellspawnboss : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/props_mvm/mvm_human_skull_collide.mdl") + iconsprite("editor/ficool2/tf_projectile_spellspawnhorde") = tf_projectile_spellspawnhorde : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/props_mvm/mvm_human_skull_collide.mdl") -= tf_projectile_spellspawnzombie : "" [] - -@PointClass base(BaseTFProjectile) appliesto(TF2) + iconsprite("editor/ficool2/tf_projectile_spelltransposeteleport") = tf_projectile_spelltransposeteleport : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_baseball.mdl") + studioprop("models/weapons/w_models/w_baseball.mdl") = tf_projectile_stun_ball : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) - studio("models/weapons/w_models/w_syringe_proj.mdl") + studioprop("models/weapons/w_models/w_syringe_proj.mdl") = tf_projectile_syringe : "" [] @PointClass base(BaseTFProjectile) appliesto(TF2) @@ -128,6 +135,3 @@ @PointClass base(BaseTFProjectile) appliesto(TF2) = tf_projectile_throwable_repel : "Unused Balloon with no model" [] - -@PointClass base(BaseTFProjectile) appliesto(TF2) -= tf_projectile_mechanicalarmorb : "Short Circult Enegry Ball" [] diff --git a/fgd/point/tf/tf_robot_destruction_robot_spawn.fgd b/fgd/point/tf/tf_robot_destruction_robot_spawn.fgd index 995bcde46..d40921ba6 100644 --- a/fgd/point/tf/tf_robot_destruction_robot_spawn.fgd +++ b/fgd/point/tf/tf_robot_destruction_robot_spawn.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) - studio("models/bots/bot_worker/bot_worker_a.mdl") + iconsprite("editor/ficool2/tf_robot_destruction_robot_spawn") line(255 255 255, targetname, startpath) line(255 255 0, targetname, spawngroup) = tf_robot_destruction_robot_spawn: "Robot Destruction Robot spawn point." diff --git a/fgd/point/tf/tf_robot_destruction_spawn_group.fgd b/fgd/point/tf/tf_robot_destruction_spawn_group.fgd index 9982fcc2f..64e1e1a1d 100644 --- a/fgd/point/tf/tf_robot_destruction_spawn_group.fgd +++ b/fgd/point/tf/tf_robot_destruction_spawn_group.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint) + iconsprite("editor/ficool2/tf_robot_destruction_spawn_group") appliesto(TF2) = tf_robot_destruction_spawn_group: "Robot Destruction spawn group. This groups the Robot Destruction spawn points" [ respawn_time(float) : "Respawn Time" : 0 : "Respawn time for this group" diff --git a/fgd/point/tf/tf_spawner.fgd b/fgd/point/tf/tf_spawner.fgd index 6ebb95584..5146c448a 100644 --- a/fgd/point/tf/tf_spawner.fgd +++ b/fgd/point/tf/tf_spawner.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(TF2) line(255 255 255, targetname, template) + iconsprite("editor/ficool2/tf_spawner") = tf_spawner: "An entity that spawns templatized entities." [ count(integer) : "Count" : 1 : "Total number of entities to spawn over the lifetime of this spawner." diff --git a/fgd/point/training/training_annotation.fgd b/fgd/point/training/training_annotation.fgd index 2cfc5df87..2b535a804 100644 --- a/fgd/point/training/training_annotation.fgd +++ b/fgd/point/training/training_annotation.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityAnimating) appliesto(TF2) - studio("models/extras/info_speech.mdl") + iconsprite("editor/ficool2/training_annotation") = training_annotation: "Training Annotation" [ display_text(string) : "Displayed Text" : "" : "The text to be displayed in the annotation popup." From 98db0be4fa5d7ccaf93f45160855535c5d720597 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 23:19:06 -0800 Subject: [PATCH 030/243] Convert broadcastclientcommand icon to VTF 7.4 --- .../editor/point_broadcastclientcommand.vtf | Bin 5720 -> 5712 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/hammer/materials/editor/point_broadcastclientcommand.vtf b/hammer/materials/editor/point_broadcastclientcommand.vtf index 15d96a725bcb8c0f540480359eb2f557a3519352..a1b601345de0bc0f22098a9dc795a2d7c4b98c23 100644 GIT binary patch literal 5712 zcmdUz4Qx}_6@afDJFyd-*oj|fCxtr1X%ZM)Ll>>4fxP^fE@%>V@Cy(PWE|R+Mj=tt zv8iaY$zKO8RhlJ?R!9YE(&9>`s)Wb>Fv!$&omw)|q?yt}RA+-yNWw~$jwZP~_uAKa z?*S@MX% ztFtf7Gb8-M-yW|}ntzQy+|XyMSAX{>;Lz4+jZ4`ROOA@|4G<3%;rg@LtRRQjh?XYtH=e6jkV`Jg6HyZ(6fq!&+xBDTtT zU{TG|=yXQ+(8c_nH5PO!pimT_!|OAxFZ8FbIf0G@_E+T04s}jnKZ2Rv*jG*6!|Z3w z{9rgeHu;G6+9S*#m{;f#T+A;B$Mk3ZVa)tga>aW!1==%z<%D4H$2^Of-x>!n#*K@$ z%zw=x;Ofj&=EwX0;k}l!#PE0KPwtc8*2**}^_!o9E zz6A9%lhw(LL2j$gMlW5+fWhI4*g9<_zS#Ig9|aUv|1(cgZWv>Maau#v6cw>%Rn$}% z2uS3gh~%XHTO$5v@z}j?_hjUYjQwr4uB4Jf#iw*gky3E0@uL<`4s!Gu+4vPXVbe}^ zvYK(&biioYcMK*Z7H@=8tzD)$uFv96kO_;)>oY5L3(RKoOu9a4NxyPQr8z12z6A>w zly;3z%#JtB#O>Fz`i~YJFs_5 zaJ_T&JBp(7=KpSMKN-KctIcLFYSZ2KodTUQxN__EVB<&x{GZ<|2)-RS14SCAj~<1g z@z36-9Gt3rp(!6fqh0;P30@5agf&E@SC{&LR>yT<;P~ucke+cgg3Oz zs`jrb1C=jSAoN;55ELk;Kk1zm-uM2*^h(dj)SnSL)FBADA@8sL)w5+}0`@7BQ4n|^ ztjBT>Wt9f>4dKr*a?|=xyhZf~C%CCxp(O3gW?uG;54_QWkkiAh_w5MkhC_`bgau*U zO~#K*l)m3oK*r0rs|D;MT6I|o8&nGutZ%oVER_nz_s(K)iM~BoGjNJ9=mSjjZ`!*T z^z}}k_U=1Dcuif+4dcn}pQZlzxq{4xARN{H`#Ma{ml%`y5MaJxJ$G9{qi+jO6Mg$N zyP3ZC{1WV$|ItatnyKKX?ZJkJBgDSJUJjp)ouqxXM~msVc)WlAIdcA~9W1_M?65Ot z`^kTc>G&MSecgDwhBB_|;bQtF{q%r76)wgwpIs`rh(DmaM1S)B3A+S{fVe+cd&acB z+Ub(8%bD;Gm8%?5xx+4%D_l~!%PG}I;Yy58`r|mGK{6g^cS_h{=e~*k95-h#)n9PQ z=3kKL?{c{$yR5xJ#`5}dIVYv@S>VNgrh=#a|EV@Ee&%a`9nlWi+5CM}PeP9-a}+WgzKUzIz`@laU*#GZ0dzDQVn zUJy1y5!cs)d&F!Y4aEK;k8>hc8`Zb8AF@*u#-TD+e@wS9t)=D3g#D1M>V61?*`H@_ zo44($ZQJ_MaNF|!q~%Fwf3h0N9oa9_zZ~)bHa@kVm$FeNrw^&OoX=6tVVr9(pKm#n z`RCnp^X~`y`#&hrF@2&Yzb;?>UYOb6YN$-U+|=;&J7~0R1%yD@eXoA{{M72No?1Pn_ZMwW z-+1oBlI*9=ADyK6Pe&dJzhqedE^^4Pv2)i$xf&Je-^3HOl>C0IxC?6;JPb_~W_Q>;JIkWXat%nbccqPeyA<0LA z%f*A7=EIZ)!60Mf%~=_1F9;5dv|Xfc+MeTJKmJi`qJKy@W$np+-EGyx>%;ku$;$`U z-fmY>Ij9)d$0fJtP8s^6=K~pomiYQ5{loSK_LHKt5H5E*o$!yo4n!RI=q_w`9i4^V@y7ZKCxMEQDreiHd=jH{}|^CPI7Un|E5f*T5L&$$xkCuO5S4`$H!bCgBT4}>{| z5!M?RA6r7SpF6czUOyK9lKF>zyQuvxNJy!F|-N6!BNnptk5 literal 5720 zcmdT|4Nz3q6~1qO;4Ng~ZQQ4e7JSHJ)_=@MbwPANHWRQ`mh2NzF;r;%uT~VT#wh_b zQG)I`L;M+1gCQi@R>@3tZA^?H%}UIuq$6m9GAKk7kbr-KfAD(lTkh_@XPua2rfsfo z@qK&lIrn_$o^$VkIrbM&AVNqDpC$0Iz(2$xM8P{AuHe#ye|3MB@JdTTg6Erp>7nov zsD&R4BTiF#1maKp1-26dpKstZBYlRV&G<*h=|vpM(p-7<^gIsnPK1JUIGP`A;yUJ0 z%-7Dh9V4eI4b_=@wM9m?$;pqXh@Pb4I@aw=A8CowPTHTfd9dI@Ez}k%&&RW?O3?HGOd`SH-A8B#y1L?O)DGD{(3FMW?7Bp0s2EYdTli# zypz5|JwnPYf$&cpz$6X=`h93?p~6Js!E|oeaUC8K7Xdp9LuJY}cBA}uf=QhEf(sNJ z3aKIS>JNOCK^1T2lDIw4f%2sd(@Fg92Lb-7ej14*>izug@Rd9w@uXcnA#-~|;z?ZJ ztrk`_3pghmm*(UoX&(ad#)U%?M@1s<+)1TyGgsjcY|7 zCM+wIII}jR4a3|!^acA7{o<3Kf@a&#Dc>Xnn4hMF+ z`a-TSA$UFy)CzC7bEd*`a8o3GJUsk3iPxCr96I0Odf$f;N}`Q4!Pvi=u|^}oW0pfr zm_`KqV}=f(g+~^og6H6-B<73J+*iFr-d4uJvo&tyl)pHg6@^`%^{;_HDH=@>(Vq$H zpS<7{2p*jiN!Rr{GYI}5J(3yQ;lmxk*K)*pm&k*25}6N=c4P?;Ybbqc zX-jCynR?A^K|w*mS^+i*4vc2CsVI6uq0=dJVeEivuZx~f`>;DsX;`t|>FoLrSLsew z@i-Ni-UzP)^Npr=jj8pcokAzpzw?|brcNDpwENy|O^qU^pvb%>rYIodmw~0KkMct{ zp%pW>PK2|qv2XY9UK3{P-Cd6!v>+s0`0bezhcFyL3iTcbEbs@mbqk>uSKss@ZV{)> zx+hepOe+`O>c#Uj|F*GPn4od>UEk0o%6E+`6V8qQxNmsI(MbA4W9A~YGxST1YaCUd zoMX6im(NfPaw$LlaPj;$141m@$PW@0E*m*Gu=&qm@9Wa_FZZE_f{465#(iLKivBVm z*A9n+a&hy%Ca7Y1J^#kdrFR6B-TY~9BHeM|GQYj&SGNR|E9zGOHVIwjxoK_nQ^4Q! ztFf5@4%e546Dx;3Y$;^;jJZ6eb1jW`1;@Sv>zA?Yp@(Q9b2ZZ8xBz?uXQ_8@Zx!|% zg;3;z^YVFAkX3TUgRwk@ex2_xDTqmd{aJS~l6Dl(yikG;V~qVvULXT6{U~LZF=ag^)+c{Nb6;?+Y<_Wuqley~)5s{8KIl^6(H0`{_`@gX00_y!Pbw zF}CwO|D^TA`D&Y;$VF_odE+BuRhm?uW|hh*yHswsN&TVh-u1CPmeuPd>#9dT^# zhR%|k-8~`QJ&fC|8jKX0=%>wpIsd?r!PPLN4Sjm^Vz62H8Sm4 zNN^ojtiRioHbXenpy+A{D$N;G9P;MHi>rsW51Lv#XhCi6C}Rk0EA^!Mm-F8;vCiP1 zzjcOI7WBRKC}NB6yc993_vWW(K4bCvhxI9)T7vayQn~0KtO6ryM3lk*e62G?X|pZa z1w_AHs|b#5y10hmsIW@y=UddVmv4K{zociC0n3)&xN!#c6ixd^nb2nVM*yYKL_aNm zRb(q`fkEtJK8X|s@$(Ff@y+3n^)UP~Ba`bu!CwwyiG8(R?X@rGuZ*mNc$}C{^xL%~ zg7fm$c=@*(+O(5l5dKkNMbG4~&ChQ@7mE@alnKhP$^bQtAH-kgf1=nAz?U#Y{uU`8 zy-bc<4C1ex57tl9KUx0x{3xv@+qeIfz`x;qtd`hsGgNA$)o1Dm|3cl2fE%lpZ>&eH zMbjV!>I{`3S`DRH{m`{4-nDcXzW-Ua%zJ*w^WSaWI+1rm|0}XZ=>H8@nTxe8Q&0i% zuRSuCU0wd(w>x@gKU_Pzm#G|;8?vzFLhR6uVV7#dFV$9=w?yH54S@UK2rUu^?~QPt z_9Mk=DrL9PZRX&(JTg$<4zd4(DL)(Rj#e;Zf~)z`j&pGSXE)a;C=B=d1zxcP_dlNg z74wnzzj93KgZ!mMKo$k=e{rM7+wD-Vo_<)wJP-Yd1VjD*C#>-3^E~tu8u9NNqCU$) zy}wxKwGR!aOdp@uyNvX}`f$Gsh{DH;$DY-y66N6HfIoIQpC5|Bp4cDy_~X?)>%s9h za9$}myk4TshWix;yM!^cH{hRVU~IGL?Ectlu@2~a?TP0dH((#kdG802k6Mb8^>*Ov zvv0K~!+MroMvFJTupY!KMFEp=w)_5jT8#PQ`XKXRJvj#Z{&I~>Zjs@~^naq9b{HXr z#Q2E$0P%+u!r0r7fqxiVpuR~M2%qo6IDYu|12TUg-k$vO>(>YN$J?)ZaB}j%e2;%B zF+ap@c(NRl8-^%n?cV!S#CjbA_V8wUDQUfm(` xw?jkf;pf3N2)I8^h6dLk+iX&toGitU$@NmXj*()k%)Vaq2lMy&E9WD}{{}^ONSXiu From 65701ab87fa0331f785b4ed0d1e2006b24934fe6 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 9 Mar 2023 23:57:29 -0800 Subject: [PATCH 031/243] Add hammer display settings to engineer buildings --- fgd/bases/BaseObject.fgd | 14 ++++++++++---- fgd/point/obj/obj_dispenser.fgd | 10 +++++++++- fgd/point/obj/obj_sentrygun.fgd | 10 +++++++++- fgd/point/obj/obj_teleporter.fgd | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/fgd/bases/BaseObject.fgd b/fgd/bases/BaseObject.fgd index c3316eef0..8a850f577 100644 --- a/fgd/bases/BaseObject.fgd +++ b/fgd/bases/BaseObject.fgd @@ -3,18 +3,24 @@ teamnum[engine](integer) : "Team" : 2 teamnum(choices) : "Team" : 2 : "Team" = [ - 2: "Red" - 3: "Blue" + 2: "RED" + 3: "BLU" ] + skin[!engine](choices) : "[H] Team" : 0 : "Team to show in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + ] + spawnflags(flags) : "spawnflags" = [ 2: "Invulnerable" : 0 ] // Inputs - input SetBuilder(string) : "Sets the builder of this object to the player given (from an output)" - input SetSolidToPlayer(string) : "Sets the builder solid to the player given (from an output)" + input SetBuilder(string) : "Sets the builder of this object to the !activator" + input SetSolidToPlayer(integer) : "Sets if the building should be solid to players other than its builder" input Show(void) : "Makes the building visible and tries to re-enable it." input Hide(void) : "Makes the building invisible and disables it." input SetHealth(integer) : "Sets a new value for the breakable's health. If the breakable's health reaches zero it will break." diff --git a/fgd/point/obj/obj_dispenser.fgd b/fgd/point/obj/obj_dispenser.fgd index 179cc23c1..904faa257 100644 --- a/fgd/point/obj/obj_dispenser.fgd +++ b/fgd/point/obj/obj_dispenser.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityAnimating, BaseObject) appliesto(TF2) - studio("models/buildables/dispenser_light.mdl") + studioprop() = obj_dispenser: "TF2 Dispenser" [ defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 @@ -11,6 +11,14 @@ 2: "Level 3" ] + model[engine](string) : "Model" : : "Doesn't exist in engine, this is a hack to prevent auto packing" + model(choices) : "[H] Upgrade Level" : "models/buildables/dispenser_light.mdl" : "Upgrade level to show in Hammer" = + [ + "models/buildables/dispenser_light.mdl" : "Level 1" + "models/buildables/dispenser_lvl2_light.mdl" : "Level 2" + "models/buildables/dispenser_lvl3_light.mdl" : "Level 3" + ] + spawnflags(flags) : "spawnflags" = [ 4: "Upgradable" : 0 diff --git a/fgd/point/obj/obj_sentrygun.fgd b/fgd/point/obj/obj_sentrygun.fgd index de79bd85c..6bc0748e8 100644 --- a/fgd/point/obj/obj_sentrygun.fgd +++ b/fgd/point/obj/obj_sentrygun.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityAnimating, BaseObject) appliesto(TF2) - studio("models/buildables/sentry3.mdl") + studioprop() = obj_sentrygun: "TF2 Sentrygun" [ defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 @@ -11,6 +11,14 @@ 2: "Level 3" ] + model[engine](string) : "Model" : : "Doesn't exist in engine, this is a hack to prevent auto packing" + model(choices) : "[H] Upgrade Level" : "models/buildables/sentry1.mdl" : "Upgrade level to show in Hammer" = + [ + "models/buildables/sentry1.mdl" : "Level 1" + "models/buildables/sentry2.mdl" : "Level 2" + "models/buildables/sentry3.mdl" : "Level 3" + ] + spawnflags(flags) : "spawnflags" = [ 4: "Upgradable" : 0 diff --git a/fgd/point/obj/obj_teleporter.fgd b/fgd/point/obj/obj_teleporter.fgd index 60815ca76..4f99096ba 100644 --- a/fgd/point/obj/obj_teleporter.fgd +++ b/fgd/point/obj/obj_teleporter.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityAnimating, BaseObject) appliesto(TF2) - studio("models/buildables/teleporter_light.mdl") + studioprop("models/buildables/teleporter_light.mdl") line(255 255 255, targetname, matchingteleporter) = obj_teleporter: "TF2 Teleporter" [ From 6f72664ab0766ee2cce4852222976eed9c5ba2b9 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 10 Mar 2023 00:39:37 -0800 Subject: [PATCH 032/243] Add hammer display settings for intelligence and capture points --- fgd/point/item/item_teamflag.fgd | 14 +++++++--- fgd/point/team/team_control_point.fgd | 38 ++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/fgd/point/item/item_teamflag.fgd b/fgd/point/item/item_teamflag.fgd index 4d43d7d58..90ad742a2 100644 --- a/fgd/point/item/item_teamflag.fgd +++ b/fgd/point/item/item_teamflag.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPhysics, TeamNum, EnableDisable, TFGameType) appliesto(TF2) - studio("models/flag/briefcase.mdl") = item_teamflag: "Team Fortress flag entity." + studioprop() = item_teamflag: "Team Fortress flag entity." [ returntime(integer) : "Return time (in seconds)" : 60 : "Length of time (in seconds) before dropped flag/intelligence returns to base." @@ -22,7 +22,7 @@ 1: "Increment capture count" ] - flag_model(string) : "Model" : "models/flag/briefcase.mdl" : "The model to be used for this entity." + flag_model(studio) : "Model" : "models/flag/briefcase.mdl" : "The model to be used for this entity." flag_icon(string) : "Icon" : "../hud/objectives_flagpanel_carried" : "The icons used for the HUD in some game modes. " + "Format: materials/vgui/[materialname]_red and materials/vgui/[materialname]_blue" flag_paper(particlesystem) : "Paper Particle" : "player_intel_papertrail" : "Particle effect used for the falling paper trail." @@ -44,7 +44,15 @@ returnbetweenwaves(boolean) : "Return Between Waves" : "1" : "Used only for MvM mode. Determines if the flag should return home between waves." - tags(string) : "tags" : : "Tags used for the AI bomb carrier to avoid nav areas that have matching tags. Tags need to be separated by empty space." + tags(string) : "AI nav avoid tags" : : "Tags used for the AI bomb carrier to avoid nav areas that have matching tags. Tags need to be separated by empty space." + + model(studio) : "[H] Model" : "models/flag/briefcase.mdl" : "The model to show in Hammer" + skin(choices) : "[H] Team" : 0 : "The team to show in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + 2 : "Neutral" + ] // Inputs input ForceDrop(void) : "Force the flag to be dropped if it's being carried by a player." diff --git a/fgd/point/team/team_control_point.fgd b/fgd/point/team/team_control_point.fgd index 7179cc8db..edfe11c4d 100644 --- a/fgd/point/team/team_control_point.fgd +++ b/fgd/point/team/team_control_point.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityAnimating, EnableDisable) appliesto(TF2) - studio("models/effects/cappoint_hologram.mdl") + studioprop() = team_control_point: "Control Point" [ spawnflags(flags) : "spawnflags" = @@ -39,48 +39,50 @@ random_owner_on_restart(boolean) : "Randomly set the owner on restart" : "0" : "Randomly set the owner of this point during a full restart of the map. The ratio of default owners among the points with this flag will be kept when selecting random owners." team_timedpoints_2(integer) : "Time-based point value for RED." : 0 - team_timedpoints_3(integer) : "Time-based point value for BLUE." : 0 + team_timedpoints_3(integer) : "Time-based point value for BLU." : 0 team_capsound_0(sound) : "Reset Sound" : : "Sound made when point resets." - team_capsound_2(sound) : "Red Capture Sound" : : "Sound made when RED captures." - team_capsound_3(sound) : "Blue Capture Sound" : : "Sound made when BLUE captures." - team_model_0(studio) : "Reset Model" : "models/effects/cappoint_hologram.mdl" : "Model when point reset." + team_capsound_2(sound) : "RED Capture Sound" : : "Sound made when RED captures." + team_capsound_3(sound) : "BLU Capture Sound" : : "Sound made when BLU captures." + team_model_0(studio) : "Neutral Model" : "models/effects/cappoint_hologram.mdl" : "Model when neither team owns the point." team_model_2(studio) : "RED Model" : "models/effects/cappoint_hologram.mdl" : "Model when RED owns the point." - team_model_3(studio) : "BLUE Model" : "models/effects/cappoint_hologram.mdl" : "Model when BLUE owns the point." - team_bodygroup_0(integer) : "Reset model bodygroup" : 3 - team_bodygroup_2(integer) : "RED model bodygroup" : 1 - team_bodygroup_3(integer) : "BLUE model bodygroup" : 1 + team_model_3(studio) : "BLU Model" : "models/effects/cappoint_hologram.mdl" : "Model when BLU owns the point." + team_bodygroup_0(integer) : "Neutral model bodygroup" : 3 : "Model bodygroup when neither team owns the point" + team_bodygroup_2(integer) : "RED model bodygroup" : 1 : "Model bodygroup when RED owns the point" + team_bodygroup_3(integer) : "BLU model bodygroup" : 1 : "Model bodygroup when BLU owns the point" team_icon_0(material) : "HUD icon neutral" : "sprites/obj_icons/icon_obj_neutral" : "HUD icon material when no one owns the point." team_icon_2(material) : "HUD icon RED" : "sprites/obj_icons/icon_obj_red" : "HUD icon material when RED owns the point." - team_icon_3(material) : "HUD icon BLUE" : "sprites/obj_icons/icon_obj_blu" : "HUD icon material when BLUE owns the point." + team_icon_3(material) : "HUD icon BLU" : "sprites/obj_icons/icon_obj_blu" : "HUD icon material when BLU owns the point." team_overlay_0(material) : "HUD overlay neutral" : : "HUD material that will overlay the icon when no one owns the point." team_overlay_2(material) : "HUD overlay RED" : : "HUD material that will overlay the icon when RED owns the point." - team_overlay_3(material) : "HUD overlay BLUE" : : "HUD material that will overlay the icon when BLUE owns the point." + team_overlay_3(material) : "HUD overlay BLU" : : "HUD material that will overlay the icon when BLU owns the point." team_previouspoint_2_0(target_source) : "RED Previous Required Point 1" : : "The name of a previous capture point that RED must own to be able to capture this point. If empty, the team must own all points preceding this one. Pointing to itself means no previous point required." team_previouspoint_2_1(target_source) : "RED Previous Required Point 2" : : "The name of a second previous capture point that RED must own to be able to capture this point." team_previouspoint_2_2(target_source) : "RED Previous Required Point 3" : : "The name of a third previous capture point that RED must own to be able to capture this point." - team_previouspoint_3_0(target_source) : "BLUE Previous Required Point 1" : : "The name of a previous capture point that BLUE must own to be able to capture this point. If empty, the team must own all points preceding this one. Pointing to itself means no previous point required." - team_previouspoint_3_1(target_source) : "BLUE Previous Required Point 2" : : "The name of a second previous capture point that BLUE must own to be able to capture this point." - team_previouspoint_3_2(target_source) : "BLUE Previous Required Point 3" : : "The name of a third previous capture point that BLUE must own to be able to capture this point." + team_previouspoint_3_0(target_source) : "BLU Previous Required Point 1" : : "The name of a previous capture point that BLU must own to be able to capture this point. If empty, the team must own all points preceding this one. Pointing to itself means no previous point required." + team_previouspoint_3_1(target_source) : "BLU Previous Required Point 2" : : "The name of a second previous capture point that BLU must own to be able to capture this point." + team_previouspoint_3_2(target_source) : "BLU Previous Required Point 3" : : "The name of a third previous capture point that BLU must own to be able to capture this point." + + model(studio) : "[H] Model" : "models/effects/cappoint_hologram.mdl" : "Model to show in Hammer" // Inputs input SetOwner(integer) : "Set the owner of the point." input HideModel(void) : "Hide the control point model." input ShowModel(void) : "Show the control point model again." input SetLocked(integer) : "Lock the control point. 0 = unlocked, 1 = locked" - input SetUnlockTime(integer) : "This will automatically unlock the control point in the specified amound of time (seconds)." + input SetUnlockTime(integer) : "Unlock the control point after the specified number of seconds have passed." input RoundActivate(void) : "Return to its original team and locked." // Outputs output OnOwnerChangedToTeam1(void) : "Sent when owner is changed to RED." - output OnOwnerChangedToTeam2(void) : "Sent when owner is changed to BLUE." + output OnOwnerChangedToTeam2(void) : "Sent when owner is changed to BLU." output OnCapReset(void) : "Sent when owner is changed to neutral." output OnRoundStartOwnedByTeam1(void) : "Sent when a round is starting and the point is owned by RED." - output OnRoundStartOwnedByTeam2(void) : "Sent when a round is starting and the point is owned by BLUE." + output OnRoundStartOwnedByTeam2(void) : "Sent when a round is starting and the point is owned by BLU." output OnCapTeam1(void) : "Sent when RED capture this point." - output OnCapTeam2(void) : "Sent when BLUE capture this point." + output OnCapTeam2(void) : "Sent when BLU capture this point." output OnUnlocked(void) : "Sent when point is unlocked via the SetLocked(0) or SetUnlockTime inputs." @resources From 50396e6fbe7805a662600ecef475f63243a1f54b Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 10 Mar 2023 00:39:56 -0800 Subject: [PATCH 033/243] Remove leftovers from cut raid mode --- fgd/point/tf/tf_logic_boss_battle.fgd | 5 ----- fgd/point/tf/tf_logic_raid.fgd | 4 ---- 2 files changed, 9 deletions(-) delete mode 100644 fgd/point/tf/tf_logic_boss_battle.fgd delete mode 100644 fgd/point/tf/tf_logic_raid.fgd diff --git a/fgd/point/tf/tf_logic_boss_battle.fgd b/fgd/point/tf/tf_logic_boss_battle.fgd deleted file mode 100644 index 36776ed18..000000000 --- a/fgd/point/tf/tf_logic_boss_battle.fgd +++ /dev/null @@ -1,5 +0,0 @@ - -@PointClass base(BaseEntityPoint) - appliesto(TF2) = tf_logic_boss_battle: "Boss Battle Entity. This is used to detect a Boss Battle map." - [ - ] diff --git a/fgd/point/tf/tf_logic_raid.fgd b/fgd/point/tf/tf_logic_raid.fgd deleted file mode 100644 index 6d791226a..000000000 --- a/fgd/point/tf/tf_logic_raid.fgd +++ /dev/null @@ -1,4 +0,0 @@ -@PointClass base(BaseEntityPoint) - appliesto(TF2) = tf_logic_raid: "Raid Entity. This is used to detect a Raid map." - [ - ] From 71718a6da7e645ce45c69d1350051424f5a845cf Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 10 Mar 2023 01:44:20 -0800 Subject: [PATCH 034/243] Add TF2 point_worldtext keyvalues --- fgd/point/point/point_worldtext.fgd | 59 ++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/fgd/point/point/point_worldtext.fgd b/fgd/point/point/point_worldtext.fgd index fb065c2c3..3ccbf9dfc 100644 --- a/fgd/point/point/point_worldtext.fgd +++ b/fgd/point/point/point_worldtext.fgd @@ -1,19 +1,66 @@ -@PointClass base(BaseEntityPoint) + +// TF2 hammer has no worldtext() helper, so replace it with a model +// FIXME: this needs a custom model, vgui_arrows is oriented the wrong way +@BaseClass studio("models/editor/axis_helper_thick.mdl") = _point_worldtext_tf2 +[ +] + +@PointClass base(BaseEntityPoint, _point_worldtext_tf2) appliesto(since_CSGO, TF2) worldtext() = point_worldtext: "An entity that displays a text message oriented in the world, at its origin." [ spawnflags(flags) : "spawnflags" = [ - 1: "Start Disabled" : 0 + 1: "Start Disabled" : 0 [!TF2] ] message(string) : "Entity Message" textsize(float) : "Text Size" : 10 : "Text Size." - color(color255) : "Color" : "255 255 255" + color[!TF2](color255) : "Color" : "255 255 255" : "Color of the text" + color[TF2](color255) : "Color" : "255 255 255 255" : "Color and opacity of the text" + + font[TF2](choices) : "Font" : 0 : "The font to use for the text" = + [ + 0 : "TF2 Build" + 1 : "TF2 Build (no outline/shadow)" + 2 : "TF2" + 3 : "TF2 (no outline/shadow)" + 4 : "Liberation Sans" + 5 : "Liberation Sans (no outline/shadow)" + 6 : "TF2 Professor" + 7 : "TF2 Professor (no outline/shadow)" + 8 : "Roboto Mono" + 9 : "Roboto Mono (no outline/shadow)" + 10 : "Roboto Mono (shadow only)" + 11 : "Roboto Mono (green glow, soft edges)" + 12 : "TF2 Build (soft edges)" + ] + + orientation[TF2](choices) : "Orientation" : 0 : "Text orientation mode. Fixed orientation left aligns the text, the others center align it." = + [ + 0 : "Fixed orientation" + 1 : "Face player" + 2 : "Face player, yaw only" + ] + + textspacingx[TF2](float) : "Text Spacing X" : 0 : "Spacing between each letter along the X axis. Negative values will invert the text." + textspacingy[TF2](float) : "Text Spacing Y" : 0 : "Spacing between each letter along the Y axis. Only used when newlines have been inserted into the text." + rainbow[TF2](boolean) : "Rainbow text" : 0 : "Enables rainbow text, overriding colors set elsewhere." // Inputs - input Enable(void) : "Start displaying the message text, if the player is within the message radius." - input Disable(void) : "Stop displaying the message text." - input SetMessage(string) : "Set the message text." + input Enable[!TF2](void) : "Start displaying the message text." + input Disable[!TF2](void) : "Stop displaying the message text." + input SetMessage[!TF2](string) : "Set the message text." + + // TF2 Inputs + input SetText[TF2](string) : "Set the message text." + input SetTextSize[TF2](float) : "Set the message text size. Setting this to 0 will hide the text. Negative values flip the text upside down." + input SetTextSpacingX[TF2](float) : "Set the spacing between letters along the X axis. Negative values will invert the text." + input SetTextSpacingY[TF2](float) : "Set the spacing between letters along the Y axis. Only used when newlines are inserted into the text." + input SetColor[TF2](color255) : "Set the message color" + input SetFont[TF2](integer) : "Set the message font. Accepts values 0-12." + input SetOrientation[TF2](integer) : "Sets the message orientation type." + input SetRainbow[TF2](integer) : "Sets rainbow text to be enabled/disabled." + ] From 04017fa7a2285575f4e7bd198af7b197bd408f1f Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 11 Mar 2023 11:27:41 -0800 Subject: [PATCH 035/243] Add some of the things from #179 --- fgd/point/point/point_hurt.fgd | 1 + fgd/point/tf/tf_generic_bomb.fgd | 2 +- fgd/point/tf/tf_logic_cp_timer.fgd | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fgd/point/point/point_hurt.fgd b/fgd/point/point/point_hurt.fgd index 443ab707b..66d8ae46f 100644 --- a/fgd/point/point/point_hurt.fgd +++ b/fgd/point/point/point_hurt.fgd @@ -13,6 +13,7 @@ spawnflags(flags) = [ 1: "Start Active" : 0 + 2: "Bypass UberCharge" : 0 [TF2] ] diff --git a/fgd/point/tf/tf_generic_bomb.fgd b/fgd/point/tf/tf_generic_bomb.fgd index bfc05cf8b..7f8c786e7 100644 --- a/fgd/point/tf/tf_generic_bomb.fgd +++ b/fgd/point/tf/tf_generic_bomb.fgd @@ -15,7 +15,7 @@ 0: "Damage attacker and enemies" 1: "Damage everyone" ] - + passActivator(boolean) : "Pass Activator" : 0 : "Pass the activator in the OnDetonate output" // Inputs input Detonate(void) : "Force detonation." diff --git a/fgd/point/tf/tf_logic_cp_timer.fgd b/fgd/point/tf/tf_logic_cp_timer.fgd index cedc7c271..7fb197bb5 100644 --- a/fgd/point/tf/tf_logic_cp_timer.fgd +++ b/fgd/point/tf/tf_logic_cp_timer.fgd @@ -1,4 +1,4 @@ -@PointClass base(BaseEntityPoint) +@PointClass base(BaseEntityPoint, TeamNum) iconsprite("editor/ficool2/tf_logic_cp_timer") appliesto(TF2) = tf_logic_cp_timer: "Control Point Timer Entity." [ From 4055245450a726473f8e8354cb55cdf33459d8a5 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 10 Mar 2023 16:27:44 +1000 Subject: [PATCH 036/243] Explicitly specify non-"studio" type for model keyvalues only used in Hammer This indicates to the packer that it doesn't need to pack these. --- CHANGELOG.md | 1 + fgd/point/commentary/commentary_dummy.fgd | 1 + fgd/point/env/env_break_shooter.fgd | 2 +- fgd/point/filter/filter_activator_model.fgd | 3 +++ fgd/point/info/info_survivor_rescue.fgd | 2 ++ fgd/point/npc/npc_cscanner.fgd | 1 + fgd/point/npc/npc_personality_core.fgd | 2 +- fgd/point/npc/npc_rocket_turret.fgd | 2 ++ fgd/point/point/point_teleport.fgd | 1 + fgd/point/prop/prop_button.fgd | 1 + fgd/point/prop/prop_floor_ball_button.fgd | 1 + fgd/point/prop/prop_floor_cube_button.fgd | 1 + fgd/point/prop/prop_monster_box.fgd | 2 +- fgd/point/prop/prop_paint_bomb.fgd | 2 ++ fgd/point/prop/prop_tractor_beam.fgd | 2 +- 15 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 525baa574..ce7fbee67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. * Limit the size of propcombined groups to avoid hitting vertex limits. +* Prevent automatically packing models specified only as previews in various entities. -------------------- diff --git a/fgd/point/commentary/commentary_dummy.fgd b/fgd/point/commentary/commentary_dummy.fgd index c60dae136..4812686d0 100644 --- a/fgd/point/commentary/commentary_dummy.fgd +++ b/fgd/point/commentary/commentary_dummy.fgd @@ -4,6 +4,7 @@ studio() = commentary_dummy: "Commentary Dummy" [ + model[engine](string) : "Hammer Preview" model[L4D](studio) : "Dummy Model" : "models/survivors/survivor_biker.mdl" model[L4D2](studio) : "Dummy Model" : "models/survivors/survivor_coach.mdl" diff --git a/fgd/point/env/env_break_shooter.fgd b/fgd/point/env/env_break_shooter.fgd index e0256a245..20cfaa5c6 100644 --- a/fgd/point/env/env_break_shooter.fgd +++ b/fgd/point/env/env_break_shooter.fgd @@ -7,7 +7,7 @@ [ angles(angle) : "Gib Direction (Pitch Yaw Roll)" : "0 0 0" : "The direction the gibs will fly." - model[engine](string) : "Model" : "" + model[engine](string) : "Model" : "" // Suppress default behaviour, this isn't always a path. model(choices) : "Model" : "WoodChunks" : "Thing(s) to shoot out. The choices only matter when the model type is Breakable Chunks. If a specific model is needed, enter its file path. If a point_template is needed, enter the point_template's name." = [ "WoodChunks" : "WoodChunks" diff --git a/fgd/point/filter/filter_activator_model.fgd b/fgd/point/filter/filter_activator_model.fgd index f6bb463f1..22a5f5344 100644 --- a/fgd/point/filter/filter_activator_model.fgd +++ b/fgd/point/filter/filter_activator_model.fgd @@ -4,6 +4,9 @@ iconsprite("editor/filter_model.vmt") = filter_activator_model: "A filter that filters by the model of the activator." [ + // Though this is a model KV, the filter won't be loading the model itself. + // That's up to the target entities. + model[engine](string) : "Filter Model" model(studio) : "Filter Model" : : "The model to filter by. " + "If the filter mode is Allow, only entities whose model matches the given string will pass the filter. " + "If the filter mode is Disallow, all entities EXCEPT those whose model matches the string will pass the filter." diff --git a/fgd/point/info/info_survivor_rescue.fgd b/fgd/point/info/info_survivor_rescue.fgd index 02d6ada71..cf6cfcf4a 100644 --- a/fgd/point/info/info_survivor_rescue.fgd +++ b/fgd/point/info/info_survivor_rescue.fgd @@ -3,5 +3,7 @@ = info_survivor_rescue: "Survivor rescue point" [ rescueeyepos(vecline) : "Eye position of survivors waiting for rescue" + + model[engine](string) : "Hammer Preview" model(studio) : "World model" : "models/editor/playerstart.mdl" ] diff --git a/fgd/point/npc/npc_cscanner.fgd b/fgd/point/npc/npc_cscanner.fgd index c5f683eee..a76bd5cd9 100644 --- a/fgd/point/npc/npc_cscanner.fgd +++ b/fgd/point/npc/npc_cscanner.fgd @@ -4,6 +4,7 @@ autovis(Entities, NPCs, Combine, City Scanner) = npc_cscanner: "City Scanner" [ + model[engine](string) : "Hammer Preview" model[!engine](choices) : "[H] Model" : "models/combine_scanner.mdl" : "These become Claw Scanners if the map name starts with 'd3_c17'. "+ "Use this to change the model shown in Hammer." = [ diff --git a/fgd/point/npc/npc_personality_core.fgd b/fgd/point/npc/npc_personality_core.fgd index 836b4d053..7737cd967 100644 --- a/fgd/point/npc/npc_personality_core.fgd +++ b/fgd/point/npc/npc_personality_core.fgd @@ -20,7 +20,7 @@ 1: "Yes" ] - model[engine](studio) : "Model" : "models/npcs/personality_sphere/personality_sphere.mdl" + model[engine](string) : "Hammer Preview" : "models/npcs/personality_sphere/personality_sphere.mdl" model(choices) : "[H] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in hammer. Set to the same as Use Alternate Skins." = [ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" diff --git a/fgd/point/npc/npc_rocket_turret.fgd b/fgd/point/npc/npc_rocket_turret.fgd index 6ffe777c3..56ebfb18c 100644 --- a/fgd/point/npc/npc_rocket_turret.fgd +++ b/fgd/point/npc/npc_rocket_turret.fgd @@ -21,6 +21,8 @@ rocketlifetime[P2](float) : "Rocket Lifetime" : 20 : "The rocket will automatically detonate after this number of seconds." TripwireMode[P2](boolean) : "Tripwire Mode" : 0 : "Makes the turret aim in a specific direction instead of following the target. When the beam is crossed, a rocket instantly fires." TripwireAimTarget[P2](target_destination) : "Tripwire Aim Target" : : "In tripwire mode, the entity to aim at." + + model[engine](string) : "Hammer Preview" model[P2](studio) : "[H] Model" : "models/props_bts/rocket_sentry.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." _sphere_radius[!engine](integer) readonly : "" : 8192 : "How far the turret will be able to see targets. Always 8192, but this keyvalue is needed to display the preview." diff --git a/fgd/point/point/point_teleport.fgd b/fgd/point/point/point_teleport.fgd index fc5884652..dc35db74c 100644 --- a/fgd/point/point/point_teleport.fgd +++ b/fgd/point/point/point_teleport.fgd @@ -7,6 +7,7 @@ "If object is physically simulated, simulation is turned off when teleported." [ target(target_destination) : "Entity To Teleport" : : "Name of the entity that will be teleported." + model[engine](string) : "Hammer Preview" model[!engine](studio) : "[H] Model" : "models/editor/angle_helper.mdl" : "The model shown in Hammer, to use for positioning." spawnflags(flags) = [ diff --git a/fgd/point/prop/prop_button.fgd b/fgd/point/prop/prop_button.fgd index ba2cbcf45..72d817564 100644 --- a/fgd/point/prop/prop_button.fgd +++ b/fgd/point/prop/prop_button.fgd @@ -11,6 +11,7 @@ 0: "Clean" 1: "Dirty" ] + model[engine](string) : "Hammer Preview" model(studio) : "[H] Model" : "models/props/switch001.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." @resources diff --git a/fgd/point/prop/prop_floor_ball_button.fgd b/fgd/point/prop/prop_floor_ball_button.fgd index da5d31da5..63a872b1e 100644 --- a/fgd/point/prop/prop_floor_ball_button.fgd +++ b/fgd/point/prop/prop_floor_ball_button.fgd @@ -3,6 +3,7 @@ autovis(Test Elements, P2 Buttons, Floor) studioprop() = prop_floor_ball_button: "A floor button which is only activated by a Sphere-type prop_weighted_cube." [ + model[engine](string) : "Hammer Preview" model(studio) : "[H] Model" : "models/props/ball_button.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." @resources [ diff --git a/fgd/point/prop/prop_floor_cube_button.fgd b/fgd/point/prop/prop_floor_cube_button.fgd index 743d3e382..77c86ba9f 100644 --- a/fgd/point/prop/prop_floor_cube_button.fgd +++ b/fgd/point/prop/prop_floor_cube_button.fgd @@ -3,6 +3,7 @@ autovis(Test Elements, P2 Buttons, Floor) studioprop() = prop_floor_cube_button: "A floor button which is activated by a prop_weighted_cube." [ + model[engine](string) : "Hammer Preview" model(studio) : "[H]] Model" : "models/props/box_socket.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." acceptsball(boolean) : "Accepts Balls" : 0 : "Do Edgeless Safety Cubes activate this? Should almost always be No unless no balls are in the map." diff --git a/fgd/point/prop/prop_monster_box.fgd b/fgd/point/prop/prop_monster_box.fgd index a0e178045..53fff9b76 100644 --- a/fgd/point/prop/prop_monster_box.fgd +++ b/fgd/point/prop/prop_monster_box.fgd @@ -6,7 +6,7 @@ boxswitchspeed(float) : "Box Switch Speed" : 400 : "Speed to force switch to a box." allowsilentdissolve(boolean) : "Allow SilentDissolve input" : 1 : "Allow the SilentDissolve input to dissolve this FrankenCube." - model[engine](studio) : "Model" : "models/npcs/monsters/monster_a.mdl" + model[engine](string) : "Hammer Preview" : "models/npcs/monsters/monster_a.mdl" model(choices) : "[H] Pose" : "models/npcs/monsters/monster_a.mdl" : "What pose to show in Hammer." = [ "models/npcs/monsters/monster_a.mdl": "Extended" diff --git a/fgd/point/prop/prop_paint_bomb.fgd b/fgd/point/prop/prop_paint_bomb.fgd index a07c80297..8af438002 100644 --- a/fgd/point/prop/prop_paint_bomb.fgd +++ b/fgd/point/prop/prop_paint_bomb.fgd @@ -13,6 +13,8 @@ allowsilentdissolve(boolean) : "Allow SilentDissolve input" : 0 : "Allow the SilentDissolve input to dissolve this bomb." playspawnsound(boolean) : "Play Spawn Sound" : 1 : "Whether or not this bomb should play a sound on spawn (PaintBlob.Inception)." + + model[engine](string) : "" // Not actually a model it really loads. model(studio) readonly : "Model" : "models/error.mdl" : "Paint bombs need a model set to suppress a warning message. This model would be loaded already." // Inputs diff --git a/fgd/point/prop/prop_tractor_beam.fgd b/fgd/point/prop/prop_tractor_beam.fgd index d016981e7..7bf8e7662 100644 --- a/fgd/point/prop/prop_tractor_beam.fgd +++ b/fgd/point/prop/prop_tractor_beam.fgd @@ -25,7 +25,7 @@ "models/props_ingame/tractor_beam_128.mdl": "128 Model (PeTI)" ] - model[engine](studio): "Funnel model" : "models/props/tractor_beam_emitter.mdl" + model[engine](string): "Hammer Preview" : "models/props/tractor_beam_emitter.mdl" // Inputs input SetLinearForce(float) : "Set the speed of the Funnel, and therefore the direction." From ea12a7b5045da91ee13cdbaba25634c4e823c194 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 11 Mar 2023 18:37:00 -0800 Subject: [PATCH 037/243] Add postcompiler transform for control point bases --- fgd/point/team/team_control_point.fgd | 3 ++- transforms/tf2.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 transforms/tf2.py diff --git a/fgd/point/team/team_control_point.fgd b/fgd/point/team/team_control_point.fgd index edfe11c4d..e025c9d52 100644 --- a/fgd/point/team/team_control_point.fgd +++ b/fgd/point/team/team_control_point.fgd @@ -14,7 +14,7 @@ point_start_locked(boolean) : "Start locked" : 0 : "Locked means the point will not be available for capture until it is unlocked via its input." - point_printname(string) : "Print Name" : "TODO: Set Name" : "LOCALIZED name to print on the HUD." + point_printname(string) : "Print Name" : "TODO: Set Name" : "The name of this control point to print on the HUD. Can be a raw text string or a localization token." point_group(integer) : "Group Index" : 0 : "Used for grouping points together under a team_control_point_master (not using control point rounds)." point_default_owner[engine](integer) : "Default Owner" : 0 @@ -63,6 +63,7 @@ team_previouspoint_3_2(target_source) : "BLU Previous Required Point 3" : : "The name of a third previous capture point that BLU must own to be able to capture this point." model(studio) : "[H] Model" : "models/effects/cappoint_hologram.mdl" : "Model to show in Hammer" + src_propname[srctools](target_destination) : "Linked Prop" : : "Set to the name of a prop that should change its skin based on who owns the point (such as the control point base model)" // Inputs input SetOwner(integer) : "Set the owner of the point." diff --git a/transforms/tf2.py b/transforms/tf2.py new file mode 100644 index 000000000..a34375efc --- /dev/null +++ b/transforms/tf2.py @@ -0,0 +1,22 @@ +"""Team Fortress 2 specific transformations""" + +from srctools import Output + +from hammeraddons.bsp_transform import trans, Context + +# Modified from the portal 2 indicator name transform, this might be able to be slightly cleaner +@trans('TF2 Control Point Props') +def comp_control_points(ctx: Context): + """Adds key to TF2 Control Points to automatically set the skin of the base (or another prop).""" + for ent in ctx.vmf.entities: + prop_name = ent['src_propname'] + if not prop_name: + continue + + ent['src_propname'] = '' + + ent.add_out( + Output('OnCapTeam1' , prop_name, 'Skin', '1'), + Output('OnCapTeam2', prop_name, 'Skin', '2'), + Output('OnCapReset', prop_name, 'Skin', '0'), + ) \ No newline at end of file From c33a1768050cc87f1d78899919c097805ad8071e Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 11 Mar 2023 22:34:18 -0800 Subject: [PATCH 038/243] Make this helper actually TF2 only --- fgd/point/point/point_worldtext.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/point/point_worldtext.fgd b/fgd/point/point/point_worldtext.fgd index 3ccbf9dfc..61ace2454 100644 --- a/fgd/point/point/point_worldtext.fgd +++ b/fgd/point/point/point_worldtext.fgd @@ -1,7 +1,7 @@ // TF2 hammer has no worldtext() helper, so replace it with a model // FIXME: this needs a custom model, vgui_arrows is oriented the wrong way -@BaseClass studio("models/editor/axis_helper_thick.mdl") = _point_worldtext_tf2 +@BaseClass studio("models/editor/axis_helper_thick.mdl") appliesto(TF2) = _point_worldtext_tf2 [ ] From 8f5c7ca760a7dccfed7b03b03f617cb940c66b44 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 00:24:17 -0800 Subject: [PATCH 039/243] Fix TF2 team name capitalization/spelling everywhere possible --- fgd/bases/TeamNum.fgd | 2 +- fgd/brush/func/func_flag_alert.fgd | 2 +- fgd/brush/func/func_tfbot_hint.fgd | 4 +- fgd/brush/trigger/trigger_capture_area.fgd | 22 +++++----- fgd/brush/trigger/trigger_timer_door.fgd | 22 +++++----- fgd/point/bot/bot_controller.fgd | 4 +- fgd/point/bot/bot_generator.fgd | 4 +- fgd/point/bot/bot_proxy.fgd | 4 +- fgd/point/bot/bot_roster.fgd | 4 +- fgd/point/game/game_forcerespawn.fgd | 2 +- fgd/point/game/game_intro_viewpoint.fgd | 6 +-- fgd/point/game/game_round_win.fgd | 4 +- fgd/point/game/game_text_tf.fgd | 4 +- .../halloween/halloween_fortune_teller.fgd | 4 +- fgd/point/info/info_player_teamspawn.fgd | 4 +- fgd/point/info/info_powerup_spawn.fgd | 4 +- fgd/point/logic/logic_eventlistener.fgd | 2 +- fgd/point/team/team_control_point_master.fgd | 2 +- fgd/point/team/team_round_timer.fgd | 2 +- fgd/point/tf/tf_base_minigame.fgd | 12 ++--- fgd/point/tf/tf_bonus_duck_pickup.fgd | 4 +- fgd/point/tf/tf_gamerules.fgd | 8 ++-- fgd/point/tf/tf_halloween_gift_pickup.fgd | 4 +- fgd/point/tf/tf_halloween_minigame.fgd | 4 +- fgd/point/tf/tf_halloween_pickup.fgd | 4 +- fgd/point/tf/tf_logic_koth.fgd | 4 +- fgd/point/tf/tf_logic_player_destruction.fgd | 44 +++++++++---------- fgd/point/tf/tf_logic_robot_destruction.fgd | 36 +++++++-------- 28 files changed, 111 insertions(+), 111 deletions(-) diff --git a/fgd/bases/TeamNum.fgd b/fgd/bases/TeamNum.fgd index 3ee93c27f..49e947301 100644 --- a/fgd/bases/TeamNum.fgd +++ b/fgd/bases/TeamNum.fgd @@ -5,7 +5,7 @@ [ 0: "Any" 2: "RED" - 3: "BLU/ROBOTS" + 3: "BLU/Robots" 5 : "Halloween Bosses" 1 : "Spectator / Halloween Souls" ] diff --git a/fgd/brush/func/func_flag_alert.fgd b/fgd/brush/func/func_flag_alert.fgd index 2b4426980..99eda24c7 100644 --- a/fgd/brush/func/func_flag_alert.fgd +++ b/fgd/brush/func/func_flag_alert.fgd @@ -7,5 +7,5 @@ // Outputs output OnTriggeredByTeam1(void) : "Sent when RED triggers the alert." - output OnTriggeredByTeam2(void) : "Sent when BLUE triggers the alert." + output OnTriggeredByTeam2(void) : "Sent when BLU triggers the alert." ] diff --git a/fgd/brush/func/func_tfbot_hint.fgd b/fgd/brush/func/func_tfbot_hint.fgd index 47fd70114..c25c9ed30 100644 --- a/fgd/brush/func/func_tfbot_hint.fgd +++ b/fgd/brush/func/func_tfbot_hint.fgd @@ -5,8 +5,8 @@ team(choices) : "Team" : "-2" : "Which team will use this hint" = [ -2: "Everyone" - 2: "Red" - 3: "Blue" + 2: "RED" + 3: "BLU" 5 : "Halloween Bosses" ] diff --git a/fgd/brush/trigger/trigger_capture_area.fgd b/fgd/brush/trigger/trigger_capture_area.fgd index 5e58526f4..dabd9660e 100644 --- a/fgd/brush/trigger/trigger_capture_area.fgd +++ b/fgd/brush/trigger/trigger_capture_area.fgd @@ -6,22 +6,22 @@ area_time_to_cap(integer) : "Time to cap (sec)" : 5 team_cancap_2(boolean) : "Can RED Cap?" : 1 - team_cancap_3(boolean) : "Can BLUE Cap?" : 1 + team_cancap_3(boolean) : "Can BLU Cap?" : 1 team_startcap_2(integer) : "Number of RED players to start capping" : 1 - team_startcap_3(integer) : "Number of BLUE players to start capping" : 1 + team_startcap_3(integer) : "Number of BLU players to start capping" : 1 team_numcap_2(integer) : "Number of RED players to cap" : 1 - team_numcap_3(integer) : "Number of BLUE players to cap" : 1 + team_numcap_3(integer) : "Number of BLU players to cap" : 1 - team_spawn_2(integer) : "Red Spawn Adjust" : 0 : "Adjust the minimum respawn time for the red team by this amount (in seconds) when red captures this point. " + - "If the red team owns this point when blue captures it, this adjustment is reversed." - team_spawn_3(integer) : "Blue Spawn Adjust" : 0 : "Adjust the minimum respawn time for the blue team by this amount (in seconds) when blue captures this point. " + - "If the blue team owns this point when red captures it, this adjustment is reversed." + team_spawn_2(integer) : "RED Spawn Adjust" : 0 : "Adjust the minimum respawn time for RED by this amount (in seconds) when RED captures this point. " + + "If the RED owns this point when BLU captures it, this adjustment is reversed." + team_spawn_3(integer) : "BLU Spawn Adjust" : 0 : "Adjust the minimum respawn time for BLU by this amount (in seconds) when BLU captures this point. " + + "If BLU owns this point when RED captures it, this adjustment is reversed." // Inputs - input SetTeamCanCap(string) : "Set whether a specific team is allowed to capture this point. Format is: <(0/1)>. i.e. '2 0' would prevent RED from capturing this point, whereas '3 1' would allow BLUE to cap it." + input SetTeamCanCap(string) : "Set whether a specific team is allowed to capture this point. Format is: <(0/1)>. i.e. '2 0' would prevent RED from capturing this point, whereas '3 1' would allow BLU to cap it." input SetControlPoint(string) : "Assign area capture to the passed control point." input CaptureCurrentCP(string) : "If we're being capped, forces the current point to capture." input RoundSpawn(void) : "Find our control point" @@ -30,9 +30,9 @@ output OnStartTeam1(void) : "Sent when RED start capture." output OnBreakTeam1(void) : "Sent when a RED capture is broken." output OnCapTeam1(void) : "Sent when RED end capture." - output OnStartTeam2(void) : "Sent when BLUE start capture." - output OnBreakTeam2(void) : "Sent when a BLUE capture is broken." - output OnCapTeam2(void) : "Sent when BLUE end capture." + output OnStartTeam2(void) : "Sent when BLU start capture." + output OnBreakTeam2(void) : "Sent when a BLU capture is broken." + output OnCapTeam2(void) : "Sent when BLU end capture." output OnStartCap(void) : "Sent when either team starts capture." output OnBreakCap(void) : "Sent when either team break capture." output OnEndCap(void) : "Sent when either team end capture." diff --git a/fgd/brush/trigger/trigger_timer_door.fgd b/fgd/brush/trigger/trigger_timer_door.fgd index beeb7fd22..8c1a4d146 100644 --- a/fgd/brush/trigger/trigger_timer_door.fgd +++ b/fgd/brush/trigger/trigger_timer_door.fgd @@ -5,20 +5,20 @@ area_cap_point(target_source) : "Control point" : : "Name of the control point this area is linked to." team_cancap_2(boolean) : "Can RED Cap?" : 1 - team_cancap_3(boolean) : "Can BLUE Cap?" : 1 + team_cancap_3(boolean) : "Can BLU Cap?" : 1 team_startcap_2(integer) : "Number of RED players to start capping" : 1 - team_startcap_3(integer) : "Number of BLUE players to start capping" : 1 + team_startcap_3(integer) : "Number of BLU players to start capping" : 1 team_numcap_2(integer) : "Number of RED players to cap" : 1 - team_numcap_3(integer) : "Number of BLUE players to cap" : 1 - team_spawn_2(integer) : "Red Spawn Adjust" : 0 : "Adjust the minimum respawn time for the red team by this amount (in seconds) " + - "when red captures this point. If the red team owns this point when blue captures it, this adjustment is reversed." - team_spawn_3(integer) : "Blue Spawn Adjust" : 0 : "Adjust the minimum respawn time for the blue team by this amount (in seconds) " + - "when blue captures this point. If the blue team owns this point when red captures it, this adjustment is reversed." + team_numcap_3(integer) : "Number of BLU players to cap" : 1 + team_spawn_2(integer) : "RED Spawn Adjust" : 0 : "Adjust the minimum respawn time for RED by this amount (in seconds) " + + "when RED captures this point. If RED owns this point when BLU captures it, this adjustment is reversed." + team_spawn_3(integer) : "BLU Spawn Adjust" : 0 : "Adjust the minimum respawn time for BLU by this amount (in seconds) " + + "when BLU captures this point. If BLU owns this point when RED captures it, this adjustment is reversed." area_time_to_cap(integer) : "Time to cap (sec)" : 5 // Inputs - input SetTeamCanCap(string) : "Set whether a specific team is allowed to capture this point. Format is: <(0/1)>. i.e. '2 0' would prevent RED from capturing this point, whereas '3 1' would allow BLUE to cap it." + input SetTeamCanCap(string) : "Set whether a specific team is allowed to capture this point. Format is: <(0/1)>. i.e. '2 0' would prevent RED from capturing this point, whereas '3 1' would allow BLU to cap it." input SetControlPoint(string) : "Assign area capture to the passed control point." input CaptureCurrentCP(string) : "If we're being capped, forces the current point to capture." input RoundSpawn(void) : "Find our control point" @@ -28,9 +28,9 @@ output OnBreakTeam1(void) : "Sent when a RED capture is broken." output OnCapTeam1(void) : "Sent when RED end capture." - output OnStartTeam2(void) : "Sent when BLUE start capture." - output OnBreakTeam2(void) : "Sent when a BLUE capture is broken." - output OnCapTeam2(void) : "Sent when BLUE end capture." + output OnStartTeam2(void) : "Sent when BLU start capture." + output OnBreakTeam2(void) : "Sent when a BLU capture is broken." + output OnCapTeam2(void) : "Sent when BLU end capture." output OnStartCap(void) : "Sent when either team starts capture." output OnBreakCap(void) : "Sent when either team break capture." diff --git a/fgd/point/bot/bot_controller.fgd b/fgd/point/bot/bot_controller.fgd index d740c432a..014fb534d 100644 --- a/fgd/point/bot/bot_controller.fgd +++ b/fgd/point/bot/bot_controller.fgd @@ -7,8 +7,8 @@ teamnum[engine](integer) : "Team" : 2 teamnum(choices) : "Team" : 2 : "Team" = [ - 2: "Red" - 3: "Blue" + 2: "RED" + 3: "BLU" ] bot_class[engine](integer) : "Class" : 0 diff --git a/fgd/point/bot/bot_generator.fgd b/fgd/point/bot/bot_generator.fgd index 08d59e9a9..5a7622ef8 100644 --- a/fgd/point/bot/bot_generator.fgd +++ b/fgd/point/bot/bot_generator.fgd @@ -8,8 +8,8 @@ team(choices) : "Team" : "auto" : "Team" = [ "auto": "Any" - "red": "Red" - "blue": "Blue" + "red": "RED" + "blue": "BLU" ] class[engine](string) : "Class" : "auto" diff --git a/fgd/point/bot/bot_proxy.fgd b/fgd/point/bot/bot_proxy.fgd index 1eaccccb7..244607140 100644 --- a/fgd/point/bot/bot_proxy.fgd +++ b/fgd/point/bot/bot_proxy.fgd @@ -11,8 +11,8 @@ team(choices) : "Team" : "auto" : "Team" = [ "auto": "Any" - "red": "Red" - "blue": "Blue" + "red": "RED" + "blue": "BLU" ] class[engine](string) : "Class" : "auto" diff --git a/fgd/point/bot/bot_roster.fgd b/fgd/point/bot/bot_roster.fgd index 260b60eea..aaffd4700 100644 --- a/fgd/point/bot/bot_roster.fgd +++ b/fgd/point/bot/bot_roster.fgd @@ -9,8 +9,8 @@ team(choices) : "Team" : "auto" : "Team" = [ "auto": "Any" - "red": "Red" - "blue": "Blue" + "red": "RED" + "blue": "BLU" ] allowclasschanges(boolean) : "Allow Class Changes" : "1" : "Allow TFBots to choose a new class when they respawn." diff --git a/fgd/point/game/game_forcerespawn.fgd b/fgd/point/game/game_forcerespawn.fgd index 0e6d56dbb..02b99beb2 100644 --- a/fgd/point/game/game_forcerespawn.fgd +++ b/fgd/point/game/game_forcerespawn.fgd @@ -6,7 +6,7 @@ // Inputs input ForceRespawn(void) : "Force players to respawn and remove buildings, grenades, etc., from the world.." input ForceRespawnSwitchTeams(void) : "Switch all players to the opposite team, and then respawn all players (and remove buildings, grenades, etc., from the world)." - input ForceTeamRespawn(integer) : "Force players on a specific team to respawn. 2 for Red, 3 for Blue. This does NOT remove buildings, grenades, etc., from the world..." + input ForceTeamRespawn(integer) : "Force players on a specific team to respawn. 2 for RED, 3 for BLU. This does NOT remove buildings, grenades, etc., from the world..." // Outputs output OnForceRespawn(void) : "Sent when the entity respawns the players." diff --git a/fgd/point/game/game_intro_viewpoint.fgd b/fgd/point/game/game_intro_viewpoint.fgd index 5aed88de7..01b5a038d 100644 --- a/fgd/point/game/game_intro_viewpoint.fgd +++ b/fgd/point/game/game_intro_viewpoint.fgd @@ -6,9 +6,9 @@ teamnum[engine](integer) : "Team" : 0 teamnum(choices) : "Team" : 0 : "Team" = [ - 0: "None (Shared)" - 2: "Red" - 3: "Blue" + 0: "Both" + 2: "RED" + 3: "BLU" ] step_number(integer) : "Step Number. Intro starts at 1." : 1 diff --git a/fgd/point/game/game_round_win.fgd b/fgd/point/game/game_round_win.fgd index a0874fce8..4500b3a33 100644 --- a/fgd/point/game/game_round_win.fgd +++ b/fgd/point/game/game_round_win.fgd @@ -8,8 +8,8 @@ teamnum[TF2](choices) : "Team" : 0 : "Team" = [ 0: "None (Sudden Death)" - 2: "Red" - 3: "Blue" + 2: "RED" + 3: "BLU" ] team[engine](integer) : "Team" : 0 diff --git a/fgd/point/game/game_text_tf.fgd b/fgd/point/game/game_text_tf.fgd index 796802f4c..07f6a1dbe 100644 --- a/fgd/point/game/game_text_tf.fgd +++ b/fgd/point/game/game_text_tf.fgd @@ -412,8 +412,8 @@ display_to_team(choices) : "Audience" : 0 = [ 0: "Everyone" - 2: "Red Team Only" - 3: "Blue Team Only" + 2: "RED Team Only" + 3: "BLU Team Only" ] background[engine](integer) : "Background Panel Color" : 0 diff --git a/fgd/point/halloween/halloween_fortune_teller.fgd b/fgd/point/halloween/halloween_fortune_teller.fgd index 4b7553a03..c1b34efcd 100644 --- a/fgd/point/halloween/halloween_fortune_teller.fgd +++ b/fgd/point/halloween/halloween_fortune_teller.fgd @@ -2,8 +2,8 @@ appliesto(TF2) studio("models/bots/merasmus/merasmas_misfortune_teller.mdl") = halloween_fortune_teller: "Halloween Fortune Teller trigger" [ - red_teleport(string) : "Red Teleport Entity" : : "Where to teleport the red team" - blue_teleport(string) : "Blue Teleport Entity" : : "Where to teleport the blue team" + red_teleport(string) : "RED Teleport Entity" : : "Where to teleport the RED team" + blue_teleport(string) : "BLU Teleport Entity" : : "Where to teleport the BLU team" // Inputs input EnableFortuneTelling(void) : "Enable fortune telling" diff --git a/fgd/point/info/info_player_teamspawn.fgd b/fgd/point/info/info_player_teamspawn.fgd index aff433f5a..8c642463e 100644 --- a/fgd/point/info/info_player_teamspawn.fgd +++ b/fgd/point/info/info_player_teamspawn.fgd @@ -39,8 +39,8 @@ 256: "Engineer" : 1 ] - round_bluespawn(target_destination) : "Blue spawn for round" : : "Blue spawn point when the associated round is being played." - round_redspawn(target_destination) : "Red spawn for round" : : "Red spawn point when the associated round is being played." + round_bluespawn(target_destination) : "BLU spawn for round" : : "BLU spawn point when the associated round is being played." + round_redspawn(target_destination) : "RED spawn for round" : : "RED spawn point when the associated round is being played." skin[!engine](choices) : "[H] Team" : 0 : "Team color to display in Hammer" = [ diff --git a/fgd/point/info/info_powerup_spawn.fgd b/fgd/point/info/info_powerup_spawn.fgd index 30d50f3d4..93912c778 100644 --- a/fgd/point/info/info_powerup_spawn.fgd +++ b/fgd/point/info/info_powerup_spawn.fgd @@ -11,7 +11,7 @@ team(choices) : "Owner Team" : -2 : "Teams can own spawn points. When Powerups are dropped, they resposition themselves after timing out. They will try to pick a spawn point based on what team designation they had when they repositioned. Set to Everyone to have no team bias in spawn positioning" = [ -2: "Everyone" - 2: "Red" - 3: "Blue" + 2: "RED" + 3: "BLU" ] ] diff --git a/fgd/point/logic/logic_eventlistener.fgd b/fgd/point/logic/logic_eventlistener.fgd index cff710266..927e9b879 100644 --- a/fgd/point/logic/logic_eventlistener.fgd +++ b/fgd/point/logic/logic_eventlistener.fgd @@ -27,7 +27,7 @@ [ 0: "Any" 2: "RED" - 3: "BLU/ROBOTS" + 3: "BLU/Robots" 5 : "Halloween Bosses" 1 : "Spectator / Halloween Souls" ] diff --git a/fgd/point/team/team_control_point_master.fgd b/fgd/point/team/team_control_point_master.fgd index a7b54fb37..a7725ff75 100644 --- a/fgd/point/team/team_control_point_master.fgd +++ b/fgd/point/team/team_control_point_master.fgd @@ -4,7 +4,7 @@ appliesto(TF2) = team_control_point_master: "Control Point Master" [ team_base_icon_2(material) : "Material for the RED Base icon" : "sprites/obj_icons/icon_base_red" - team_base_icon_3(material) : "Material for the BLUE Base icon" : "sprites/obj_icons/icon_base_blu" + team_base_icon_3(material) : "Material for the BLU Base icon" : "sprites/obj_icons/icon_base_blu" caplayout(string) : "Cap Layout" : : "A string that tells the HUD how to lay out the cap points. It should be a string with indexes of cap points seperated by commas to denote a new line. So <2,0 1> would create a pyramid, with cap point 2 on the top and cap points 0 & 1 on the bottom." custom_position_x(float) : "Custom cap position X" : -1 : "Set the cap layout custom X position [0,1]" custom_position_y(float) : "Custom cap position Y" : -1 : "Set the cap layout custom Y position [0,1]" diff --git a/fgd/point/team/team_round_timer.fgd b/fgd/point/team/team_round_timer.fgd index 4d37d2b65..18b519079 100644 --- a/fgd/point/team/team_round_timer.fgd +++ b/fgd/point/team/team_round_timer.fgd @@ -29,7 +29,7 @@ input Resume(void) : "Resume the timer." input SetTime(integer) : "Set the timer to this value (in seconds)." input AddTime(integer) : "Add time to the timer (in seconds). Added time cannot excede the max timer length." - input AddTeamTime(string) : "Input takes a string (space delimited) with the team number and the time to be added (in seconds) because of the team (2 for red, 3 for blue, and 0 for no team...but you could just use AddTime for that). Added time cannot excede the max timer length. Example: 2 600 (adds 10 minutes because of team red)" + input AddTeamTime(string) : "Input takes a string (space delimited) with the team number and the time to be added (in seconds) because of the team (2 for RED, 3 for BLU, and 0 for no team...but you could just use AddTime for that). Added time cannot excede the max timer length. Example: 2 600 (adds 10 minutes because of team red)" input Restart(void) : "Restart the timer." input ShowInHUD(integer) : "Show this timer in the HUD (0 no, 1 Yes)." input SetMaxTime(integer) : "Set the max timer length to this value (in seconds). The timer's time will never excede this value." diff --git a/fgd/point/tf/tf_base_minigame.fgd b/fgd/point/tf/tf_base_minigame.fgd index 0199d9531..2959ddd90 100644 --- a/fgd/point/tf/tf_base_minigame.fgd +++ b/fgd/point/tf/tf_base_minigame.fgd @@ -6,8 +6,8 @@ iconsprite("editor/ficool2/tf_base_minigame") = tf_base_minigame: "Base minigame" [ - redspawn(target_source) : "Red Spawn Name" : : "The name of the spawnpoints for RED for this minigame" - bluespawn(target_source) : "Blue Spawn Name" : : "The name of the spawnpoints for BLU for this minigame" + redspawn(target_source) : "RED Spawn Name" : : "The name of the spawnpoints for RED for this minigame" + bluespawn(target_source) : "BLU Spawn Name" : : "The name of the spawnpoints for BLU for this minigame" inrandompool(boolean) : "Put in Random Pool" : 1 : "If Yes, allowed to be chosen when told to go to a random minigame" @@ -27,16 +27,16 @@ // Inputs input ScoreTeamRed(integer) : "Give points to team RED" - input ScoreTeamBlue(integer) : "Give points to team BLUE" + input ScoreTeamBlue(integer) : "Give points to team BLU" input ReturnFromMinigame(void) : "Force players to return from the current minigame." input ChangeHudResFile(string) : "Change the HUD resource file." // Outputs output OnReturnFromMinigame(void) : "Sent when players return from this minigame." output OnTeleportToMinigame(void) : "Sent when players arrive in this minigame." - output OnRedHitMaxScore(void) : "Sent RED hits the max score for this minigame." - output OnBlueHitMaxScore(void) : "Sent BLUE hits the max score for this minigame." + output OnRedHitMaxScore(void) : "Sent when RED hits the max score for this minigame." + output OnBlueHitMaxScore(void) : "Sent when BLU hits the max score for this minigame." output OnAllRedDead(void) : "Sent when the entire RED team is dead." - output OnAllBlueDead(void) : "Send when the entire BLUE team is dead." + output OnAllBlueDead(void) : "Send when the entire BLU team is dead." output OnSuddenDeathStart(void) : "Sent when sudden death starts." ] diff --git a/fgd/point/tf/tf_bonus_duck_pickup.fgd b/fgd/point/tf/tf_bonus_duck_pickup.fgd index 8f09ad9e9..862f57fea 100644 --- a/fgd/point/tf/tf_bonus_duck_pickup.fgd +++ b/fgd/point/tf/tf_bonus_duck_pickup.fgd @@ -6,6 +6,6 @@ pickup_sound(sound) : "Sound Effect" : "" : "The sound script entry that is played when the item is picked up." pickup_particle(string) : "Particle Effect" : "" : "The particle effect that is displayed when the item is picked up." - output OnRedPickup(void) : "Sent when Red picks-up the item." - output OnBluePickup(void) : "Sent when Blue picks-up the item." + output OnRedPickup(void) : "Sent when RED picks-up the item." + output OnBluePickup(void) : "Sent when BLU picks-up the item." ] diff --git a/fgd/point/tf/tf_gamerules.fgd b/fgd/point/tf/tf_gamerules.fgd index d7cff9684..fab190574 100644 --- a/fgd/point/tf/tf_gamerules.fgd +++ b/fgd/point/tf/tf_gamerules.fgd @@ -44,11 +44,11 @@ // Outputs output OnWonByTeam1(void) : "Sent when RED wins the round." - output OnWonByTeam2(void) : "Sent when BLUE wins the round." + output OnWonByTeam2(void) : "Sent when BLU wins the round." output Team1PlayersChanged(integer) : "Sent when RED team player count changes, due to a player joining or leaving the team." - output Team2PlayersChanged(integer) : "Sent when BLUE team player count changes, due to a player joining or leaving the team." - output OnPowerupImbalanceTeam1(void) : "Sent when BLUE team has a powerup kill advantage over the RED team. Initiate RED team assist measures now." - output OnPowerupImbalanceTeam2(void) : "Sent when RED team has a powerup kill advantage over the BLUE team. Initiate BLUE team assist measures now." + output Team2PlayersChanged(integer) : "Sent when BLU team player count changes, due to a player joining or leaving the team." + output OnPowerupImbalanceTeam1(void) : "Sent when BLU team has a powerup kill advantage over the RED team. Initiate RED team assist measures now." + output OnPowerupImbalanceTeam2(void) : "Sent when RED team has a powerup kill advantage over the BLU team. Initiate BLU team assist measures now." output OnPowerupImbalanceMeasuresOver(void) : "Sent when powerup imbalance measures should be stopped" output OnStateEnterBetweenRounds(void) : "Fired when entering the between-rounds state (MvM and Competitive)." output OnStateEnterPreRound(void) : "Fired when entering the pre-round state (just before round running)." diff --git a/fgd/point/tf/tf_halloween_gift_pickup.fgd b/fgd/point/tf/tf_halloween_gift_pickup.fgd index b602684ea..3096ccb84 100644 --- a/fgd/point/tf/tf_halloween_gift_pickup.fgd +++ b/fgd/point/tf/tf_halloween_gift_pickup.fgd @@ -8,6 +8,6 @@ pickup_particle(string) : "Particle Effect" : : "The particle effect that is displayed when the item is picked up." // Outputs - output OnRedPickup(void) : "Sent when Red picks-up the item." - output OnBluePickup(void) : "Sent when Blue picks-up the item." + output OnRedPickup(void) : "Sent when RED picks-up the item." + output OnBluePickup(void) : "Sent when BLU picks-up the item." ] diff --git a/fgd/point/tf/tf_halloween_minigame.fgd b/fgd/point/tf/tf_halloween_minigame.fgd index 68c184754..b1cfb2a0e 100644 --- a/fgd/point/tf/tf_halloween_minigame.fgd +++ b/fgd/point/tf/tf_halloween_minigame.fgd @@ -13,9 +13,9 @@ // Inputs input KartWinAnimationRed(void) : "Play win animation for all players in kart on team RED" - input KartWinAnimationBlue(void) : "Play win animation for all players in kart on team BLUE" + input KartWinAnimationBlue(void) : "Play win animation for all players in kart on team BLU" input KartLoseAnimationRed(void) : "Play lose animation for all players in kart on team RED" - input KartLoseAnimationBlue(void) : "Play lose animation for all players in kart on team BLUE" + input KartLoseAnimationBlue(void) : "Play lose animation for all players in kart on team BLU" input EnableSpawnBoss(string) : "Spawn Halloween boss HHH at specified target entity" input DisableSpawnBoss(void) : "Stop spawning Halloween boss" ] diff --git a/fgd/point/tf/tf_halloween_pickup.fgd b/fgd/point/tf/tf_halloween_pickup.fgd index c4c848fda..bb878d37e 100644 --- a/fgd/point/tf/tf_halloween_pickup.fgd +++ b/fgd/point/tf/tf_halloween_pickup.fgd @@ -9,6 +9,6 @@ pickup_particle(string) : "Particle Effect" : : "The particle effect that is displayed when the item is picked up." // Outputs - output OnRedPickup(void) : "Sent when Red picks-up the item." - output OnBluePickup(void) : "Sent when Blue picks-up the item." + output OnRedPickup(void) : "Sent when RED picks-up the item." + output OnBluePickup(void) : "Sent when BLU picks-up the item." ] diff --git a/fgd/point/tf/tf_logic_koth.fgd b/fgd/point/tf/tf_logic_koth.fgd index e0ab604ae..ebbf1c67b 100644 --- a/fgd/point/tf/tf_logic_koth.fgd +++ b/fgd/point/tf/tf_logic_koth.fgd @@ -7,9 +7,9 @@ // Inputs input SetRedTimer(integer) : "Set the time remaining for the RED timer." - input SetBlueTimer(integer) : "Set the time remaining for the BLUE timer." + input SetBlueTimer(integer) : "Set the time remaining for the BLU timer." input AddRedTimer(integer) : "Add time to the RED timer." - input AddBlueTimer(integer) : "Add time to the BLUE timer." + input AddBlueTimer(integer) : "Add time to the BLU timer." input RoundActivate(void) : "Find control points and its master." input RoundSpawn(void) : "Create new team_round_timer entities for both team." diff --git a/fgd/point/tf/tf_logic_player_destruction.fgd b/fgd/point/tf/tf_logic_player_destruction.fgd index 9bf33c1f4..02bfca896 100644 --- a/fgd/point/tf/tf_logic_player_destruction.fgd +++ b/fgd/point/tf/tf_logic_player_destruction.fgd @@ -9,8 +9,8 @@ loser_respawn_bonus_per_bot(float) : "Loser Respawn Bonus Per Bot (percentage)" : 0 : "How much faster the losing team will respawn per bot difference." score_interval(float) : "Robot Scoring Interval (in seconds)" : 1 : "How often a robot should score a point while hacking." - red_respawn_time(float) : "Red Respawn Time (in seconds)" : 10 : "Respawn times for Red" - blue_respawn_time(float) : "Blue Respawn Time (in seconds)" : 10 : "Respawn times for Blue" + red_respawn_time(float) : "RED Respawn Time (in seconds)" : 10 : "Respawn times for RED" + blue_respawn_time(float) : "BLU Respawn Time (in seconds)" : 10 : "Respawn times for BLU" min_points(integer) : "Min Points" : 10 : "The minimum points to win" max_points(integer) : "Max Points" : 200 : "The number of points a team must get to unlock their win condition." @@ -22,8 +22,8 @@ heal_distance(integer) : "Heal Distance" : 450 : "The distance for the team leader's ability to heal teammates." // Inputs - input ScoreRedPoints(void) : "Score points for red." - input ScoreBluePoints(void) : "Score points for blue." + input ScoreRedPoints(void) : "Score points for RED." + input ScoreBluePoints(void) : "Score points for BLU." input EnableMaxScoreUpdating(void) : "Allow the max score to update based on player count." input DisableMaxScoreUpdating(void) : "Disallow the max score to update based on player count." input SetCountdownTimer(integer) : "Set the countdown time and start the timer." @@ -32,24 +32,24 @@ input SetPointsOnPlayerDeath(integer) : "Set number of points per flag dropped upon player death." // Outputs - output OnBlueHitMaxPoints(void) : "Sent when Blue hits the max points." - output OnRedHitMaxPoints(void) : "Sent when Red hits the max points." - output OnBlueLeaveMaxPoints(void) : "Sent when blue goes from max points to fewer." - output OnRedLeaveMaxPoints(void) : "Sent when red goes from max points to fewer." - output OnBlueHitZeroPoints(void) : "Sent when Blue hits 0 points." - output OnRedHitZeroPoints(void) : "Sent when Red hits 0 points" - output OnBlueHasPoints(void) : "Sent when Blue goes from 0 to any points" - output OnRedHasPoints(void) : "Sent when Red goes from 0 to any points" - - output OnRedFinalePeriodEnd(void) : "Sent when the red finale period ends." - output OnBlueFinalePeriodEnd(void) : "Sent when the blue finale period ends." - - output OnRedFirstFlagStolen(void) : "Sent when red's first flag gets stolen." - output OnRedFlagStolen(void) : "Sent when a flag gets stolen from red." - output OnRedLastFlagReturned(void) : "Sent when red's last stolen flag gets returned." - output OnBlueFirstFlagStolen(void) : "Sent when blue's first flag gets stolen." - output OnBlueFlagStolen(void) : "Sent when a flag gets stolen from blue." - output OnBlueLastFlagReturned(void) : "Sent when blue's last stolen flag gets returned." + output OnBlueHitMaxPoints(void) : "Sent when BLU hits the max points." + output OnRedHitMaxPoints(void) : "Sent when RED hits the max points." + output OnBlueLeaveMaxPoints(void) : "Sent when BLU goes from max points to fewer." + output OnRedLeaveMaxPoints(void) : "Sent when RED goes from max points to fewer." + output OnBlueHitZeroPoints(void) : "Sent when BLU hits 0 points." + output OnRedHitZeroPoints(void) : "Sent when RED hits 0 points" + output OnBlueHasPoints(void) : "Sent when BLU goes from 0 to any points" + output OnRedHasPoints(void) : "Sent when RED goes from 0 to any points" + + output OnRedFinalePeriodEnd(void) : "Sent when the RED finale period ends." + output OnBlueFinalePeriodEnd(void) : "Sent when the BLU finale period ends." + + output OnRedFirstFlagStolen(void) : "Sent when RED's first flag gets stolen." + output OnRedFlagStolen(void) : "Sent when a flag gets stolen from RED." + output OnRedLastFlagReturned(void) : "Sent when RED's last stolen flag gets returned." + output OnBlueFirstFlagStolen(void) : "Sent when BLU's first flag gets stolen." + output OnBlueFlagStolen(void) : "Sent when a flag gets stolen from BLU." + output OnBlueLastFlagReturned(void) : "Sent when BLU's last stolen flag gets returned." output OnRedScoreChanged(float) : "Send when score changes, and is a value representing total progress from [0..1]." output OnBlueScoreChanged(float) : "Send when score changes, and is a value representing total progress from [0..1]." diff --git a/fgd/point/tf/tf_logic_robot_destruction.fgd b/fgd/point/tf/tf_logic_robot_destruction.fgd index dafd13086..6fe6d6d69 100644 --- a/fgd/point/tf/tf_logic_robot_destruction.fgd +++ b/fgd/point/tf/tf_logic_robot_destruction.fgd @@ -7,32 +7,32 @@ score_interval(float) : "Robot Scoring Interval (in seconds)" : 1 : "How often a robot should score a point while hacking." loser_respawn_bonus_per_bot(float) : "Loser Respawn Bonus Per Bot (percentage)" : 0 : "How much faster the losing team will respawn per bot difference." - red_respawn_time(float) : "Red Respawn Time (in seconds)" : 10 : "Respawn times for Red" - blue_respawn_time(float) : "Blue Respawn Time (in seconds)" : 10 : "Respawn times for Blue" + red_respawn_time(float) : "RED Respawn Time (in seconds)" : 10 : "Respawn times for Red" + blue_respawn_time(float) : "BLU Respawn Time (in seconds)" : 10 : "Respawn times for BLU" max_points(integer) : "Max Points" : 200 : "The number of points a team must get to unlock their win condition." finale_length(float) : "Finale Length" : 30 : "The amount of time from after max score is reached a team will win." res_file(string) : "HUD Res File" : "resource/UI/HudObjectiveRobotDestruction.res" : "What res file to use for the HUD" // Outputs - output OnBlueHitMaxPoints(void) : "Sent when Blue hits the max points." - output OnRedHitMaxPoints(void) : "Sent when Red hits the max points." - output OnBlueLeaveMaxPoints(void) : "Sent when blue goes from max points to fewer." - output OnRedLeaveMaxPoints(void) : "Sent when red goes from max points to fewer." - output OnBlueHitZeroPoints(void) : "Sent when Blue hits 0 points." - output OnRedHitZeroPoints(void) : "Sent when Red hits 0 points" - output OnBlueHasPoints(void) : "Sent when Blue goes from 0 to any points" - output OnRedHasPoints(void) : "Sent when Red goes from 0 to any points" + output OnBlueHitMaxPoints(void) : "Sent when BLU hits the max points." + output OnRedHitMaxPoints(void) : "Sent when RED hits the max points." + output OnBlueLeaveMaxPoints(void) : "Sent when BLU goes from max points to fewer." + output OnRedLeaveMaxPoints(void) : "Sent when RED goes from max points to fewer." + output OnBlueHitZeroPoints(void) : "Sent when BLU hits 0 points." + output OnRedHitZeroPoints(void) : "Sent when RED hits 0 points" + output OnBlueHasPoints(void) : "Sent when BLU goes from 0 to any points" + output OnRedHasPoints(void) : "Sent when RED goes from 0 to any points" - output OnRedFinalePeriodEnd(void) : "Sent when the red finale period ends." - output OnBlueFinalePeriodEnd(void) : "Sent when the blue finale period ends." + output OnRedFinalePeriodEnd(void) : "Sent when the RED finale period ends." + output OnBlueFinalePeriodEnd(void) : "Sent when the BLU finale period ends." - output OnRedFirstFlagStolen(void) : "Sent when red's first flag gets stolen." - output OnRedFlagStolen(void) : "Sent when a flag gets stolen from red." - output OnRedLastFlagReturned(void) : "Sent when red's last stolen flag gets returned." - output OnBlueFirstFlagStolen(void) : "Sent when blue's first flag gets stolen." - output OnBlueFlagStolen(void) : "Sent when a flag gets stolen from blue." - output OnBlueLastFlagReturned(void) : "Sent when blue's last stolen flag gets returned." + output OnRedFirstFlagStolen(void) : "Sent when RED's first flag gets stolen." + output OnRedFlagStolen(void) : "Sent when a flag gets stolen from RED." + output OnRedLastFlagReturned(void) : "Sent when RED's last stolen flag gets returned." + output OnBlueFirstFlagStolen(void) : "Sent when BLU's first flag gets stolen." + output OnBlueFlagStolen(void) : "Sent when a flag gets stolen from BLU." + output OnBlueLastFlagReturned(void) : "Sent when BLU's last stolen flag gets returned." input RoundActivate(void) : "Reactivate." ] From 1e6c825d25e1b9246157a7db8aed828917aa38cf Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 00:37:48 -0800 Subject: [PATCH 040/243] Use base class for TF2 item pickups --- fgd/bases/TFPickup.fgd | 12 ++++++++++++ fgd/point/tf/tf_bonus_duck_pickup.fgd | 9 ++------- fgd/point/tf/tf_halloween_gift_pickup.fgd | 13 +++---------- fgd/point/tf/tf_halloween_pickup.fgd | 14 +++----------- 4 files changed, 20 insertions(+), 28 deletions(-) create mode 100644 fgd/bases/TFPickup.fgd diff --git a/fgd/bases/TFPickup.fgd b/fgd/bases/TFPickup.fgd new file mode 100644 index 000000000..0232e6ad2 --- /dev/null +++ b/fgd/bases/TFPickup.fgd @@ -0,0 +1,12 @@ +@BaseClass base(Item) + appliesto(TF2) + sphere(fademindist) + sphere(fademaxdist) = TFPickup: "Base class for TF2 item pickups" + [ + pickup_sound(string) : "Sound Effect" : : "The sound script entry that is played when the item is picked up." + pickup_particle(string) : "Particle Effect" : : "The particle effect that is displayed when the item is picked up." + + // Outputs + output OnRedPickup(void) : "Sent when RED picks-up the item." + output OnBluePickup(void) : "Sent when BLU picks-up the item." + ] \ No newline at end of file diff --git a/fgd/point/tf/tf_bonus_duck_pickup.fgd b/fgd/point/tf/tf_bonus_duck_pickup.fgd index 862f57fea..fe0838716 100644 --- a/fgd/point/tf/tf_bonus_duck_pickup.fgd +++ b/fgd/point/tf/tf_bonus_duck_pickup.fgd @@ -1,11 +1,6 @@ -@PointClass base(Item) +@PointClass base(TFPickup) appliesto(TF2) - studio("models/workshop/player/items/pyro/eotl_ducky/eotl_bonus_duck.mdl") + studioprop("models/workshop/player/items/pyro/eotl_ducky/eotl_bonus_duck.mdl") = tf_bonus_duck_pickup : "EOTL Bonus Ducks" [ - pickup_sound(sound) : "Sound Effect" : "" : "The sound script entry that is played when the item is picked up." - pickup_particle(string) : "Particle Effect" : "" : "The particle effect that is displayed when the item is picked up." - - output OnRedPickup(void) : "Sent when RED picks-up the item." - output OnBluePickup(void) : "Sent when BLU picks-up the item." ] diff --git a/fgd/point/tf/tf_halloween_gift_pickup.fgd b/fgd/point/tf/tf_halloween_gift_pickup.fgd index 3096ccb84..267b4a095 100644 --- a/fgd/point/tf/tf_halloween_gift_pickup.fgd +++ b/fgd/point/tf/tf_halloween_gift_pickup.fgd @@ -1,13 +1,6 @@ -@PointClass base(Item) +@PointClass base(TFPickup) appliesto(TF2) - studio("models/props_halloween/halloween_gift.mdl") - sphere(fademindist) - sphere(fademaxdist) = tf_halloween_gift_pickup: "Halloween pickup" + studioprop("models/props_halloween/halloween_gift.mdl") += tf_halloween_gift_pickup: "Halloween pickup" [ - pickup_sound(string) : "Sound Effect" : : "The sound script entry that is played when the item is picked up." - pickup_particle(string) : "Particle Effect" : : "The particle effect that is displayed when the item is picked up." - - // Outputs - output OnRedPickup(void) : "Sent when RED picks-up the item." - output OnBluePickup(void) : "Sent when BLU picks-up the item." ] diff --git a/fgd/point/tf/tf_halloween_pickup.fgd b/fgd/point/tf/tf_halloween_pickup.fgd index bb878d37e..4b4059ac7 100644 --- a/fgd/point/tf/tf_halloween_pickup.fgd +++ b/fgd/point/tf/tf_halloween_pickup.fgd @@ -1,14 +1,6 @@ - -@PointClass base(Item) +@PointClass base(TFPickup) appliesto(TF2) - studio("models/items/target_duck.mdl") - sphere(fademindist) - sphere(fademaxdist) = tf_halloween_pickup: "Halloween pickup" + studioprop("models/items/target_duck.mdl") += tf_halloween_pickup: "Halloween pickup" [ - pickup_sound(string) : "Sound Effect" : : "The sound script entry that is played when the item is picked up." - pickup_particle(string) : "Particle Effect" : : "The particle effect that is displayed when the item is picked up." - - // Outputs - output OnRedPickup(void) : "Sent when RED picks-up the item." - output OnBluePickup(void) : "Sent when BLU picks-up the item." ] From 56f679580a1552c6b63bd8d824c599964afa6b4a Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 00:40:32 -0800 Subject: [PATCH 041/243] Don't strip worldtext() helpers in older games This allows the helper to appear when using Hammer++ --- src/hammeraddons/unify_fgd.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index 58638de9a..286ea0354 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -213,17 +213,6 @@ def _polyfill_scripts(fgd: FGD) -> None: inp.type = ValueTypes.STRING -@_polyfill('until_csgo') -def _polyfill_worldtext(fgd: FGD): - """Strip worldtext(), since this is not available.""" - for ent in fgd: - ent.helpers[:] = [ - helper - for helper in ent.helpers - if not isinstance(helper, HelperWorldText) - ] - - @_polyfill() def _polyfill_ext_valuetypes(fgd: FGD) -> None: # Convert extension types to their real versions. From 938461d5648d2fc5f7432db5bb509a5e95df2fc4 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 00:46:08 -0800 Subject: [PATCH 042/243] Fix some of the game titles slightly --- src/hammeraddons/unify_fgd.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index 286ea0354..4e282c719 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -24,8 +24,8 @@ # If 'until_l4d' etc is used in FGD, only games before include it. GAMES_CHRONO: List[Tuple[str, str]] = [ ('HL2', 'Half-Life 2'), - ('EP1', 'Half-Life 2 Episode 1'), - ('EP2', 'Half-Life 2 Episode 2'), + ('EP1', 'Half-Life 2: Episode One'), + ('EP2', 'Half-Life 2: Episode Two'), ('TF2', 'Team Fortress 2'), ('P1', 'Portal'), @@ -33,7 +33,7 @@ ('L4D2', 'Left 4 Dead 2'), ('ASW', 'Alien Swarm'), ('P2', 'Portal 2'), - ('CSGO', 'Counter-Strike Global Offensive'), + ('CSGO', 'Counter-Strike: Global Offensive'), ('SFM', 'Source Filmmaker'), ('DOTA2', 'Dota 2'), @@ -48,9 +48,9 @@ ], 'EP2': [ ('MESA', 'Black Mesa'), - ('GMOD', "Gary's Mod"), - ('EZ1', 'Entropy Zero'), - ('EZ2', 'Entropy Zero 2'), + ('GMOD', "Garry's Mod"), + ('EZ1', 'Entropy: Zero'), + ('EZ2', 'Entropy: Zero 2'), ('KZ', 'Kreedz Climbing'), ], 'P2': [ From 7e9cc581175789f8f74a30b110152ea019a65ab4 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 01:30:46 -0800 Subject: [PATCH 043/243] Remove non functional keyvalues from team_control_point --- fgd/point/team/team_control_point.fgd | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/fgd/point/team/team_control_point.fgd b/fgd/point/team/team_control_point.fgd index e025c9d52..83e999468 100644 --- a/fgd/point/team/team_control_point.fgd +++ b/fgd/point/team/team_control_point.fgd @@ -43,18 +43,21 @@ team_capsound_0(sound) : "Reset Sound" : : "Sound made when point resets." team_capsound_2(sound) : "RED Capture Sound" : : "Sound made when RED captures." team_capsound_3(sound) : "BLU Capture Sound" : : "Sound made when BLU captures." - team_model_0(studio) : "Neutral Model" : "models/effects/cappoint_hologram.mdl" : "Model when neither team owns the point." - team_model_2(studio) : "RED Model" : "models/effects/cappoint_hologram.mdl" : "Model when RED owns the point." - team_model_3(studio) : "BLU Model" : "models/effects/cappoint_hologram.mdl" : "Model when BLU owns the point." - team_bodygroup_0(integer) : "Neutral model bodygroup" : 3 : "Model bodygroup when neither team owns the point" - team_bodygroup_2(integer) : "RED model bodygroup" : 1 : "Model bodygroup when RED owns the point" - team_bodygroup_3(integer) : "BLU model bodygroup" : 1 : "Model bodygroup when BLU owns the point" - team_icon_0(material) : "HUD icon neutral" : "sprites/obj_icons/icon_obj_neutral" : "HUD icon material when no one owns the point." - team_icon_2(material) : "HUD icon RED" : "sprites/obj_icons/icon_obj_red" : "HUD icon material when RED owns the point." - team_icon_3(material) : "HUD icon BLU" : "sprites/obj_icons/icon_obj_blu" : "HUD icon material when BLU owns the point." - team_overlay_0(material) : "HUD overlay neutral" : : "HUD material that will overlay the icon when no one owns the point." - team_overlay_2(material) : "HUD overlay RED" : : "HUD material that will overlay the icon when RED owns the point." - team_overlay_3(material) : "HUD overlay BLU" : : "HUD material that will overlay the icon when BLU owns the point." + team_model_0(studio) : "Neutral Model" : "models/effects/cappoint_hologram.mdl" : "Model when neither team owns the point. The body group will be set to 0." + team_model_2(studio) : "RED Model" : "models/effects/cappoint_hologram.mdl" : "Model when RED owns the point. The body group will be set to 2." + team_model_3(studio) : "BLU Model" : "models/effects/cappoint_hologram.mdl" : "Model when BLU owns the point. The body group will be set to 3." + + // These don't seem to be used anywhere in the code + // team_bodygroup_0(integer) : "Neutral model bodygroup" : 3 : "Model bodygroup when neither team owns the point" + // team_bodygroup_2(integer) : "RED model bodygroup" : 1 : "Model bodygroup when RED owns the point" + // team_bodygroup_3(integer) : "BLU model bodygroup" : 1 : "Model bodygroup when BLU owns the point" + // team_icon_0(material) : "HUD icon neutral" : "sprites/obj_icons/icon_obj_neutral" : "HUD icon material when no one owns the point." + // team_icon_2(material) : "HUD icon RED" : "sprites/obj_icons/icon_obj_red" : "HUD icon material when RED owns the point." + // team_icon_3(material) : "HUD icon BLU" : "sprites/obj_icons/icon_obj_blu" : "HUD icon material when BLU owns the point." + // team_overlay_0(material) : "HUD overlay neutral" : : "HUD material that will overlay the icon when no one owns the point." + // team_overlay_2(material) : "HUD overlay RED" : : "HUD material that will overlay the icon when RED owns the point." + // team_overlay_3(material) : "HUD overlay BLU" : : "HUD material that will overlay the icon when BLU owns the point." + team_previouspoint_2_0(target_source) : "RED Previous Required Point 1" : : "The name of a previous capture point that RED must own to be able to capture this point. If empty, the team must own all points preceding this one. Pointing to itself means no previous point required." team_previouspoint_2_1(target_source) : "RED Previous Required Point 2" : : "The name of a second previous capture point that RED must own to be able to capture this point." team_previouspoint_2_2(target_source) : "RED Previous Required Point 3" : : "The name of a third previous capture point that RED must own to be able to capture this point." From 11f2478aaf081ad44d99a690d9317cacd50a4c73 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 12 Mar 2023 19:45:51 +1000 Subject: [PATCH 044/243] Change propcombine yaw correction to just correct relative to nearest 90 deg. --- src/hammeraddons/propcombine.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index fd7a9048e..ba01adfd8 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -217,12 +217,14 @@ async def combine_group( for prop in props: avg_pos += prop.origin - avg_yaw += prop.angles.yaw + yaw = prop.angles.yaw % 90 + if yaw > 45.0: + avg_yaw -= 90.0 - yaw + else: + avg_yaw += yaw visleafs.update(prop.visleafs) - # Snap to nearest 45 degrees to keep the models themselves not - # strangely rotated. - avg_yaw = round(avg_yaw / (45 * len(props))) * 45.0 + avg_yaw /= len(props) avg_pos /= len(props) yaw_rot = Matrix.from_yaw(-avg_yaw) From dff04a987e519a6f953c04b4046697817ae2249b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 12 Mar 2023 19:46:10 +1000 Subject: [PATCH 045/243] Rewrite auto combine algorithm using an actual clustering method --- src/hammeraddons/propcombine.py | 112 ++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index ba01adfd8..cbfd5cd16 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -4,8 +4,9 @@ draw call. """ from typing import ( - Callable, Dict, FrozenSet, Iterable, Iterator, List, MutableMapping, Optional, Set, Tuple, - Union, + Callable, Dict, FrozenSet, Iterable, Iterator, List, Literal, MutableMapping, Optional, Set, + Tuple, + Union, Sequence, ) from collections import defaultdict from enum import Enum @@ -957,58 +958,89 @@ def group_props_auto( # Each of these groups cannot be merged with other ones. dist_sq = dist * dist - large_dist_sq = 4 * dist_sq + neighbours: Dict[StaticProp, Sequence[StaticProp]] = {} + + def find_neighbours(start: StaticProp) -> Sequence[StaticProp]: + """Find props within dist from the specified one.""" + try: + return neighbours[start] + except KeyError: + pass + neigh = [ + prop for prop in group + if (prop.origin - start.origin).mag_sq() <= dist_sq + ] + neighbours[start] = neigh + return neigh + + UNSET: Literal['unset'] = 'unset' + NOISE: Literal['noise'] = 'noise' for group in prop_groups.values(): # No point merging single/empty groups. if len(group) < 2: continue - todo = set(group) - while todo: - center = todo.pop() - cluster = {center} + # DBSCAN algorithm. + labels: Dict[StaticProp, Union[int, Literal['noise', 'unset']]] = dict.fromkeys(group, UNSET) + neighbours.clear() + cluster_ind = 0 - for prop in todo: - if (center.origin - prop.origin).mag_sq() <= large_dist_sq: - cluster.add(prop) - if len(cluster) > MAX_GROUP: - # Limit the number of maximum props that can be used. - break + LOGGER.debug('Grouping {} props', len(group)) - if len(cluster) < min_cluster: + for prop in group: + if labels[prop] is not UNSET: continue - - bbox_min, bbox_max = Vec.bbox(prop.origin for prop in cluster) - center_pos = (bbox_min + bbox_max) / 2 - - cluster_list: List[Tuple[StaticProp, int, float]] = [] - + neigh = find_neighbours(prop) + if len(neigh) < min_cluster: + labels[prop] = NOISE + continue + cluster_ind += 1 + labels[prop] = cluster_ind + todo = set(neigh) + + while todo: + sub_prop = todo.pop() + if labels[sub_prop] is NOISE: + labels[sub_prop] = cluster_ind + elif labels[sub_prop] != UNSET: + continue # Already handled. + labels[sub_prop] = cluster_ind + neigh = find_neighbours(sub_prop) + if len(neigh) > min_cluster: + todo.update(neigh) + + neighbours.clear() # Discard, no longer useful. + + clusters: Dict[int, List[StaticProp]] = defaultdict(list) + for prop, key in labels.items(): + if type(key) is int: + clusters[key].append(prop) + + for cluster in clusters.values(): + total_verts = 0 + selected_props: List[StaticProp] = [] + warned: bool = False for prop in cluster: - prop_off = (center_pos - prop.origin).mag_sq() qc, mdl = get_model(prop.model) assert mdl is not None - if prop_off <= dist_sq: - cluster_list.append((prop, mdl.total_verts, prop_off)) - - # Sort by the distance to the original, so we prefer closer models with more verts. - cluster_list.sort(key=lambda tup: tup[2] - 8.0 * tup[1]) - total_verts = 0 - selected_props: List[StaticProp] = [] - for prop, vert_count, off in cluster_list: - total_verts += vert_count + total_verts += mdl.total_verts if total_verts > MAX_VERTS: # Make this just info level, just might be props nearby. - LOGGER.info( - 'Hit vert limit for auto group @ {} with models {}', center_pos, - {prop.model for prop, vert, off in cluster_list}, - ) - break - elif len(selected_props) > MAX_GROUP: - break - else: - selected_props.append(prop) - todo.difference_update(selected_props) + if not warned: + bb_min, bb_max = Vec.bbox(prop.origin for prop in cluster) + LOGGER.info( + 'Hit vert limit for auto group @ ({} - {}) with models {}' , + bb_min, bb_max, + {prop.model for prop in cluster}, + ) + warned = True + # Split the group here, create a new prop. + if len(selected_props) >= min_cluster: + yield selected_props + selected_props = [] + total_verts = mdl.total_verts + selected_props.append(prop) if len(selected_props) >= min_cluster: yield selected_props From ada4a1c4f51c699a278004c626a141331723213f Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 03:06:02 -0700 Subject: [PATCH 046/243] Cleanup ents using the BaseObject class --- fgd/bases/BaseObject.fgd | 12 +++++++----- fgd/point/bot/bot_hint_engineer_nest.fgd | 2 +- fgd/point/bot/bot_hint_sentrygun.fgd | 2 +- fgd/point/bot/bot_hint_sniper_spot.fgd | 10 ---------- fgd/point/bot/bot_hint_teleporter_exit.fgd | 2 +- fgd/point/mapobj_cart_dispenser.fgd | 2 +- fgd/point/obj/obj_dispenser.fgd | 14 +++++++------- fgd/point/obj/obj_sentrygun.fgd | 12 +++++------- fgd/point/obj/obj_teleporter.fgd | 14 ++++++-------- 9 files changed, 29 insertions(+), 41 deletions(-) delete mode 100644 fgd/point/bot/bot_hint_sniper_spot.fgd diff --git a/fgd/bases/BaseObject.fgd b/fgd/bases/BaseObject.fgd index 8a850f577..207a14e3e 100644 --- a/fgd/bases/BaseObject.fgd +++ b/fgd/bases/BaseObject.fgd @@ -7,11 +7,13 @@ 3: "BLU" ] - skin[!engine](choices) : "[H] Team" : 0 : "Team to show in Hammer" = - [ - 0 : "RED" - 1 : "BLU" - ] + defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 + defaultupgrade(choices) : "Starting Upgrade Level" : "0" = + [ + 0: "Level 1" + 1: "Level 2" + 2: "Level 3" + ] spawnflags(flags) : "spawnflags" = [ diff --git a/fgd/point/bot/bot_hint_engineer_nest.fgd b/fgd/point/bot/bot_hint_engineer_nest.fgd index 911bbc135..f3e17190f 100644 --- a/fgd/point/bot/bot_hint_engineer_nest.fgd +++ b/fgd/point/bot/bot_hint_engineer_nest.fgd @@ -1,5 +1,5 @@ -@PointClass base(BaseEntityPoint, BaseObject, EnableDisable) +@PointClass base(BaseEntityPoint, EnableDisable) appliesto(TF2) autovis(TF2, TFBots, Bot Hint) studio("models/bots/engineer/bot_engineer.mdl") = bot_hint_engineer_nest: "TF2 Engineer Nest Hint for Bots" diff --git a/fgd/point/bot/bot_hint_sentrygun.fgd b/fgd/point/bot/bot_hint_sentrygun.fgd index ead842b99..6004fb908 100644 --- a/fgd/point/bot/bot_hint_sentrygun.fgd +++ b/fgd/point/bot/bot_hint_sentrygun.fgd @@ -1,5 +1,5 @@ -@PointClass base(BaseEntityPoint, BaseObject, EnableDisable) +@PointClass base(BaseEntityPoint, EnableDisable) appliesto(TF2) autovis(TF2, TFBots, Bot Hint) studio("models/buildables/sentry1_blueprint.mdl") = bot_hint_sentrygun: "TF2 Sentry Gun Placement Hint for Bots" diff --git a/fgd/point/bot/bot_hint_sniper_spot.fgd b/fgd/point/bot/bot_hint_sniper_spot.fgd deleted file mode 100644 index 514212ab9..000000000 --- a/fgd/point/bot/bot_hint_sniper_spot.fgd +++ /dev/null @@ -1,10 +0,0 @@ - -@PointClass base(BaseEntityPoint, BaseObject) - appliesto(TF2) - autovis(TF2, TFBots, Bot Hint) - studio("models/bots/sniper/bot_sniper.mdl") - sphere(radius) -= bot_hint_sniper_spot: "TF2 Sniper Spot Hint for Bots" - [ - radius(float) : "Hint Radius" : 100 : "Radius of hint influence." - ] diff --git a/fgd/point/bot/bot_hint_teleporter_exit.fgd b/fgd/point/bot/bot_hint_teleporter_exit.fgd index 3c84734a5..4c37d4864 100644 --- a/fgd/point/bot/bot_hint_teleporter_exit.fgd +++ b/fgd/point/bot/bot_hint_teleporter_exit.fgd @@ -1,5 +1,5 @@ -@PointClass base(BaseEntityPoint, BaseObject) +@PointClass base(BaseEntityPoint) appliesto(TF2) autovis(TF2, TFBots, Bot Hint) studio("models/buildables/teleporter_blueprint_exit.mdl") = bot_hint_teleporter_exit: "TF2 Teleporter Exit Placement Hint for Bots" diff --git a/fgd/point/mapobj_cart_dispenser.fgd b/fgd/point/mapobj_cart_dispenser.fgd index e53716822..5cd38e7b4 100644 --- a/fgd/point/mapobj_cart_dispenser.fgd +++ b/fgd/point/mapobj_cart_dispenser.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPoint, BaseObject) appliesto(TF2) - iconsprite("editor/bullseye.vmt") = mapobj_cart_dispenser: "TF2 Dispenser" + iconsprite("editor/ficool2/mapobj_cart_dispenser.vmt") = mapobj_cart_dispenser: "TF2 Dispenser" [ spawnflags(flags) : "spawnflags" = [ diff --git a/fgd/point/obj/obj_dispenser.fgd b/fgd/point/obj/obj_dispenser.fgd index 904faa257..710894f64 100644 --- a/fgd/point/obj/obj_dispenser.fgd +++ b/fgd/point/obj/obj_dispenser.fgd @@ -3,13 +3,13 @@ studioprop() = obj_dispenser: "TF2 Dispenser" [ - defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 - defaultupgrade(choices) : "Starting Upgrade Level" : "0" = - [ - 0: "Level 1" - 1: "Level 2" - 2: "Level 3" - ] + + touch_trigger(target_destination) : "Custom Touch Trigger" + skin[!engine](choices) : "[H] Team" : 0 : "Team to show in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + ] model[engine](string) : "Model" : : "Doesn't exist in engine, this is a hack to prevent auto packing" model(choices) : "[H] Upgrade Level" : "models/buildables/dispenser_light.mdl" : "Upgrade level to show in Hammer" = diff --git a/fgd/point/obj/obj_sentrygun.fgd b/fgd/point/obj/obj_sentrygun.fgd index 6bc0748e8..ca53e4cf0 100644 --- a/fgd/point/obj/obj_sentrygun.fgd +++ b/fgd/point/obj/obj_sentrygun.fgd @@ -3,13 +3,11 @@ studioprop() = obj_sentrygun: "TF2 Sentrygun" [ - defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 - defaultupgrade(choices) : "Starting Upgrade Level" : "0" = - [ - 0: "Level 1" - 1: "Level 2" - 2: "Level 3" - ] + skin[!engine](choices) : "[H] Team" : 0 : "Team to show in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + ] model[engine](string) : "Model" : : "Doesn't exist in engine, this is a hack to prevent auto packing" model(choices) : "[H] Upgrade Level" : "models/buildables/sentry1.mdl" : "Upgrade level to show in Hammer" = diff --git a/fgd/point/obj/obj_teleporter.fgd b/fgd/point/obj/obj_teleporter.fgd index 4f99096ba..89902d068 100644 --- a/fgd/point/obj/obj_teleporter.fgd +++ b/fgd/point/obj/obj_teleporter.fgd @@ -4,14 +4,6 @@ line(255 255 255, targetname, matchingteleporter) = obj_teleporter: "TF2 Teleporter" [ - defaultupgrade[engine](integer) : "Starting Upgrade Level" : 0 - defaultupgrade(choices) : "Starting Upgrade Level" : "0" = - [ - 0: "Level 1" - 1: "Level 2" - 2: "Level 3" - ] - spawnflags(flags) : "spawnflags" = [ 4: "Upgradable" : 0 @@ -26,6 +18,12 @@ matchingteleporter(target_destination) : "Matching Teleporter" : : "The teleporter linked to this one." + skin[!engine](choices) : "[H] Team" : 0 : "Team to show in Hammer" = + [ + 0 : "RED" + 1 : "BLU" + ] + // Outputs output OnDestroyed(void) : "Fired when this entity is destroyed." ] From 64e33b05409e447838e7fa39c1e0071becca3ab2 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 20:17:22 -0700 Subject: [PATCH 047/243] Indicate that hammer_notes text appears in 3D view with hammer++ --- fgd/point/hammer/hammer_notes.fgd | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fgd/point/hammer/hammer_notes.fgd b/fgd/point/hammer/hammer_notes.fgd index fe88c0907..35e4b9783 100644 --- a/fgd/point/hammer/hammer_notes.fgd +++ b/fgd/point/hammer/hammer_notes.fgd @@ -7,12 +7,14 @@ worldtext() = hammer_notes: "Fake entity to store notes and comments inside. Won`t spawn." [ - // If not in CSGO, this isn't going to do anything, so remove the size/color KVs, - // and change help. - message[until_CSGO](string) : "Message" : : "A text field for entering any notes." - message[since_CSGO](string) : "Display Message" : : "Text to display in the 3D view." - textsize[since_CSGO](float) : "Text Size" : 10 : "Text Size." - color[since_CSGO](color255) : "Color" : "255 255 255" + // If not in CSGO or Hammer++, this isn't going to do anything so change help. + // FIXME: We could maybe replace the hammerplusplus_fgd.fgd file to make these only appear when actually using hammer++ + message[until_CSGO](string) : "Message" : : "A text field for entering any notes. If you are using Hammer++, this will also appear in the 3D view." + textsize[since_CSGO](float) : "Text Size" : 10 : "Size of the text in the 3D view (Hammer++)" + color[since_CSGO](color255) : "Color" : "255 255 255" : "Color of the text in the 3D view (Hammer++)" + message[since_CSGO](string) : "Display Message" : : "Text to display in the 3D view" + textsize[since_CSGO](float) : "Text Size" : 10 : "Size of the text in the 3D view" + color[since_CSGO](color255) : "Color" : "255 255 255" : "Color of the text in the 3D view" scale(float) : "Scale" : "0.25" : "Changes the icon size, to allow seeing this from further away." linename1(target_destination) : "White Related Entity" : : "Add entity names to have lines drawn to them." From 2596e3e35f15b472db0f2fc1946ef4e048e9e1e8 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 21:14:07 -0700 Subject: [PATCH 048/243] Fix #198 --- fgd/point/env/env_projectedtexture.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/env/env_projectedtexture.fgd b/fgd/point/env/env_projectedtexture.fgd index 9adb83fa7..3e8cc1f5b 100644 --- a/fgd/point/env/env_projectedtexture.fgd +++ b/fgd/point/env/env_projectedtexture.fgd @@ -12,7 +12,7 @@ spawnflags(flags) = [ 1: "Enabled" : 1 - 2: "Always Update (moving light)" : 0 [since_ASW. MBase] + 2: "Always Update (moving light)" : 0 [since_ASW, MBase] ] target(target_destination) : "target" : : "target" From 0d618f1e4a4b616432035dd7157407bb2e4471b2 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 21:17:59 -0700 Subject: [PATCH 049/243] HammerAddons always has spawnflags prefixed --- fgd/bases/BaseEntityIO.fgd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/bases/BaseEntityIO.fgd b/fgd/bases/BaseEntityIO.fgd index ecb58e4cb..9e9fd29ff 100644 --- a/fgd/bases/BaseEntityIO.fgd +++ b/fgd/bases/BaseEntityIO.fgd @@ -50,8 +50,8 @@ input AcceptInput[MBase](string) : "Fires the named input on this entity. Format: '::::' (SetTarget:cheese). Everything beyond the input name is optional. Mind the fact this is arranged differently from FireOutput, having the parameter right after the input name." input CancelPending[MBase](void) : "Cancels any events fired by this entity that are currently pending in the I/O event queue." - input AddSpawnFlags[MBase](integer) : "Adds spawnflag(s) to this entity. Many spawnflags have their respective numbers suffixed in this FGD." - input RemoveSpawnFlags[MBase](integer) : "Removes spawnflag(s) to this entity. Many spawnflags have their respective numbers suffixed in this FGD." + input AddSpawnFlags[MBase](integer) : "Adds spawnflag(s) to this entity. Spawnflags have their respective numbers prefixed in this FGD." + input RemoveSpawnFlags[MBase](integer) : "Removes spawnflag(s) to this entity. Spawnflags have their respective numbers prefixed in this FGD." input AddSolidFlags[MBase](integer) : "Adds solid flags to this entity." input RemoveSolidFlags[MBase](integer) : "Removes solid flags from this entity." @@ -62,7 +62,7 @@ input SetTarget[MBase](target_destination) : "Sets this entity's target. This is specific to certain entities, particularly logic entities that involve a target." input SetOwnerEntity[MBase](target_destination) : "Sets this entity's owner entity. This has nothing to do with parenting and has more to do with collision and kill credits." - input SetThinkNull[MBase](void) : "Sets this entity's general think function to null. Behavior varies from entity to entity.." + input SetThinkNull[MBase](void) : "Sets this entity's general think function to null. Behavior varies from entity to entity." input Use[MBase](void) : "More or less replicates the player interacting with an entity. (+USE)" From c87e19f35249277ccf2e3a78af40f503ac645250 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 21:30:11 -0700 Subject: [PATCH 050/243] Split BaseEntityIO and have PlayerInputs include the inputs --- fgd/bases/BaseEntity.fgd | 2 +- fgd/bases/BaseEntityBrush.fgd | 2 +- ...{BaseEntityIO.fgd => BaseEntityInputs.fgd} | 23 ++++--------------- fgd/bases/BaseEntityOutputs.fgd | 15 ++++++++++++ fgd/bases/BaseEntityPoint.fgd | 2 +- fgd/point/logic/PlayerInputs.fgd | 2 +- 6 files changed, 24 insertions(+), 22 deletions(-) rename fgd/bases/{BaseEntityIO.fgd => BaseEntityInputs.fgd} (84%) create mode 100644 fgd/bases/BaseEntityOutputs.fgd diff --git a/fgd/bases/BaseEntity.fgd b/fgd/bases/BaseEntity.fgd index 85260c0d7..3767e5e2b 100644 --- a/fgd/bases/BaseEntity.fgd +++ b/fgd/bases/BaseEntity.fgd @@ -1,5 +1,5 @@ // Special case - entities that aren't quite brushes or point ents. -@BaseClass base(BaseEntityIO) = BaseEntity +@BaseClass base(BaseEntityInputs, BaseEntityOutputs) = BaseEntity [ targetname(target_source) : "Name" : : "The name that other entities refer to this entity by." globalname(string) : "Global Entity Name" : : "Name by which this entity is linked to another entity in a different map. " + diff --git a/fgd/bases/BaseEntityBrush.fgd b/fgd/bases/BaseEntityBrush.fgd index 5ef9c2523..df036edfd 100644 --- a/fgd/bases/BaseEntityBrush.fgd +++ b/fgd/bases/BaseEntityBrush.fgd @@ -1,4 +1,4 @@ -@BaseClass base(BaseEntityIO) = BaseEntityBrush +@BaseClass base(BaseEntityInputs, BaseEntityOutputs) = BaseEntityBrush [ targetname(target_source) : "Name" : : "The name that other entities refer to this entity by." globalname(string) : "Global Entity Name" : : "Name by which this entity is linked to another entity in a different map. " + diff --git a/fgd/bases/BaseEntityIO.fgd b/fgd/bases/BaseEntityInputs.fgd similarity index 84% rename from fgd/bases/BaseEntityIO.fgd rename to fgd/bases/BaseEntityInputs.fgd index 9e9fd29ff..5f7ce6ef3 100644 --- a/fgd/bases/BaseEntityIO.fgd +++ b/fgd/bases/BaseEntityInputs.fgd @@ -1,7 +1,6 @@ -// IO available on all entities. -@BaseClass = BaseEntityIO - [ - // Inputs +@BaseClass = BaseEntityInputs +[ + // Inputs input Kill(void) : "Removes this entity from the world." input KillHierarchy(void) : "Removes this entity and all its children from the world." @@ -26,14 +25,7 @@ input ClearScriptScope[+MBase, +VSCRIPT](void) : "Clears this entity's script scope" input TerminateScriptScope[+TF2, +VSCRIPT](void) : "Terminates the script scope of the entity." - // Outputs - output OnUser1(void) : "Fired in response to FireUser1 input." - output OnUser2(void) : "Fired in response to FireUser2 input." - output OnUser3(void) : "Fired in response to FireUser3 input." - output OnUser4(void) : "Fired in response to FireUser4 input." - output OnKilled[MBase, L4D](void) : "Fired when the entity is killed and removed from the game." - - // Mapbase BaseEntity changes: + // Mapbase Inputs: input PassUser1[MBase](string) : "Causes this entity's OutUser1 output to be fired, passing along the parameter unchanged." input PassUser2[MBase](string) : "Causes this entity's OutUser2 output to be fired, passing along the parameter unchanged." input PassUser3[MBase](string) : "Causes this entity's OutUser3 output to be fired, passing along the parameter unchanged." @@ -65,9 +57,4 @@ input SetThinkNull[MBase](void) : "Sets this entity's general think function to null. Behavior varies from entity to entity." input Use[MBase](void) : "More or less replicates the player interacting with an entity. (+USE)" - - output OutUser1[MBase](string) : "Fires in response to PassUser1 input, with the parameter passed through unchanged." - output OutUser2[MBase](string) : "Fires in response to PassUser2 input, with the parameter passed through unchanged." - output OutUser3[MBase](string) : "Fires in response to PassUser3 input, with the parameter passed through unchanged." - output OutUser4[MBase](string) : "Fires in response to PassUser4 input, with the parameter passed through unchanged." - ] +] \ No newline at end of file diff --git a/fgd/bases/BaseEntityOutputs.fgd b/fgd/bases/BaseEntityOutputs.fgd new file mode 100644 index 000000000..4fd8eb1c6 --- /dev/null +++ b/fgd/bases/BaseEntityOutputs.fgd @@ -0,0 +1,15 @@ +@BaseClass = BaseEntityOutputs +[ + // Outputs + output OnUser1(void) : "Fired in response to FireUser1 input." + output OnUser2(void) : "Fired in response to FireUser2 input." + output OnUser3(void) : "Fired in response to FireUser3 input." + output OnUser4(void) : "Fired in response to FireUser4 input." + output OnKilled[MBase, L4D](void) : "Fired when the entity is killed and removed from the game." + + // Mapbase Outputs + output OutUser1[MBase](string) : "Fires in response to PassUser1 input, with the parameter passed through unchanged." + output OutUser2[MBase](string) : "Fires in response to PassUser2 input, with the parameter passed through unchanged." + output OutUser3[MBase](string) : "Fires in response to PassUser3 input, with the parameter passed through unchanged." + output OutUser4[MBase](string) : "Fires in response to PassUser4 input, with the parameter passed through unchanged." +] \ No newline at end of file diff --git a/fgd/bases/BaseEntityPoint.fgd b/fgd/bases/BaseEntityPoint.fgd index 8c16ec3f1..f877e1fd8 100644 --- a/fgd/bases/BaseEntityPoint.fgd +++ b/fgd/bases/BaseEntityPoint.fgd @@ -1,4 +1,4 @@ -@BaseClass base(BaseEntityIO) = BaseEntityPoint +@BaseClass base(BaseEntityInputs, BaseEntityOutputs) = BaseEntityPoint [ targetname(target_source) : "Name" : : "The name that other entities refer to this entity by." globalname(string) : "Global Entity Name" : : "Name by which this entity is linked to another entity in a different map. " + diff --git a/fgd/point/logic/PlayerInputs.fgd b/fgd/point/logic/PlayerInputs.fgd index e8b1ec36d..3309a396a 100644 --- a/fgd/point/logic/PlayerInputs.fgd +++ b/fgd/point/logic/PlayerInputs.fgd @@ -1,4 +1,4 @@ -@BaseClass = PlayerInputs: "The inputs that can be fired on the player, for entities that pass through those." +@BaseClass base(BaseEntityInputs) = PlayerInputs: "The inputs that can be fired on the player, for entities that pass through those." [ input IgnoreFallDamage[HL2](float) : "Prevent the player from taking fall damage for [n] seconds, but reset back to taking fall damage after the first impact (so players will be hurt if they bounce off what they hit)." input IgnoreFallDamageWithoutReset[HL2](float) : "Absolutely prevent the player from taking fall damage for [n] seconds. " From 5f8292043719b96ca3f23ff8fa684887b68a1573 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 21:32:51 -0700 Subject: [PATCH 051/243] Clarify RunScriptCode behavior --- fgd/bases/BaseEntityInputs.fgd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/bases/BaseEntityInputs.fgd b/fgd/bases/BaseEntityInputs.fgd index 5f7ce6ef3..669013188 100644 --- a/fgd/bases/BaseEntityInputs.fgd +++ b/fgd/bases/BaseEntityInputs.fgd @@ -16,10 +16,10 @@ input DispatchEffect[until_L4D](string) : "Dispatch an effect from the entity's origin. See https://developer.valvesoftware.com/wiki/List_of_Client_Effects" input RunScriptFile[VSCRIPT](string) : "Execute a game script file from disk." - input RunScriptCode[+VSCRIPT, -srctools, -TF2](script) : "Execute a string of script source code." - input RunScriptCode[+VSCRIPT, +srctools, -TF2](script) : "Execute a string of script source code. Backtick ( ` ) characters will be converted to quotes in-game for strings." + input RunScriptCode[+VSCRIPT, -srctools, -TF2](script) : "Execute a string of script source code. Using double quote characters will corrupt the VMF, so strings cannot be passed in." + input RunScriptCode[+VSCRIPT, +srctools, -TF2](script) : "Execute a string of script source code. Using double quote characters will corrupt the VMF, use backticks ( ` ) instead if you need to pass in a string." // TF2 does this in game code. - input RunScriptCode[+TF2, +VSCRIPT](script) : "Execute a string of script source code. Backtick ( ` ) characters will be converted to quotes in-game for strings." + input RunScriptCode[+TF2, +VSCRIPT](script) : "Execute a string of script source code. Using double quote characters will corrupt the VMF, use backticks ( ` ) instead if you need to pass in a string." input RunScriptCodeQuotable[+MBase, +VSCRIPT](string) : "Execute a string of script source code which converts double apostrophes ('') to quotation marks for strings." input CallScriptFunction[VSCRIPT](string) : "Execute the given function name." input ClearScriptScope[+MBase, +VSCRIPT](void) : "Clears this entity's script scope" From f4d19b0bb22880b8de734a009eb27f890443c1e4 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 12 Mar 2023 22:09:18 -0700 Subject: [PATCH 052/243] Fix #126 --- fgd/bases/NavAttributeRegion.fgd | 6 ++++++ fgd/point/weapon/weapon_item_spawn.fgd | 9 +-------- fgd/point/weapon/weapon_rifle_sg552_spawn.fgd | 7 +++++++ fgd/point/weapon/weapon_smg_mp5_spawn.fgd | 7 +++++++ fgd/point/weapon/weapon_sniper_awp_spawn.fgd | 7 +++++++ fgd/point/weapon/weapon_sniper_scout_spawn.fgd | 7 +++++++ fgd/point/weapon/weapon_spawn.fgd | 9 +++++++++ 7 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 fgd/point/weapon/weapon_rifle_sg552_spawn.fgd create mode 100644 fgd/point/weapon/weapon_smg_mp5_spawn.fgd create mode 100644 fgd/point/weapon/weapon_sniper_awp_spawn.fgd create mode 100644 fgd/point/weapon/weapon_sniper_scout_spawn.fgd diff --git a/fgd/bases/NavAttributeRegion.fgd b/fgd/bases/NavAttributeRegion.fgd index c6834634d..4c1a9c78b 100644 --- a/fgd/bases/NavAttributeRegion.fgd +++ b/fgd/bases/NavAttributeRegion.fgd @@ -20,4 +20,10 @@ precise(boolean) : "Precise" : 0 crouch(boolean) : "Crouch" : 0 stairs(boolean) : "Stairs" : 0 + remove_attributes(integer) : "Remove Attributes" : 0 : "Should remove attributes from nav areas instead of applying them?" + tank_only(boolean) : "Tank Only" : 0 + mob_only(boolean) : "Mob Only" : 0 + + // Inputs + input ApplyNavAttributes(void) : "Applies the nav attributes." ] diff --git a/fgd/point/weapon/weapon_item_spawn.fgd b/fgd/point/weapon/weapon_item_spawn.fgd index b1931ea48..674d07a19 100644 --- a/fgd/point/weapon/weapon_item_spawn.fgd +++ b/fgd/point/weapon/weapon_item_spawn.fgd @@ -1,4 +1,4 @@ -@PointClass appliesto(L4D2) base(BaseEntityPoint) +@PointClass appliesto(L4D2) base(WeaponSpawnSingle) iconsprite(editor/weapon_item_spawn) autovis(Weapons, L4D Weapons, L4D Items) = weapon_item_spawn @@ -18,11 +18,4 @@ item17(integer) : "Grenade Launcher" : 0 item18(integer) : "M60 Machinegun" : 0 melee_weapon(string) : "Melee Weapon" : : "Options: 'Any' or a comma-delimited string of melee weapon script names. Leave blank for none." - spawnflags(flags) = - [ - 1: "Enable Physics on spawned item" : 0 - 2: "Spawned Item Must Exist" : 0 - 8: "Infinite Items" : 0 - ] - ] diff --git a/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd b/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd new file mode 100644 index 000000000..fc16574d1 --- /dev/null +++ b/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd @@ -0,0 +1,7 @@ +@PointClass base(WeaponSpawn) + appliesto(L4D2) + autovis(Weapons, L4D Weapons, Weapon Spawners) + studioprop("models/w_models/Weapons/w_rifle_sg552.mdl") += weapon_rifle_sg552_spawn: "SG552" +[ +] \ No newline at end of file diff --git a/fgd/point/weapon/weapon_smg_mp5_spawn.fgd b/fgd/point/weapon/weapon_smg_mp5_spawn.fgd new file mode 100644 index 000000000..7d5f34be7 --- /dev/null +++ b/fgd/point/weapon/weapon_smg_mp5_spawn.fgd @@ -0,0 +1,7 @@ +@PointClass base(WeaponSpawn) + appliesto(L4D2) + autovis(Weapons, L4D Weapons, Weapon Spawners) + studioprop("models/w_models/Weapons/w_smg_mp5.mdl") += weapon_smg_mp5_spawn: "MP5" +[ +] \ No newline at end of file diff --git a/fgd/point/weapon/weapon_sniper_awp_spawn.fgd b/fgd/point/weapon/weapon_sniper_awp_spawn.fgd new file mode 100644 index 000000000..9cfd63f87 --- /dev/null +++ b/fgd/point/weapon/weapon_sniper_awp_spawn.fgd @@ -0,0 +1,7 @@ +@PointClass base(WeaponSpawn) + appliesto(L4D2) + autovis(Weapons, L4D Weapons, Weapon Spawners) + studioprop("models/w_models/Weapons/w_sniper_awp.mdl") += weapon_sniper_awp_spawn: "AWP Sniper" +[ +] \ No newline at end of file diff --git a/fgd/point/weapon/weapon_sniper_scout_spawn.fgd b/fgd/point/weapon/weapon_sniper_scout_spawn.fgd new file mode 100644 index 000000000..e22cde8b2 --- /dev/null +++ b/fgd/point/weapon/weapon_sniper_scout_spawn.fgd @@ -0,0 +1,7 @@ +@PointClass base(WeaponSpawn) + appliesto(L4D2) + autovis(Weapons, L4D Weapons, Weapon Spawners) + studioprop("models/w_models/Weapons/w_sniper_scout.mdl") += weapon_sniper_scout_spawn: "Scout Sniper" +[ +] \ No newline at end of file diff --git a/fgd/point/weapon/weapon_spawn.fgd b/fgd/point/weapon/weapon_spawn.fgd index 28f2a8753..ec96cdbb2 100644 --- a/fgd/point/weapon/weapon_spawn.fgd +++ b/fgd/point/weapon/weapon_spawn.fgd @@ -30,8 +30,17 @@ "weapon_rifle_ak47": "AK47" "weapon_hunting_rifle": "Hunting Rifle" "weapon_sniper_military": "Sniper Military" + "weapon_smg_mp5" : "MP5" + "weapon_rifle_sg552" : "SG552" + "weapon_sniper_awp" : "Sniper AWP" + "weapon_sniper_scout" : "Sniper Scout" ] spawn_without_director(boolean) : "Spawn instantly without director" : 0 : "Bypass the proximity checks that make nearby weapons be the same tier / not the same weapon." + no_cs_weapons(boolean) : "No CS Weapons" : 0 : "When selecting 'any' weapon, set this to true if you do not want CS weapons." + spawnflags(flags) = + [ + 16 : "Constrain to spawn position (don't drop to the ground)" : 0 + ] ] From 8ef6456b019f7206d3c5a76c6ac9b5034e7e99a0 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 13 Mar 2023 00:44:07 -0700 Subject: [PATCH 053/243] Fix #206: game_text sizes are Portal 2 only --- fgd/point/game/game_text.fgd | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fgd/point/game/game_text.fgd b/fgd/point/game/game_text.fgd index 41da377e7..24ad69015 100644 --- a/fgd/point/game/game_text.fgd +++ b/fgd/point/game/game_text.fgd @@ -39,12 +39,14 @@ "stored in channels. Select which channel this text should be placed in, " + "which will overwrite any active message already in that channel." = [ - 0: "Channel 0 (warning: may be used by some HUD elements)" - 1: "Channel 1 (medium text size)" - 2: "Channel 2 (small text size)" - 3: "Channel 3 (large text size)" - 4: "Channel 4 (medium text size)" - 5: "Channel 5 (warning: may be used by some HUD elements)" + 0: "Channel 0 (warning: may be used by HUD)" + 1: "Channel 1" + 2: "Channel 2" [!P2] + 3: "Channel 3" [!P2] + 2: "Channel 2 (small text size)" [P2] + 3: "Channel 3 (large text size)" [P2] + 4: "Channel 4" + 5: "Channel 5 (warning: may be used by HUD)" ] customfont[KZ](string) : "Custom Font Name" : "" : "The name of the Game Font to use for this text. Game font lists are found in the KreedzClimbing/kz/resource/ClientScheme.res and SourceScheme.res (examples: ClientTitleFont, DefaultSmall). Custom .ttf files are currently not supported. Leave blank to use the default font." autobreak[MBase](boolean) : "Automatically break lines" : 0 : "Allows text to automatically shift to the next line whenever it can't fit on a player's screen. " + From 0ff9efdcf40de546ecdb004888326de271310baa Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 19 Mar 2023 00:10:28 -0700 Subject: [PATCH 054/243] Remove nonfunctional portalgun_nospawn global state --- fgd/point/env/env_global.fgd | 1 - 1 file changed, 1 deletion(-) diff --git a/fgd/point/env/env_global.fgd b/fgd/point/env/env_global.fgd index ef649803c..4d611ce94 100644 --- a/fgd/point/env/env_global.fgd +++ b/fgd/point/env/env_global.fgd @@ -28,7 +28,6 @@ globalstate[P2, -INFRA](choices) : "Global State to Set" = [ - "portalgun_nospawn": "Spawn without Portalgun" "no_pinging_blue": "Prevent Pinging ATLAS" "no_pinging_orange": "Prevent Pinging P-Body" "no_taunting_blue": "Prevent Taunting ATLAS" From 4d81357e8bf0c91c60897c8a6891e71ee409db34 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 15 Mar 2023 17:55:52 +1000 Subject: [PATCH 055/243] Handle nested braces when parsing QCs --- src/hammeraddons/propcombine.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index cbfd5cd16..97fe16134 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -671,9 +671,14 @@ def parse_qc(qc_loc: Path, qc_path: Path) -> Optional[Tuple[ return None elif token_type is Token.BRACE_OPEN: # Skip other "compound" sections we don't care about. + depth = 1 for body_type, body_value in tok: if body_type is Token.BRACE_CLOSE: - break + depth -= 1 + if not depth: + break + elif body_type is Token.BRACE_OPEN: + depth += 1 else: raise tok.error("EOF reached without closing brace (})!") From 45a30cb21950ce977360e99045675874f5e1b667 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 25 Mar 2023 18:47:34 +1000 Subject: [PATCH 056/243] Fix propcombine sometimes removing collisions entirely from component props --- CHANGELOG.md | 3 ++- src/hammeraddons/propcombine.py | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce7fbee67..8b7ef8448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. * Limit the size of propcombined groups to avoid hitting vertex limits. -* Prevent automatically packing models specified only as previews in various entities. +* Prevent automatically packing models specified only as Hammer previews in various entities. +* Fix propcombine sometimes removing collisions entirely from component props. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 97fe16134..d75170c01 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -40,6 +40,16 @@ LOGGER = get_logger(__name__) +class CollType(Enum): + """Collision types that static props can have.""" + NONE = 0 # No collision + BSP = 1 # Treat the same as MODEL. + BBOX = 2 + OBB = 3 + OBB_YAW = 4 + VPHYS = 6 # Collision model + + @attrs.frozen class QC: path: str # QC path. @@ -78,7 +88,7 @@ class QC: # Cache of the SMD models we have already parsed, so we don't need # to parse them again. For the collision model, we store them pre-split. _mesh_cache: ACache[Tuple[QC, int], Mesh] = ACache() -_coll_cache: ACache[Optional[str], List[Mesh]] = ACache() +_coll_cache: ACache[Tuple[Optional[str], CollType], List[Mesh]] = ACache() # Limit the amount of decompile/recompiles we do simultaneously. LIM_PROCESS = trio.CapacityLimiter(8) @@ -153,16 +163,6 @@ def check(point: Vec) -> bool: return check -class CollType(Enum): - """Collision types that static props can have.""" - NONE = 0 # No collision - BSP = 1 # Treat the same as MODEL. - BBOX = 2 - OBB = 3 - OBB_YAW = 4 - VPHYS = 6 # Collision model - - @attrs.frozen class PropPos: """Key used to match models to each other.""" @@ -330,7 +330,7 @@ async def compile_func( assert mdl is not None, prop.model child_ref = await _mesh_cache.fetch((qc, prop.skin), build_reference, prop, qc, mdl) - child_coll = await _coll_cache.fetch(qc.phy_smd, build_collision, qc, prop, child_ref, volume_tolerance > 0) + child_coll = await _coll_cache.fetch((qc.phy_smd, prop.solidity), build_collision, qc, prop, child_ref, volume_tolerance > 0) scale = Vec(prop.scale_x, prop.scale_y, prop.scale_z) offset = Vec(prop.x, prop.y, prop.z) From 8eb843886cf618146ab30115f858ac806e547a21 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 25 Mar 2023 18:50:44 +1000 Subject: [PATCH 057/243] Add an option to allow the sources for compiled models to be preserved. --- CHANGELOG.md | 1 + src/hammeraddons/bsp_transform/__init__.py | 17 ++++++++++---- src/hammeraddons/config.py | 6 +++++ src/hammeraddons/mdl_compiler.py | 27 ++++++++++++++++++++-- src/hammeraddons/postcompiler.py | 17 +++++++++++++- src/hammeraddons/propcombine.py | 2 ++ 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7ef8448..ff80be086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Limit the size of propcombined groups to avoid hitting vertex limits. * Prevent automatically packing models specified only as Hammer previews in various entities. * Fix propcombine sometimes removing collisions entirely from component props. +* Add an option to allow the sources for compiled models to be preserved. -------------------- diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index acf85bbe1..e467d7379 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -47,8 +47,9 @@ def __init__( bsp: BSP, game: Game, *, - studiomdl_loc: Optional[Path]=None, - tags: FrozenSet[str]=frozenset(), + studiomdl_loc: Optional[Path] = None, + tags: FrozenSet[str] = frozenset(), + modelcompile_dump: Optional[Path] = None, ) -> None: self.sys = filesys self.vmf = vmf @@ -57,6 +58,7 @@ def __init__( self.bsp_path = Path(bsp.filename) self._fgd: Optional[FGD] = None self.tags = tags + self.modelcompile_dump = modelcompile_dump self.game = game self.studiomdl = studiomdl_loc self.config = Keyvalues.root() @@ -147,12 +149,17 @@ async def run_transformations( pack: PackList, bsp: BSP, game: Game, - studiomdl_loc: Optional[Path]=None, - config: Mapping[str, Keyvalues]=EmptyMapping, + studiomdl_loc: Optional[Path] = None, + config: Mapping[str, Keyvalues] = EmptyMapping, tags: FrozenSet[str] = frozenset(), + modelcompile_dump: Optional[Path] = None, ) -> None: """Run all transformations.""" - context = Context(filesys, vmf, pack, bsp, game, studiomdl_loc=studiomdl_loc, tags=tags) + context = Context( + filesys, vmf, pack, bsp, game, + studiomdl_loc=studiomdl_loc, tags=tags, + modelcompile_dump=modelcompile_dump, + ) for func_name, func in sorted( TRANSFORMS.items(), diff --git a/src/hammeraddons/config.py b/src/hammeraddons/config.py index a65780d38..efe238a78 100644 --- a/src/hammeraddons/config.py +++ b/src/hammeraddons/config.py @@ -372,6 +372,12 @@ def packfile_filters(block: Keyvalues, kind: str) -> Iterator[re_Pattern[str]]: """, ) +MODEL_COMPILE_DUMP = Opt.string( + 'modelcompile_dump', '', + """If set, models will be compiled as subfolders of this folder, instead of in a + temporary directory. +""") + USE_COMMA_SEP = Opt.boolean_or_none( 'use_comma_sep', """Before L4D, entity I/O used ',' to seperate the different parts. diff --git a/src/hammeraddons/mdl_compiler.py b/src/hammeraddons/mdl_compiler.py index 8a0bb2b0f..2143841c5 100644 --- a/src/hammeraddons/mdl_compiler.py +++ b/src/hammeraddons/mdl_compiler.py @@ -3,13 +3,18 @@ Each comes with a key, used to identify a previously compiled version. We can then reuse already compiled versions. """ -from typing import Any, Awaitable, Callable, Generic, Hashable, List, Set, Tuple, TypeVar +import shutil +from typing import ( + Any, Awaitable, Callable, Generic, Hashable, List, Optional, Set, Tuple, TypeVar, + ContextManager, Union, +) from typing_extensions import Self from pathlib import Path import os import pickle import random import tempfile +import contextlib from srctools import AtomicWriter, logger from srctools.game import Game @@ -52,6 +57,7 @@ def __init__( folder_name: str, version: object=0, pack_models: bool=True, + compile_dir: Optional[Path] =None, ) -> None: # The models already constructed. self._built_models: ACache[ModelKey, GenModel[OutT]] = ACache() @@ -65,6 +71,7 @@ def __init__( self.pack: PackList = pack self.version = version self.studiomdl_loc = studiomdl_loc + self.compile_dir = (compile_dir / folder_name) if compile_dir is not None else None self.limiter = trio.CapacityLimiter(8) self.pack_models = pack_models # For statistics, the number we built this compile @@ -82,6 +89,7 @@ def from_ctx(cls, ctx: Context, folder_name: str, version: object=0) -> 'ModelCo ctx.bsp_path.stem, folder_name, version, + compile_dir=ctx.modelcompile_dump, ) def use_count(self) -> int: @@ -225,7 +233,20 @@ async def _compile( self._mdl_names.add(mdl_name) break - with tempfile.TemporaryDirectory(prefix='mdl_compile') as folder: + # If compile dir is specified, create the folder/clear it, but don't delete once done. + ctx_man: ContextManager[Union[str, bytes, Path]] + if self.compile_dir is not None: + path = Path(self.compile_dir, mdl_name) + ctx_man = contextlib.nullcontext(path) + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + path.mkdir(parents=True, exist_ok=True) + else: # If not specified, use a temporary directory. + ctx_man = tempfile.TemporaryDirectory(prefix='mdl_compile') + + with ctx_man as folder: path = Path(folder) result = await compile_func(key, path, f'{self.model_folder}{mdl_name}.mdl', args) studio_args = [ @@ -237,6 +258,8 @@ async def _compile( LOGGER.debug("Execute {}", studio_args) async with self.limiter: res = await trio.run_process(studio_args, capture_stdout=True, check=False) + if self.compile_dir is not None: + (path / 'compile.log').write_bytes(res.stdout) LOGGER.debug( 'Log for {}:\n{}', str(path / 'model.qc'), diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index bcfd7906d..2fd388bbd 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -1,4 +1,5 @@ """Runs before VRAD, to run operations on the final BSP.""" +import shutil from pathlib import Path import sys import warnings @@ -20,7 +21,6 @@ from srctools import __version__ as version_lib, conv_bool from srctools.bsp import BSP -from srctools.fgd import FGD from srctools.filesys import ZipFileSystem from srctools.packlist import PackList @@ -139,6 +139,19 @@ async def main(argv: List[str]) -> None: LOGGER.warning('No studiomdl path provided.') studiomdl_loc = None + modelcompile_dump_str = conf.opts.get(config.MODEL_COMPILE_DUMP) + modelcompile_dump = conf.expand_path(modelcompile_dump_str) if modelcompile_dump_str else None + if modelcompile_dump is not None: + LOGGER.info('Clearing model compile dump folder {}', modelcompile_dump) + try: + for file in modelcompile_dump.iterdir(): + if file.is_dir(): + shutil.rmtree(file) + else: + file.unlink() + except FileNotFoundError: + pass # Already empty. + use_comma_sep = conf.opts.get(config.USE_COMMA_SEP) if use_comma_sep is None: # Guess the format, by checking existing outputs. @@ -176,6 +189,7 @@ async def main(argv: List[str]) -> None: studiomdl_loc, transform_conf, pack_tags, + modelcompile_dump=modelcompile_dump, ) if studiomdl_loc is not None and args.propcombine: @@ -211,6 +225,7 @@ async def main(argv: List[str]) -> None: min_cluster=conf.opts.get(config.PROPCOMBINE_MIN_CLLUSTER), blacklist=conf.opts.get(config.PROPCOMBINE_BLACKLIST).as_array(), volume_tolerance=conf.opts.get(config.PROPCOMBINE_VOLUME_TOLERANCE), + compile_dump=modelcompile_dump, debug_dump=args.dumpgroups, pack_models=conf.opts.get(config.PROPCOMBINE_PACK) or False, ) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index d75170c01..6ec96f0e8 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -1064,6 +1064,7 @@ async def combine( qc_folders: Optional[List[Path]]=None, crowbar_loc: Optional[Path]=None, decomp_cache_loc: Optional[Path]=None, + compile_dump: Optional[Path]=None, blacklist: Iterable[str]=(), auto_range: float=0, min_cluster: int=2, @@ -1266,6 +1267,7 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: 'ver': 2, 'vol_tolerance': volume_tolerance, }, + compile_dir=compile_dump, pack_models=pack_models, ) as compiler: async def do_combine(group: List[StaticProp]) -> None: From ba8d856cc648fe0d89db94504701d3bec93c8a8f Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 25 Mar 2023 18:52:38 +1000 Subject: [PATCH 058/243] Tidy up some code --- src/hammeraddons/propcombine.py | 20 ++++++++------------ transforms/p2_antline.py | 12 +++++------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 6ec96f0e8..7f7707ed6 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -52,9 +52,10 @@ class CollType(Enum): @attrs.frozen class QC: + """The relevant we need from a QC.""" path: str # QC path. - ref_smd: str # Location of main visible geometry. - phy_smd: Optional[str] # Relative location of collision model, or None + ref_smd: str # Absolute location of main visible geometry. + phy_smd: Optional[str] # Absolute location of collision model, or None ref_scale: float # Scale of main model. phy_scale: float # Scale of collision model. is_concave: bool # If the collision model is known to be concave. @@ -150,10 +151,6 @@ def make_collision_brush(origin: Vec, angles: Angle, brush: BModel) -> Callable[ """Produce a collision checker using a brush entity.""" # Transpose the angles, giving us the inverse transform. inv_angles = Matrix.from_angle(angles).transpose() - # brushes = { - # br for leaf in brush.node.iter_leafs() - # for br in leaf.brushes - # } def check(point: Vec) -> bool: """Check if the given position is inside the volume.""" @@ -336,7 +333,6 @@ async def compile_func( offset = Vec(prop.x, prop.y, prop.z) rot_matrix = Matrix.from_angle(prop.pit, prop.yaw, prop.rol) - ref_mesh.append_model(child_ref, rot_matrix, offset, scale * qc.ref_scale) if has_coll and child_coll is not None: @@ -345,9 +341,9 @@ async def compile_func( matrix = Matrix() # Set the scale - matrix[0,0] = phy_scale.x - matrix[1,1] = phy_scale.y - matrix[2,2] = phy_scale.z + matrix[0, 0] = phy_scale.x + matrix[1, 1] = phy_scale.y + matrix[2, 2] = phy_scale.z # Rotate the matrix matrix @= rot_matrix @@ -1271,15 +1267,15 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: pack_models=pack_models, ) as compiler: async def do_combine(group: List[StaticProp]) -> None: - nonlocal group_count + """Task run to combine one prop.""" grouped_prop = await combine_group(compiler, group, get_model, volume_tolerance) rejected.difference_update(group) final_props.append(grouped_prop) - group_count += 1 async with trio.open_nursery() as nursery: for group_ in grouper: nursery.start_soon(do_combine, group_) + group_count += 1 final_props.extend(rejected) diff --git a/transforms/p2_antline.py b/transforms/p2_antline.py index 961715d34..473c0a352 100644 --- a/transforms/p2_antline.py +++ b/transforms/p2_antline.py @@ -4,8 +4,6 @@ If the one of the target entities is a prop_indicator_panel, it also toggles that. """ -from typing import List, Set - from srctools import Entity, Output from srctools.logger import get_logger @@ -48,15 +46,15 @@ def comp_antlines(ctx: Context): # These are the names, not the ents themselves. # Or brush ents holding overlays. - ind_overlays = set() # type: Set[str] - ind_toggles = set() # type: Set[str] + ind_overlays: set[str] = set() + ind_toggles: set[str] = set() # These need the right inputs. - ind_panel_tim = set() # type: Set[str] - ind_panel_check = set() # type: Set[str] + ind_panel_tim: set[str] = set() + ind_panel_check: set[str] = set() # Panels without an indicator set - we can use # these instead of a texturetoggle. - unused_panels = [] # type: List[Entity] + unused_panels: list[Entity] = [] for ind_ent in ctx.vmf.search(ind_name): cls = ind_ent['classname'] From 075b54443f625a72adedefb5f9f8fd264c339679 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 28 Mar 2023 09:31:56 +1000 Subject: [PATCH 059/243] Implement #210: Add OnFinished output to comp_numeric_transition. --- CHANGELOG.md | 1 + fgd/point/comp/comp_numeric_transition.fgd | 2 ++ transforms/comp_numeric_transition.py | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff80be086..71e821ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Prevent automatically packing models specified only as Hammer previews in various entities. * Fix propcombine sometimes removing collisions entirely from component props. * Add an option to allow the sources for compiled models to be preserved. +* #210: Add `OnFinished` output to `comp_numeric_transition`. -------------------- diff --git a/fgd/point/comp/comp_numeric_transition.fgd b/fgd/point/comp/comp_numeric_transition.fgd index c1b39613a..c51a374c6 100644 --- a/fgd/point/comp/comp_numeric_transition.fgd +++ b/fgd/point/comp/comp_numeric_transition.fgd @@ -59,4 +59,6 @@ "sine" : "Sinusoidal" // "overshoot" : "Overshoot" ] + + output OnFinished(void) : "Fired once the transition has completed." ] diff --git a/transforms/comp_numeric_transition.py b/transforms/comp_numeric_transition.py index 626345dcb..8cbfdcade 100644 --- a/transforms/comp_numeric_transition.py +++ b/transforms/comp_numeric_transition.py @@ -172,6 +172,12 @@ def compute_point(x: float) -> float: # Directly sets the location at every point. result = map(compute_point, points) + # Add the duration so the output fires after the transition finishes. + for out in ent.outputs: + if out.output.casefold() == 'onfinished': + out.output = 'OnTrigger' + out.delay += duration + last_inp = None for i, point in enumerate(result): if io_type == 'kv': From 0e7d495d05c29d7936f3a3770606613f5eea3a41 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 28 Mar 2023 09:32:16 +1000 Subject: [PATCH 060/243] Tidy up comp_numeric_transition code --- transforms/comp_numeric_transition.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/transforms/comp_numeric_transition.py b/transforms/comp_numeric_transition.py index 8cbfdcade..b44c28cdd 100644 --- a/transforms/comp_numeric_transition.py +++ b/transforms/comp_numeric_transition.py @@ -1,14 +1,16 @@ """Implements an entity which transitions a value from one to another.""" -import math +from typing import Callable, Dict, Iterator, Optional, Union from string import ascii_lowercase -from typing import Callable, Dict, Optional +import math -from srctools import conv_float, Output +from srctools import Entity, conv_float, Output from srctools.fgd import EntityDef from srctools.logger import get_logger +from srctools.vmf import conv_kv from hammeraddons.bsp_transform import trans, Context + LOGGER = get_logger(__name__) halfpi = math.pi / 2.0 @@ -17,7 +19,7 @@ EPSILON = 1e-6 BRIGHT_LETTERS = { - let : i/25 + let: i/25 for i, let in enumerate(ascii_lowercase) } @@ -43,6 +45,7 @@ def lerp(x: float, in_min: float, in_max: float, out_min: float, out_max: float) @trans('comp_numeric_transition') def numeric_transition(ctx: Context) -> None: """When triggered, animates a keyvalue/input over time with various options.""" + ent: Entity for ent in ctx.vmf.by_class['comp_numeric_transition']: ent['classname'] = 'logic_relay' @@ -59,7 +62,8 @@ def numeric_transition(ctx: Context) -> None: # Special case - if the transform type is "light", allow parsing these # as A-Z values. - value_start = value_end = None # type: Optional[float] + value_start: Optional[float] = None + value_end: Optional[float] = None if transform_type == 'light': value_start = BRIGHT_LETTERS.get(ent['startval'].lower(), None) value_end = BRIGHT_LETTERS.get(ent['endval'].lower(), None) @@ -128,6 +132,7 @@ def numeric_transition(ctx: Context) -> None: ease_end_func = ease_func_linear def compute_point(x: float) -> float: + """Apply the easing equations to compute the value.""" pos = x * ease_start_func(x) + (1.0 - x) * ease_end_func(x) if pos < 0.0: pos = 0.0 @@ -141,6 +146,7 @@ def compute_point(x: float) -> float: point_count = 1 points = [i / point_count for i in range(int(point_count))] + result: Iterator[Union[str, float]] if transform_type == 'speed': # Compute the speed from x to x+1 result = ( @@ -185,7 +191,7 @@ def compute_point(x: float) -> float: param = '{} {}'.format(input_name, point) else: # input io_input = input_name - param = format(point) + param = conv_kv(point) if param != last_inp: ent.add_out(Output( @@ -232,18 +238,18 @@ def ease_func_sine_end(x: float) -> float: return 1.0 - math.cos(x * halfpi) -EASE_START_FUNC = { +EASE_START_FUNC: Dict[str, Callable[[float], float]] = { 'linear': ease_func_linear, 'quad': ease_func_power_start(2), 'cubic': ease_func_power_start(3), 'quartic': ease_func_power_start(4), 'sine': ease_func_sine_start, -} # type: Dict[str, Callable[[float], float]] +} -EASE_END_FUNC = { +EASE_END_FUNC: Dict[str, Callable[[float], float]] = { 'linear': ease_func_linear, 'quad': ease_func_power_end(2), 'cubic': ease_func_power_end(3), 'quartic': ease_func_power_end(4), 'sine': ease_func_sine_end, -} # type: Dict[str, Callable[[float], float]] +} From 2f4aeea2082caa6893e4bc262f79fd1edf47fd32 Mon Sep 17 00:00:00 2001 From: Adr Date: Tue, 28 Mar 2023 18:04:27 +0200 Subject: [PATCH 061/243] Add additional aliases --- fgd/point/monster_furniture.fgd | 6 ++++++ fgd/point/proto_sniper.fgd | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 fgd/point/monster_furniture.fgd create mode 100644 fgd/point/proto_sniper.fgd diff --git a/fgd/point/monster_furniture.fgd b/fgd/point/monster_furniture.fgd new file mode 100644 index 000000000..d8cb4d96f --- /dev/null +++ b/fgd/point/monster_furniture.fgd @@ -0,0 +1,6 @@ +@PointClass aliasof(npc_furniture) + studio() + autovis(NPCs, Furniture) += monster_furniture: "Alternate name for npc_furniture." + [ + ] diff --git a/fgd/point/proto_sniper.fgd b/fgd/point/proto_sniper.fgd new file mode 100644 index 000000000..876565bbb --- /dev/null +++ b/fgd/point/proto_sniper.fgd @@ -0,0 +1,7 @@ +@NpcClass aliasof(npc_sniper) + appliesto(EP1, EP2, HL2, P1, Mesa) + autovis(Entities, NPCs, Combine, Combine Sniper) + studio("models/combine_soldier.mdl") += proto_sniper: "Alternate name for npc_sniper." + [ + ] From d10817ed1035b4cb87c5cc10eb5d36b48c754c8c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 29 Mar 2023 11:31:16 +1000 Subject: [PATCH 062/243] #10: Include an invisible diamond selection in axis helpers for easier targeting --- CHANGELOG.md | 1 + hammer/materials/models/editor/invisible.vmt | 5 + .../models/editor/axis_helper_white.dx80.vtx | Bin 1993 -> 4740 bytes .../models/editor/axis_helper_white.dx90.vtx | Bin 1993 -> 4740 bytes hammer/models/editor/axis_helper_white.mdl | Bin 1898 -> 1944 bytes hammer/models/editor/axis_helper_white.sw.vtx | Bin 1985 -> 4724 bytes hammer/models/editor/axis_helper_white.vvd | Bin 10048 -> 25792 bytes .../editor/axis_helper_white/axis_helper.smd | 618 ++++++++++++++++++ .../axis_helper_white/axis_helper_white.qc | 28 + .../editor/axis_helper_white/helper.blend | Bin 0 -> 786588 bytes modelsrc/editor/axis_helper_white/idle.smd | 9 + 11 files changed, 661 insertions(+) create mode 100644 hammer/materials/models/editor/invisible.vmt create mode 100644 modelsrc/editor/axis_helper_white/axis_helper.smd create mode 100644 modelsrc/editor/axis_helper_white/axis_helper_white.qc create mode 100644 modelsrc/editor/axis_helper_white/helper.blend create mode 100644 modelsrc/editor/axis_helper_white/idle.smd diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e821ac3..2ab518322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Fix propcombine sometimes removing collisions entirely from component props. * Add an option to allow the sources for compiled models to be preserved. * #210: Add `OnFinished` output to `comp_numeric_transition`. +* #10: The center of the axis helper used for sprites can now be clicked on. -------------------- diff --git a/hammer/materials/models/editor/invisible.vmt b/hammer/materials/models/editor/invisible.vmt new file mode 100644 index 000000000..009ff83f2 --- /dev/null +++ b/hammer/materials/models/editor/invisible.vmt @@ -0,0 +1,5 @@ +WriteZ + { + // Only writes to Z buffer, so it's invisible but can be selected. + "%noToolTexture" 1 + } diff --git a/hammer/models/editor/axis_helper_white.dx80.vtx b/hammer/models/editor/axis_helper_white.dx80.vtx index 9b9c42d27711aae12e4576d94bec89987d1ad502..00b987f217f74caf5414e794670ad8c394e2ffb7 100644 GIT binary patch literal 4740 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 literal 1993 zcmZA2=T_A~7)IfJ5Cs$k1v@HsR8%Y|3JPKe6?+$Z?;Wur_TI5xjNXNp<2N~L`@_6z zGSAH9BrD0BOc5|DNqBR%w?;=R`lqjh#{;w{LcG}WdNy5)4tD^jg@~eM- zU}Cr+3O#YQG?2D*VA^uz(vLf0=--wr$Teq;K&x0G^4E-^!S)OhWujaR6BS~pSSaen zBC%L35lcmbXcUDaJ)F3qg4_TR4fM4AQJ!P7;Js1-BC3^7a8i8*4nm@5{Dd1Ah35-no6 zSSId>`{JIsD_)7W;*EGMR*KakP3r94vpT)F^gqdZvbmjl(nQXBzKI{=yI3REiM66n zd=+2BdeJW0#7FU2d=eW(hu9=Gip^rH*dpGE58}PpCU%JJ;<v}9qE|c+N5ye*OxzN;MYlL1PKlG^hUgME#c6R? zoDo;Wb#YCc6PLt!aY0-ZSH$HE@j)(#C7_W0kwOMU3Zq>VpZ=0aF#{vT(H=x8gCnI3 ziIhgWjB43AVYf|0SnjE;SMo%#Wb)a(!f$0X<}KViRCoY z!iq=>t*nf7YIS54Ygo%V*3(8i8zSv&WD^~1W(!-{#&&kFlU?j)4}00ieh$#d!AK{E zILr}_a*X4g;3TIw%^A*ej`LjLBA2+#6|QoP>)hZbUEHFZ+mUYWaF=`Br-ujh@{mV7 r<_S-E#&cfql2^Ru4R3kJdp_`yPkg42FOfdJ@{RB0KQHAP{r diff --git a/hammer/models/editor/axis_helper_white.mdl b/hammer/models/editor/axis_helper_white.mdl index b5dee9a3cb716b845f584fc71ffd290a6234e14a..5a5d3fc3a584f0ae207205f5596c8ec316b0f859 100644 GIT binary patch literal 1944 zcmds2J4+l<6uzsjHiEGbEG!~67Qxs=g^13I2rFWfl!6#$f&()d*5lJ6;w41Ulb z;uOu)Ear+xw@Bx+p~q8zh@gYjal>cQ@vo^7u<5U4qc&I(YMm*grWE&Jl4er9(Zo#q zF!tqryJnV30BqGZ@fMPoq1WW!d~?}Q`f(|DwG2ped9-mG6>@1Xt`@&fbO2k8%^1^0 zsjI(njL%%Tx@Ie=32ZesYnb*|!}0d$qOQGBVLM8Zvg_0R;@9~1o+hjMCuut`=N7#J p%LPG!S+4Clj4xw>y{cay@xRL4S4(c*eL*NcupKWyBa+WhkGDC4(X;>n literal 1898 zcmeZt2@Wx2U|{g#7j{Zb$t=k)(od|&ERN4e%_&GNiZ9Q|EJ@YNP00Z&AqcYA(UhE7 zZ_RLK{caGReSGbKM)`FI%z+q4I{^6(fk2F=1I}dxvOstS6GW(h8N!FrN1%KM7PuC$ z`ZH`0bv!_~z|0VUvSnBiW`S5BK1}QYRE`)r0BBwW5DP-BhUjUq2iXS#CO|iUKn;|J z@ecsqj19o-1j)^S83JU(^uzSbhPn~PAI|h!U|9Xf1JM0`j>Xg-DApjs3Z@yB@t*|q z85kR^VK`UN(_$IFr$w;fNsG3QOJ*STK(rr114)JkkRA|dfU+A9>cHX%0xd3{LenEc z0d^KIR2>hJv^6kKm;o_}4h80k03Zg@3^>vYl2$O&=Fk8C=fL^ogCj@)DG8w~Md1^b zsZn$QISLF6Obj15v+NynK~e{P|NkEWwA%)nPeD-z5(j}RK#VKjgWPxnqzDK=Y!HS; zH%JT}oIT@X2g2y$xcDHuK^O)=cHMz-2-1*r&CC1IzJChfW__>QSO9k zTL&!{8B$Wy5=(PR7z*-pDuYXl(h`$X4H+CVQ*u%nz=TtNN+oirGeByn7@$EJB_#z` z`ufG?`T0pXsl|H9`MC^kFbl|}Re)tWN-RUFKZe}=l+>K!7?|aFt4WxbfK_I8YGqks OPH8GvH5XSc7XtvftnZBg diff --git a/hammer/models/editor/axis_helper_white.sw.vtx b/hammer/models/editor/axis_helper_white.sw.vtx index 5d352dc271f94ed9ca290cdacb46b0578f8e16d9..10a097c3266ccc941e50fafaafc958c058b3093d 100644 GIT binary patch literal 4724 zcmYk=1$0zL7{&2>AprsecPGK!0|a+>ch}(V5Ij(Kr`}SdLXEl`ZK=Bpb$6lPX8E7f zIp^*C&YO9&cjvr$yR(HAr9k3HQ^BZ?WF9=^sB_9liMmc4iMq)D>O}YAaU@EjExIO= zk-+{qH81MKFnJ}C4XqLlh2qGL6P2PqRyx_xCee`phEXT3Ocdvjs^ifo(Kk^N*Cxuz zrlZj$7fAF!aUy|;lti3LB0fwKoj^9|3_5}okP5nic<1Ceg+Xu73-kd!K}=Wjsx&YF z6a~dVI*3V4UKO*HL@7`b#3xTSl?5^V$us3Z1yCMT1QWnSFdmEpG3m(xD}$<_3aAEZ zfa;(os10g?xIvO5$8D8FCa4SQgLsv0gTWv$1PlX1!Ei7Vi~ys+7%&=)1(U#J zFcnMz)4&Wc9n1u?!7MNb#G@iPhjYPvun;T&Iba!B4pxE{U=>&cR)e)*Jy-`efK6Z{ z*bKIUEnpkC7u*Amfcro$*bW{9JHSrx2zV4c3?2i!z;5sq*aP;0eIO6)2d9Elz-izN za5^{>oDI$b=YTiBo8Wcu8aNl62QC2TgA2jM;39AdxC~qh4uC7b<={$iHMj~~1HJ~| zfN#N9;977UI0&u>H-MYKjo@Z*2;2g01-FCSz#ZUDa2VVL?gsaR2f#z%aqt9q5Kl3ve8K z3BCj0gCD^U;3x14_!;~Peh0sSKfqt$Pw+SR7yJWG0Q=;_GkzE)pQ7=bD2WtM5Tt^_ zpb#hmih?vy45WkNpadudN`lg$EGPrYfeN5Js0b>9N}vj;2C9PUpeCpRYJm(;8)SmI zpbn@98i4wsA!rO5fhM3CXbPHxmY@Y_1=@htpe@J(?Ld3b5p)3Apfl(Mx`1w=E9ee- zf*zn3=mUC#zMwzo2L^yaU?3O_hJqnr7#IPDgOOk~7zM_FabPSM4<>>MU=o-DCWEP9 zI+zA#fLUNBm<{HFIba@G0Oo^*U=hdxi@{Q`1S|t9z;dt>tOl#V8n6zm1?#~^umNlW zTfk*vMQ(Ys-TLo*{rN8s;X+Lt{SSTTB@xKWyT)3uIj128mOTfsj-@< zshX*|TBxO3skPdut=cI|?bSgYm90+dtS;)RZtAWc>ZxAptv>3je(J9Q8W?F(BF~^m zG+09-(a=bf9(jgEqTw15iAHLaMr(}5YMjPvf+lK`CTohOMw*PsGc6KL*9^_nEX~#& z&5a%Rd@ayIYl)UdnykpPED|l(3a!*Ct=1Z?)jF-$25rPSt5TU1#V_ou#vNj?UG2I$sy)LS3Ybb%`$3WjdhCb%n0fRk~W& z=vrN;>vd2!=tkY7n{|s0=~msQ+jWNy>rUOJyLFH5)e+sN`}Kex)I)k$kLXc7rpNV! zp43x%I@07uo@XM_vwAKP9gQ^mBG2=Y=motPiH_+dy{uRCs$SFUdP8sOExoOG^lqd% z5P9B4;xrd^F&B5bOSq&?UsNW^V2lZs}HT?KW=fcFuBpcW_5%yOTS+ zi@Um;ySs;bx|e&qkNdix`+I-~dXNWuh=+QZhkJxadXz_djK_MM$9sY&dXgu5il=&- zr+bEHdX{H(E=*h{?B%e>qxywa<@+H1Vl>%86@ywRJy*;~BT+nnp| s-r=3z<=x)nz0Py2(39&c5K64Glvr=hUvC9Mu?X}3Q%a53#63^>58&pC%>V!Z literal 1985 zcmZA2X6Jtf87%wJ?38F|$6jMZrC>E2&G*K$1iWwpyj17bnGOaY~#K=fqiYUR)Ag;)1v+u87N`TU-@A;+p6az2dsK zDQ<{c;*Pj2`o&#wU)&Q9#3S)gJQh#I6Y)&E5YNR+@mjnR1LBQ%C*F$p;-mN=2E}Lb zNqiCC#MdFj<2W;t5wC&aWHBO=MRq*+a~KuLVKlkqF^2ru=0?p6jirEbkpc=M1u-5^ z5fdUsOk@(plthY|%oIwQ8YyKOWt20W8L=&kS{|yPl9`c8W<@GvJew-!M5>s}JgS)= zsiuYn)Uq&A%OdKir-8+>t&3V8T0$dBBaJjg8e_bSW|l{qS;0zH(ZcF@PpxGg>)F6Y zTG_;A+Sn3lqn)j6V>>(8$u4%YhrR4$KLhsHP$6bItxZVo428CH&uyut0>M$N(W%Nf4>552P)Z_1pWbeb?D% zt$prS=cISOZ{71h|FibmYp=cj=ld@F>aFkmt$mBd;>_zFIdze|{R z+pv}${|?7R-~H5M>x;#y4eU#vIN06t>h(=tUtD|HjaPr+S~)ky=j%>waGk~f&LYn_|UvkIm;a_*= zmX+|o@V#rhZ+-1=SMq1$!XSo`0qY+^#ot}U+~0d75)?ZujzhU)|j(i&YJia{wJ>c?7D}K`~iR2@t>RU+j_#a zvVJ>W-@SED_pzH7@_xL21;3^B$Kt>JkvlqauEv7_Kh&RI>lN{NQhciMmS5^s!9RXt z$Hl{L>eUEOOl$tSUU5BR#`klV{#kQKliEe-ul8<8Sf^q7Ju;GMHk@>f2aODbKSKaV{rU0 zvH2GiZXVmZEkBoOx&!c`a>i44l5O(?#UOBX% zq~G)UNif>aSo~jId~5g2`>&d;FU9l9q5R-i;m043zusSzztP{8e#80`^|xT6_(c6J zc+=me*f#p%(SE^R$33Hdm~-?eymBc1-)?@B+Yf`$e$V3RhmZW)&SXDQJg*$e58ms~ z!Fc@mm((*U@`O*Gs{R#T{FrHpNum1DOVfj70&9}N=_4;%0UO!2F^YHkf>#OGX z^g@jH`~S$FRzI)vJNC!d3za`9Ph0&zc*9@t#3$(Ii=GvK3ZDJQ(+lLAu1AZH`v1tE zsGo=5%y020(hGLK;9i<p$-l&)!H6;q6}oe#_$7tD}C*WoGL zjS>8j-qC!n`2#&t^3&oIzoSQNeW>vse-O{6esSh&Wj)f;JDUGiA4U3wF&clgAL}DM zV%M+bkMxf1pShRhf6r^AUpOcJqc3#*Cj6CO|4moF7`&{rF+XDt>cfN|ec?U-iTKeL z_lriY`E9>#{b}eg;mCT;=!K^KlKE0SrFtRNLT~+A|7-p|;I;qnBK-xw@u%RKUr!&| z_!vEs#_Q>|0N>PKjz25wDeA)>^q0mz)L&J63O>?PGF~qqLcNyj4|h4W@l!mP|N6oH zZSsts{mY9dNL|Ag&;AwRxrQ2H@$h@|OZ}AIKh^Xz*T4J?T+5pO{MC23dWZeR;2%@` zvroNcCH&ieky#_|Wf3?)bnfk zrT!E={(E@%Ifi;Q!h6qGHJ;=H*J*!p#e0vtwSe*RY3eSUhtT;kkzSs`2!Ebv{)5 zo%%y<2=X)4zrmjoKJE{TKLyX{5gvc;Pw*=DaJ*xV>3_WO=KVe6?f%H~%*DZbZ>Yy> z`M2cx{8#1YkL4LXJPrq6FB~B{PCyY;r8%EhJzli z#?yng_{EowYlsi}DC*zx9Gu#&@m@cZy~+MqJl}uV1AnjnkPBY@vGYqTE#BMjDt`)| z_Gw+gjQTyW`5vkNEcmFO)Otnzl>9IKUZX!u^=iP6`nTx$x%L}p zuRmGvKm3Q^cl#@ir`B_9)-U)u{>|%87XRr#z31~0KX{K{;^U6D(cj)=`rG19)Ze!J zS*5>?`eFFBf75$OJUgC;d(W#Cqx%;&cs?KT{NcRE58nHJ4tS25AAf@WJjJudH~M)O z-{|Ml{MP(YKcBsy`VaqH{@8vs>i;$0@TbOm{R#f)`m*>)FGTpL|Bw8c>*s6#BfU`h zQ}hx2iPxVie&8?oMn7NHqr|7+;rH}{t%nvL_5YDSQ9rNzwg2EfyM5JH9X#a2Z3;nRCUo778)5}Nk(f-`x@rN9$^MBx{o@`rx&{N*$p|;<) z{bxgex%#E0zwoKnFRSV)`1hc{iXM#gm&S1Lr{LiZ^pxh$NIyn;&BoK}mzMt0_*nd^ zdJ6tXuW9~Ry%y;&8y}00^i<~NsY7oBuWb+@%*!@Ya9ue13rr zV{crA|E=FS>h|aGYyMb%@Lqk3{CWJuyIg!d-ogK*FOGcqbCc)Kc6}Lt68`*+Uz^|q z|I_zpR^dg`Xs{F-mppVWT_cqg76_^bZq^Amo%U)cO)zw`U6{M}f5v3UB820rca z77xGIFX2z_SI7MHlK$U=WF5sdmY?s3M0o94Yy9G%^pw?l8T?89^S2#8kp1-4qLZGL zzm@Uw)5LS#;$K|5_XZDd_tP5h>Df9zWeh!Yk@MkiCi83XXNNf}_)q-&hbDNlzD9rV zmh*G({2#mdSh(JGf3g06_u>=#bKJ$(@#i~j{E!b`JZ-+|-|i^hdp=Olm-3@!J-p?o zUs8=t|Axl!@{PD?|E752@8PXKHNXFT#jLyND>ZkxANd)qm-M$6U}}7@UMAml;#$_r zD*W{SGyZQ`f53asD@qyl{i%W%kzSMful;=L zhxeerBxd=L_0{b`PnG@+yk6VTYc9T_zY3@Ir=h2Gys6*T@iz3Di*M*J*B|hnUTDSN z!^?ix>;7o#TSI^K8gq+Be+7D~*Z#1^2YSuLH}se5PeV`Xc*)OlZ9}iQ_=f%}oR&Y* zQ&IdQy=LY&^;dKH%kndCd(cy5y@0p;o?gQronMP@=r4_D@?ZUl^c4PEe#J+6t@dY5 ze+B$4J=J;oh`rs@Yi7Puf2;m9^p`i@P*0ip9q~tc&F&XAKO6dMTkC&~5A+mx(e>m0 zt)bUkd_#Y^{?MO9{UN^Kll8x$*9yP%=ZEL?m)%b-o_-+c|F_kj8t>_mI)8Kwk^ZXl zLGcYerTLuJqvGNB^qTQ!gm36CUGFJBmH)(kUr!nTC;VP~Og^K3534?k^jAG!if`zt zlAkqypuY;9eo6IIq`#~`H9qP;hy3Ard*P4tn#5-VJUy$#zT-VdLw{}1%Y(7_hMoei z>wUx@tVb8$&|j`U4L!Ai?~K9v-_UD{M}KY3=`Wctd4`b>i${M2daC$e<2}8`d};he zFKnW{TKdcSWAP0=rQ=1fjrcu%Wb$VV|E)gq@YbIiALKLRP5EQ-kzO0(lkkK8P5ov4 zvG|6b()gg)h)?17^%uFY+~!{nt#8@lS@`&Qc#P!=51*Oj+=jl}&1(wodKR5QMH@Gmcdy(Tl znh*T7^W)lhIiCML*&Tx$#;5y+YdijNt@XFI#m~|Crv3dP|KwG8mi=Ha^XsyEZs)<% zkG$`m{<7}FQpcD5oV}{Q&+Xi|Ht@@H9QfNdeh6Otx^E5qoNfF`_8KF97@z#;!TVi1*T7{b>m7`)Z}ms{C$_R>F6B$> zb1A>;^ZD48^`7>gVf}Y=w&9bvBmN6w%ls#wa~|k;bj>zxH=c%1c$alOnY9_OsUOL= zJ=o$4M~+XPTid+*-?mpBuhbE(Z`2>^O4F8lvuWy2H)nra>gx0I^Qy&Vsb0<5)C>0FOTY5fLyVvO z8T%)*o!0MQ%lByVdba1O7t#w1^H8prSf>17KTmv-&HBu1HR0K>FQ}dGo4N7-LfbO` zWzQylgUx-G+;50~FY|GFFZQq&Zq8;O*EI#6_xxOZZZqZAzQz5ge@1fpA7zhcd~@~-!uNmjbIIa0DSmUd za32=VwSm8zv)RutS$y=g;2B@q-&?lCsyFM4ST=3V|6zUE^_1d~=TGO!e>L@%ywd(n z-lg@Z>y`CcuV0Dr7|(hi*6Wh%hw}-i_V8NH*Y=Q;a%rgtdOiDjswbS3|0cHlUXFC% zC9ksHx6Jy#;C8xQ`iH$s_ji7AVQlKZ?g7KNbbgi%TjSN?iH+yLmwu0rLw;%fn6-Cv z1?FWpXM^I%KJ3OPvL*NNLF)&(-?XJ(e9_d8e$Li8K5+ z{><7^Z#q+d`Z*hJQ-At7oBiC>o3y{TZ1xe2Ir%YXfAfXU973zJF2NI5Yk%{F1NHf& z?WtSD)m%T=1z(@CNedW%=4|V)aE$zK***Rkylc;|56wN8^7{>Ww(yno zoFT`P`b8a+^Ky^;>|xi|9M@Z zuK_=2&&9Jo&;2>_?{{Mp*%y8Ap8laXpWJ+Y=Ph4kymL0|Lu)AbIa^#w&mv5G=4|4Z zY7G30rDfB{P*3P%1mDfs+WXU|z&~dT-`Y^4WNw%AO-(!VJ$RZrw96mc62CQ5f6_kC zvZa1}$nHf8 zEqR_e;bpw}CtJLqo|pf>gx&hnfi(5Uu}Ad_{)EYQNca1H+AnFnXuT1CM)hZ6OTAe$ z^{1b+>v+xCIS!@%lvv1h@_si~YD>N8O#SKRY}SjZKWV+R>>(c16N$yBp3K?BUsHeN zccXely;(E$C)J0RE%ia3iQKm~)Sqt7rkW`&U~fb4E(XJ^OawB=jTG%KL)&U B!pr~w literal 10048 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 O`F5~&6wefIt^WrYY#ELK diff --git a/modelsrc/editor/axis_helper_white/axis_helper.smd b/modelsrc/editor/axis_helper_white/axis_helper.smd new file mode 100644 index 000000000..39de15057 --- /dev/null +++ b/modelsrc/editor/axis_helper_white/axis_helper.smd @@ -0,0 +1,618 @@ +version 1 +nodes +0 "root" -1 +end +skeleton +time 0 +0 0.000000 0.000000 0.000000 0.000000 1.570790 0.000000 +end +triangles +axis_helper_white +0 0.356750 5.500000 0.000000 0.706345 -0.046407 0.706345 0.002002 0.832332 1 0 1.000000 +0 0.000000 5.500000 0.356750 0.706345 -0.046407 0.706345 0.165666 0.832332 1 0 1.000000 +0 0.000000 0.070000 0.000000 0.706345 -0.046407 0.706345 0.165666 0.502002 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.728657 0.880334 1 0 1.000000 +0 0.000000 -0.776880 -0.034950 0.000000 -1.000000 0.000000 0.765664 0.843327 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.765664 0.880334 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 0.000000 -1.000000 0.000000 0.713667 0.843327 1 0 1.000000 +0 0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.676660 0.880334 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.713667 0.880334 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.713667 0.895324 1 0 1.000000 +0 -0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.676660 0.932331 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.713667 0.932331 1 0 1.000000 +axis_helper_white +0 0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.708670 0.838330 1 0 1.000000 +0 0.000000 0.931410 -0.034950 0.000000 1.000000 0.000000 0.671663 0.875337 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.671663 0.838330 1 0 1.000000 +axis_helper_white +0 0.000000 0.931410 -0.034950 0.000000 1.000000 0.000000 0.723660 0.875337 1 0 1.000000 +0 -0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.760668 0.838330 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.723660 0.838330 1 0 1.000000 +axis_helper_white +0 -0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.671663 0.927334 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.708670 0.890327 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.671663 0.890327 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.905064 0.000000 -0.425275 0.665666 0.873999 1 0 1.000000 +0 0.000000 -0.776880 -0.034950 0.905064 0.000000 -0.425275 0.665666 0.856169 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.905064 0.000000 -0.425275 0.335335 0.873999 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 0.905064 0.000000 -0.425275 0.853166 0.502002 1 0 1.000000 +0 0.000000 0.931410 -0.034950 0.905064 0.000000 -0.425275 0.853166 0.832332 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.905064 0.000000 -0.425275 0.835335 0.832332 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 -0.905064 0.000000 -0.425275 0.665666 0.853166 1 0 1.000000 +0 -0.026680 -0.776880 0.021830 -0.905064 0.000000 -0.425275 0.665666 0.835335 1 0 1.000000 +0 0.000000 0.931410 -0.034950 -0.905064 0.000000 -0.425275 0.335335 0.853166 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 -0.905064 0.000000 -0.425275 0.664665 0.834334 1 0 1.000000 +0 -0.026680 0.931410 0.021830 -0.905064 0.000000 -0.425275 0.334334 0.834334 1 0 1.000000 +0 0.000000 0.931410 -0.034950 -0.905064 0.000000 -0.425275 0.334334 0.852165 1 0 1.000000 +axis_helper_white +0 0.356750 5.500000 0.000000 0.000000 1.000000 0.000000 0.998999 0.498999 1 0 1.000000 +0 0.000000 5.500000 -0.356750 0.000000 1.000000 0.000000 0.835335 0.498999 1 0 1.000000 +0 0.000000 5.500000 0.356750 0.000000 1.000000 0.000000 0.998999 0.335335 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 -0.356750 0.000000 1.000000 0.000000 0.997998 0.334334 1 0 1.000000 +0 -0.356750 5.500000 0.000000 0.000000 1.000000 0.000000 0.834334 0.334334 1 0 1.000000 +0 0.000000 5.500000 0.356750 0.000000 1.000000 0.000000 0.834334 0.497998 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 0.356750 -0.706345 -0.046407 0.706345 0.335335 0.665666 1 0 1.000000 +0 -0.356750 5.500000 0.000000 -0.706345 -0.046407 0.706345 0.498999 0.665666 1 0 1.000000 +0 0.000000 0.070000 0.000000 -0.706345 -0.046407 0.706345 0.498999 0.335335 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.665666 0.936499 1 0 1.000000 +0 0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.665666 0.929085 1 0 1.000000 +0 -0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.335335 0.936499 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.335335 0.926082 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.665666 0.926082 1 0 1.000000 +0 -0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.665666 0.918669 1 0 1.000000 +axis_helper_white +0 -0.356750 5.500000 0.000000 -0.706345 -0.046407 -0.706345 0.168669 0.665666 1 0 1.000000 +0 0.000000 5.500000 -0.356750 -0.706345 -0.046407 -0.706345 0.332332 0.665666 1 0 1.000000 +0 0.000000 0.070000 0.000000 -0.706345 -0.046407 -0.706345 0.332332 0.335335 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 -0.356750 0.706345 -0.046407 -0.706345 0.331331 0.334334 1 0 1.000000 +0 0.356750 5.500000 0.000000 0.706345 -0.046407 -0.706345 0.167668 0.334334 1 0 1.000000 +0 0.000000 0.070000 0.000000 0.706345 -0.046407 -0.706345 0.167668 0.664665 1 0 1.000000 +axis_helper_white +0 -0.364710 -5.500000 0.000000 0.000000 -1.000000 0.000000 0.832332 0.832332 1 0 1.000000 +0 0.000000 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.668669 0.832332 1 0 1.000000 +0 0.000000 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.832332 0.668669 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.831331 0.667668 1 0 1.000000 +0 0.364710 -5.500000 0.000000 0.000000 -1.000000 0.000000 0.667668 0.667668 1 0 1.000000 +0 0.000000 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.667668 0.831331 1 0 1.000000 +axis_helper_white +0 -0.364710 -5.500000 0.000000 -0.706311 0.047440 0.706311 0.832332 0.502002 1 0 1.000000 +0 0.000000 -5.500000 0.364710 -0.706311 0.047440 0.706311 0.832332 0.665666 1 0 1.000000 +0 0.000000 -0.070000 0.000000 -0.706311 0.047440 0.706311 0.502002 0.665666 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 0.364710 0.706311 0.047440 0.706311 0.668669 0.332332 1 0 1.000000 +0 0.364710 -5.500000 0.000000 0.706311 0.047440 0.706311 0.832332 0.332332 1 0 1.000000 +0 0.000000 -0.070000 0.000000 0.706311 0.047440 0.706311 0.832332 0.002002 1 0 1.000000 +axis_helper_white +0 0.364710 -5.500000 0.000000 0.706311 0.047440 -0.706311 0.665666 0.002002 1 0 1.000000 +0 0.000000 -5.500000 -0.364710 0.706311 0.047440 -0.706311 0.665666 0.165666 1 0 1.000000 +0 0.000000 -0.070000 0.000000 0.706311 0.047440 -0.706311 0.335335 0.165666 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 -0.364710 -0.706311 0.047440 -0.706311 0.334334 0.164665 1 0 1.000000 +0 -0.364710 -5.500000 0.000000 -0.706311 0.047440 -0.706311 0.334334 0.001001 1 0 1.000000 +0 0.000000 -0.070000 0.000000 -0.706311 0.047440 -0.706311 0.664665 0.001001 1 0 1.000000 +axis_helper_white +0 -0.356750 5.500000 0.000000 -0.706345 -0.046407 0.706345 0.002002 0.832332 1 0 1.000000 +0 0.000000 0.070000 0.000000 -0.706345 -0.046407 0.706345 0.165666 0.502002 1 0 1.000000 +0 0.000000 5.500000 0.356750 -0.706345 -0.046407 0.706345 0.165666 0.832332 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.728657 0.880334 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.765664 0.880334 1 0 1.000000 +0 0.000000 -0.776880 -0.034950 0.000000 -1.000000 0.000000 0.765664 0.843327 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 0.000000 -1.000000 0.000000 0.713667 0.843327 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.713667 0.880334 1 0 1.000000 +0 -0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.676660 0.880334 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.713667 0.895324 1 0 1.000000 +0 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.000000 0.713667 0.932331 1 0 1.000000 +0 0.026680 -0.776880 0.021830 0.000000 -1.000000 0.000000 0.676660 0.932331 1 0 1.000000 +axis_helper_white +0 -0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.708670 0.838330 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.671663 0.838330 1 0 1.000000 +0 0.000000 0.931410 -0.034950 0.000000 1.000000 0.000000 0.671663 0.875337 1 0 1.000000 +axis_helper_white +0 0.000000 0.931410 -0.034950 0.000000 1.000000 0.000000 0.723660 0.875337 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.723660 0.838330 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.760668 0.838330 1 0 1.000000 +axis_helper_white +0 0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.671663 0.927334 1 0 1.000000 +0 0.000000 0.931410 0.000000 0.000000 1.000000 0.000000 0.671663 0.890327 1 0 1.000000 +0 -0.026680 0.931410 0.021830 0.000000 1.000000 0.000000 0.708670 0.890327 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 -0.905064 0.000000 -0.425275 0.665666 0.873999 1 0 1.000000 +0 -0.026680 0.931410 0.021830 -0.905064 0.000000 -0.425275 0.335335 0.873999 1 0 1.000000 +0 0.000000 -0.776880 -0.034950 -0.905064 0.000000 -0.425275 0.665666 0.856169 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 -0.905064 0.000000 -0.425275 0.853166 0.502002 1 0 1.000000 +0 -0.026680 0.931410 0.021830 -0.905064 0.000000 -0.425275 0.835335 0.832332 1 0 1.000000 +0 0.000000 0.931410 -0.034950 -0.905064 0.000000 -0.425275 0.853166 0.832332 1 0 1.000000 +axis_helper_white +0 0.000000 -0.776880 -0.034950 0.905064 0.000000 -0.425275 0.665666 0.853166 1 0 1.000000 +0 0.000000 0.931410 -0.034950 0.905064 0.000000 -0.425275 0.335335 0.853166 1 0 1.000000 +0 0.026680 -0.776880 0.021830 0.905064 0.000000 -0.425275 0.665666 0.835335 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.905064 0.000000 -0.425275 0.664665 0.834334 1 0 1.000000 +0 0.000000 0.931410 -0.034950 0.905064 0.000000 -0.425275 0.334334 0.852165 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.905064 0.000000 -0.425275 0.334334 0.834334 1 0 1.000000 +axis_helper_white +0 -0.356750 5.500000 0.000000 0.000000 1.000000 0.000000 0.998999 0.498999 1 0 1.000000 +0 0.000000 5.500000 0.356750 0.000000 1.000000 0.000000 0.998999 0.335335 1 0 1.000000 +0 0.000000 5.500000 -0.356750 0.000000 1.000000 0.000000 0.835335 0.498999 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 -0.356750 0.000000 1.000000 0.000000 0.997998 0.334334 1 0 1.000000 +0 0.000000 5.500000 0.356750 0.000000 1.000000 0.000000 0.834334 0.497998 1 0 1.000000 +0 0.356750 5.500000 0.000000 0.000000 1.000000 0.000000 0.834334 0.334334 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 0.356750 0.706345 -0.046407 0.706345 0.335335 0.665666 1 0 1.000000 +0 0.000000 0.070000 0.000000 0.706345 -0.046407 0.706345 0.498999 0.335335 1 0 1.000000 +0 0.356750 5.500000 0.000000 0.706345 -0.046407 0.706345 0.498999 0.665666 1 0 1.000000 +axis_helper_white +0 0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.665666 0.936499 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.335335 0.936499 1 0 1.000000 +0 -0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.665666 0.929085 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.776880 0.021830 0.000000 0.000000 1.000000 0.335335 0.926082 1 0 1.000000 +0 0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.665666 0.918669 1 0 1.000000 +0 -0.026680 0.931410 0.021830 0.000000 0.000000 1.000000 0.665666 0.926082 1 0 1.000000 +axis_helper_white +0 0.356750 5.500000 0.000000 0.706345 -0.046407 -0.706345 0.168669 0.665666 1 0 1.000000 +0 0.000000 0.070000 0.000000 0.706345 -0.046407 -0.706345 0.332332 0.335335 1 0 1.000000 +0 0.000000 5.500000 -0.356750 0.706345 -0.046407 -0.706345 0.332332 0.665666 1 0 1.000000 +axis_helper_white +0 0.000000 5.500000 -0.356750 -0.706345 -0.046407 -0.706345 0.331331 0.334334 1 0 1.000000 +0 0.000000 0.070000 0.000000 -0.706345 -0.046407 -0.706345 0.167668 0.664665 1 0 1.000000 +0 -0.356750 5.500000 0.000000 -0.706345 -0.046407 -0.706345 0.167668 0.334334 1 0 1.000000 +axis_helper_white +0 0.364710 -5.500000 0.000000 0.000000 -1.000000 0.000000 0.832332 0.832332 1 0 1.000000 +0 0.000000 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.832332 0.668669 1 0 1.000000 +0 0.000000 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.668669 0.832332 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.831331 0.667668 1 0 1.000000 +0 0.000000 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.667668 0.831331 1 0 1.000000 +0 -0.364710 -5.500000 0.000000 0.000000 -1.000000 0.000000 0.667668 0.667668 1 0 1.000000 +axis_helper_white +0 0.364710 -5.500000 0.000000 0.706311 0.047440 0.706311 0.832332 0.502002 1 0 1.000000 +0 0.000000 -0.070000 0.000000 0.706311 0.047440 0.706311 0.502002 0.665666 1 0 1.000000 +0 0.000000 -5.500000 0.364710 0.706311 0.047440 0.706311 0.832332 0.665666 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 0.364710 -0.706311 0.047440 0.706311 0.668669 0.332332 1 0 1.000000 +0 0.000000 -0.070000 0.000000 -0.706311 0.047440 0.706311 0.832332 0.002002 1 0 1.000000 +0 -0.364710 -5.500000 0.000000 -0.706311 0.047440 0.706311 0.832332 0.332332 1 0 1.000000 +axis_helper_white +0 -0.364710 -5.500000 0.000000 -0.706311 0.047440 -0.706311 0.665666 0.002002 1 0 1.000000 +0 0.000000 -0.070000 0.000000 -0.706311 0.047440 -0.706311 0.335335 0.165666 1 0 1.000000 +0 0.000000 -5.500000 -0.364710 -0.706311 0.047440 -0.706311 0.665666 0.165666 1 0 1.000000 +axis_helper_white +0 0.000000 -5.500000 -0.364710 0.706311 0.047440 -0.706311 0.334334 0.164665 1 0 1.000000 +0 0.000000 -0.070000 0.000000 0.706311 0.047440 -0.706311 0.664665 0.001001 1 0 1.000000 +0 0.364710 -5.500000 0.000000 0.706311 0.047440 -0.706311 0.334334 0.001001 1 0 1.000000 +axis_helper_white +0 0.356750 0.000000 5.500000 0.706345 -0.706344 -0.046407 0.998999 0.332332 1 0 1.000000 +0 0.000000 -0.356750 5.500000 0.706345 -0.706344 -0.046407 0.835335 0.332332 1 0 1.000000 +0 0.000000 0.000000 0.070000 0.706345 -0.706344 -0.046407 0.998999 0.002002 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 0.000002 0.000000 -1.000000 0.832652 0.880334 1 0 1.000000 +0 0.000000 0.034950 -0.776880 0.000004 0.000000 -1.000000 0.869659 0.843327 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.869659 0.880334 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 -0.000004 0.000000 -1.000000 0.817662 0.843327 1 0 1.000000 +0 0.026680 -0.021830 -0.776880 -0.000002 0.000000 -1.000000 0.780654 0.880334 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.817662 0.880334 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 -0.000002 0.000000 -1.000000 0.817662 0.895324 1 0 1.000000 +0 -0.026680 -0.021830 -0.776880 0.000002 0.000000 -1.000000 0.780654 0.932331 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.817662 0.932331 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.728657 0.932331 1 0 1.000000 +0 0.000000 0.034950 0.931410 0.000000 0.000000 1.000000 0.765664 0.895324 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.765664 0.932331 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 0.931410 0.000000 0.000000 1.000000 0.723660 0.927334 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.760668 0.890327 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.723660 0.890327 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.921656 0.843327 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.884649 0.880334 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.921656 0.880334 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 0.905064 0.425275 0.000000 0.873999 0.832332 1 0 1.000000 +0 0.000000 0.034950 -0.776880 0.905064 0.425275 0.000000 0.856169 0.832332 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.905064 0.425275 0.000000 0.873999 0.502002 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 0.905064 0.425275 0.000000 0.834334 0.831331 1 0 1.000000 +0 0.000000 0.034950 0.931410 0.905064 0.425275 0.000000 0.834334 0.501001 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.905064 0.425275 0.000000 0.852165 0.501001 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 -0.905064 0.425275 0.000000 0.665666 0.894832 1 0 1.000000 +0 -0.026680 -0.021830 -0.776880 -0.905064 0.425275 0.000000 0.665666 0.877002 1 0 1.000000 +0 0.000000 0.034950 0.931410 -0.905064 0.425275 0.000000 0.335335 0.894832 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 -0.905064 0.425275 0.000000 0.664665 0.876001 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 -0.905064 0.425275 0.000000 0.334334 0.876001 1 0 1.000000 +0 0.000000 0.034950 0.931410 -0.905064 0.425275 0.000000 0.334334 0.893831 1 0 1.000000 +axis_helper_white +0 0.356750 0.000000 5.500000 0.000000 0.000000 1.000000 0.665666 0.832332 1 0 1.000000 +0 0.000000 0.356750 5.500000 0.000000 0.000000 1.000000 0.502002 0.832332 1 0 1.000000 +0 0.000000 -0.356750 5.500000 0.000000 0.000000 1.000000 0.665666 0.668669 1 0 1.000000 +axis_helper_white +0 0.000000 0.356750 5.500000 0.000000 0.000000 1.000000 0.664665 0.667668 1 0 1.000000 +0 -0.356750 0.000000 5.500000 0.000000 0.000000 1.000000 0.501001 0.667668 1 0 1.000000 +0 0.000000 -0.356750 5.500000 0.000000 0.000000 1.000000 0.501001 0.831331 1 0 1.000000 +axis_helper_white +0 0.000000 -0.356750 5.500000 -0.706345 -0.706344 -0.046407 0.497998 0.334334 1 0 1.000000 +0 -0.356750 0.000000 5.500000 -0.706345 -0.706344 -0.046407 0.334334 0.334334 1 0 1.000000 +0 0.000000 0.000000 0.070000 -0.706345 -0.706344 -0.046407 0.334334 0.664665 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.884416 0.832332 1 0 1.000000 +0 0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.877002 0.832332 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.884416 0.502002 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.664665 0.917668 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.334334 0.917668 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.334334 0.925081 1 0 1.000000 +axis_helper_white +0 -0.356750 0.000000 5.500000 -0.706344 0.706345 -0.046407 0.502002 0.498999 1 0 1.000000 +0 0.000000 0.356750 5.500000 -0.706344 0.706345 -0.046407 0.665666 0.498999 1 0 1.000000 +0 0.000000 0.000000 0.070000 -0.706344 0.706345 -0.046407 0.665666 0.168669 1 0 1.000000 +axis_helper_white +0 0.000000 0.356750 5.500000 0.706344 0.706345 -0.046407 0.498999 0.832332 1 0 1.000000 +0 0.356750 0.000000 5.500000 0.706344 0.706345 -0.046407 0.498999 0.668669 1 0 1.000000 +0 0.000000 0.000000 0.070000 0.706344 0.706345 -0.046407 0.168669 0.832332 1 0 1.000000 +axis_helper_white +0 -0.364710 0.000000 -5.500000 0.000000 0.000000 -1.000000 0.832332 0.498999 1 0 1.000000 +0 0.000000 0.364710 -5.500000 0.000000 0.000000 -1.000000 0.832332 0.335335 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 0.000000 0.000000 -1.000000 0.668669 0.498999 1 0 1.000000 +axis_helper_white +0 0.000000 0.364710 -5.500000 0.000002 0.000000 -1.000000 0.165666 0.835335 1 0 1.000000 +0 0.364710 0.000000 -5.500000 0.000002 0.000000 -1.000000 0.165666 0.998999 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 0.000002 0.000000 -1.000000 0.002002 0.998999 1 0 1.000000 +axis_helper_white +0 -0.364710 0.000000 -5.500000 -0.706310 -0.706311 0.047440 0.498999 0.168669 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 -0.706310 -0.706311 0.047440 0.498999 0.332332 1 0 1.000000 +0 0.000000 0.000000 -0.070000 -0.706310 -0.706311 0.047440 0.168669 0.332332 1 0 1.000000 +axis_helper_white +0 0.000000 -0.364710 -5.500000 0.706310 -0.706311 0.047440 0.831331 0.001001 1 0 1.000000 +0 0.364710 0.000000 -5.500000 0.706310 -0.706311 0.047440 0.667668 0.001001 1 0 1.000000 +0 0.000000 0.000000 -0.070000 0.706310 -0.706311 0.047440 0.667668 0.331331 1 0 1.000000 +axis_helper_white +0 0.364710 0.000000 -5.500000 0.706311 0.706310 0.047440 0.332332 0.165666 1 0 1.000000 +0 0.000000 0.364710 -5.500000 0.706311 0.706310 0.047440 0.332332 0.002002 1 0 1.000000 +0 0.000000 0.000000 -0.070000 0.706311 0.706310 0.047440 0.002002 0.165666 1 0 1.000000 +axis_helper_white +0 0.000000 0.364710 -5.500000 -0.706311 0.706310 0.047440 0.501001 0.664665 1 0 1.000000 +0 -0.364710 0.000000 -5.500000 -0.706311 0.706310 0.047440 0.501001 0.501001 1 0 1.000000 +0 0.000000 0.000000 -0.070000 -0.706311 0.706310 0.047440 0.831331 0.501001 1 0 1.000000 +axis_helper_white +0 -0.356750 0.000000 5.500000 -0.706345 -0.706344 -0.046407 0.998999 0.332332 1 0 1.000000 +0 0.000000 0.000000 0.070000 -0.706345 -0.706344 -0.046407 0.998999 0.002002 1 0 1.000000 +0 0.000000 -0.356750 5.500000 -0.706345 -0.706344 -0.046407 0.835335 0.332332 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 -0.000002 0.000000 -1.000000 0.832652 0.880334 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.869659 0.880334 1 0 1.000000 +0 0.000000 0.034950 -0.776880 -0.000004 0.000000 -1.000000 0.869659 0.843327 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 0.000004 0.000000 -1.000000 0.817662 0.843327 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.817662 0.880334 1 0 1.000000 +0 -0.026680 -0.021830 -0.776880 0.000002 0.000000 -1.000000 0.780654 0.880334 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 0.000002 0.000000 -1.000000 0.817662 0.895324 1 0 1.000000 +0 0.000000 0.000000 -0.776880 0.000000 0.000000 -1.000000 0.817662 0.932331 1 0 1.000000 +0 0.026680 -0.021830 -0.776880 -0.000002 0.000000 -1.000000 0.780654 0.932331 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.728657 0.932331 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.765664 0.932331 1 0 1.000000 +0 0.000000 0.034950 0.931410 0.000000 0.000000 1.000000 0.765664 0.895324 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 0.931410 0.000000 0.000000 1.000000 0.723660 0.927334 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.723660 0.890327 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.760668 0.890327 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.921656 0.843327 1 0 1.000000 +0 0.000000 -0.000000 0.931410 0.000000 0.000000 1.000000 0.921656 0.880334 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 0.000000 0.000000 1.000000 0.884649 0.880334 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 -0.905064 0.425275 0.000000 0.873999 0.832332 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 -0.905064 0.425275 0.000000 0.873999 0.502002 1 0 1.000000 +0 0.000000 0.034950 -0.776880 -0.905064 0.425275 0.000000 0.856169 0.832332 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 -0.905064 0.425275 0.000000 0.834334 0.831331 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 -0.905064 0.425275 0.000000 0.852165 0.501001 1 0 1.000000 +0 0.000000 0.034950 0.931410 -0.905064 0.425275 0.000000 0.834334 0.501001 1 0 1.000000 +axis_helper_white +0 0.000000 0.034950 -0.776880 0.905064 0.425275 0.000000 0.665666 0.894832 1 0 1.000000 +0 0.000000 0.034950 0.931410 0.905064 0.425275 0.000000 0.335335 0.894832 1 0 1.000000 +0 0.026680 -0.021830 -0.776880 0.905064 0.425275 0.000000 0.665666 0.877002 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 0.905064 0.425275 0.000000 0.664665 0.876001 1 0 1.000000 +0 0.000000 0.034950 0.931410 0.905064 0.425275 0.000000 0.334334 0.893831 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.905064 0.425275 0.000000 0.334334 0.876001 1 0 1.000000 +axis_helper_white +0 -0.356750 0.000000 5.500000 0.000000 0.000000 1.000000 0.665666 0.832332 1 0 1.000000 +0 0.000000 -0.356750 5.500000 0.000000 0.000000 1.000000 0.665666 0.668669 1 0 1.000000 +0 0.000000 0.356750 5.500000 0.000000 0.000000 1.000000 0.502002 0.832332 1 0 1.000000 +axis_helper_white +0 0.000000 0.356750 5.500000 0.000000 0.000000 1.000000 0.664665 0.667668 1 0 1.000000 +0 0.000000 -0.356750 5.500000 0.000000 0.000000 1.000000 0.501001 0.831331 1 0 1.000000 +0 0.356750 0.000000 5.500000 0.000000 0.000000 1.000000 0.501001 0.667668 1 0 1.000000 +axis_helper_white +0 0.000000 -0.356750 5.500000 0.706345 -0.706344 -0.046407 0.497998 0.334334 1 0 1.000000 +0 0.000000 0.000000 0.070000 0.706345 -0.706344 -0.046407 0.334334 0.664665 1 0 1.000000 +0 0.356750 0.000000 5.500000 0.706345 -0.706344 -0.046407 0.334334 0.334334 1 0 1.000000 +axis_helper_white +0 0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.884416 0.832332 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.884416 0.502002 1 0 1.000000 +0 -0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.877002 0.832332 1 0 1.000000 +axis_helper_white +0 -0.026680 -0.021830 -0.776880 0.000000 -1.000000 -0.000000 0.664665 0.917668 1 0 1.000000 +0 0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.334334 0.925081 1 0 1.000000 +0 -0.026680 -0.021830 0.931410 0.000000 -1.000000 -0.000000 0.334334 0.917668 1 0 1.000000 +axis_helper_white +0 0.356750 0.000000 5.500000 0.706344 0.706345 -0.046407 0.502002 0.498999 1 0 1.000000 +0 0.000000 0.000000 0.070000 0.706344 0.706345 -0.046407 0.665666 0.168669 1 0 1.000000 +0 0.000000 0.356750 5.500000 0.706344 0.706345 -0.046407 0.665666 0.498999 1 0 1.000000 +axis_helper_white +0 0.000000 0.356750 5.500000 -0.706344 0.706345 -0.046407 0.498999 0.832332 1 0 1.000000 +0 0.000000 0.000000 0.070000 -0.706344 0.706345 -0.046407 0.168669 0.832332 1 0 1.000000 +0 -0.356750 0.000000 5.500000 -0.706344 0.706345 -0.046407 0.498999 0.668669 1 0 1.000000 +axis_helper_white +0 0.364710 0.000000 -5.500000 0.000000 0.000000 -1.000000 0.832332 0.498999 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 0.000000 0.000000 -1.000000 0.668669 0.498999 1 0 1.000000 +0 0.000000 0.364710 -5.500000 0.000000 0.000000 -1.000000 0.832332 0.335335 1 0 1.000000 +axis_helper_white +0 0.000000 0.364710 -5.500000 -0.000002 0.000000 -1.000000 0.165666 0.835335 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 -0.000002 0.000000 -1.000000 0.002002 0.998999 1 0 1.000000 +0 -0.364710 0.000000 -5.500000 -0.000002 0.000000 -1.000000 0.165666 0.998999 1 0 1.000000 +axis_helper_white +0 0.364710 0.000000 -5.500000 0.706310 -0.706311 0.047440 0.498999 0.168669 1 0 1.000000 +0 0.000000 0.000000 -0.070000 0.706310 -0.706311 0.047440 0.168669 0.332332 1 0 1.000000 +0 0.000000 -0.364710 -5.500000 0.706310 -0.706311 0.047440 0.498999 0.332332 1 0 1.000000 +axis_helper_white +0 0.000000 -0.364710 -5.500000 -0.706310 -0.706311 0.047440 0.831331 0.001001 1 0 1.000000 +0 0.000000 0.000000 -0.070000 -0.706310 -0.706311 0.047440 0.667668 0.331331 1 0 1.000000 +0 -0.364710 0.000000 -5.500000 -0.706310 -0.706311 0.047440 0.667668 0.001001 1 0 1.000000 +axis_helper_white +0 -0.364710 0.000000 -5.500000 -0.706311 0.706310 0.047440 0.332332 0.165666 1 0 1.000000 +0 0.000000 0.000000 -0.070000 -0.706311 0.706310 0.047440 0.002002 0.165666 1 0 1.000000 +0 0.000000 0.364710 -5.500000 -0.706311 0.706310 0.047440 0.332332 0.002002 1 0 1.000000 +axis_helper_white +0 0.000000 0.364710 -5.500000 0.706311 0.706310 0.047440 0.501001 0.664665 1 0 1.000000 +0 0.000000 0.000000 -0.070000 0.706311 0.706310 0.047440 0.831331 0.501001 1 0 1.000000 +0 0.364710 0.000000 -5.500000 0.706311 0.706310 0.047440 0.501001 0.501001 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 -0.356750 -0.046407 -0.706344 -0.706345 0.834334 0.001001 1 0 1.000000 +0 5.500000 -0.356750 0.000000 -0.046407 -0.706344 -0.706345 0.997998 0.001001 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.046407 -0.706344 -0.706345 0.834334 0.331331 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 0.026680 -1.000000 -0.000002 0.000000 0.812665 0.838330 1 0 1.000000 +0 -0.776880 0.034950 0.000000 -1.000000 0.000000 0.000000 0.775658 0.875337 1 0 1.000000 +0 -0.776880 0.000000 0.000000 -1.000000 -0.000001 0.000000 0.775658 0.838330 1 0 1.000000 +axis_helper_white +0 -0.776880 0.034950 0.000000 -1.000000 0.000000 0.000000 0.827655 0.875337 1 0 1.000000 +0 -0.776880 -0.021830 -0.026680 -1.000000 -0.000002 0.000000 0.864662 0.838330 1 0 1.000000 +0 -0.776880 0.000000 0.000000 -1.000000 -0.000001 0.000000 0.827655 0.838330 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 -0.026680 -1.000000 -0.000002 0.000000 0.775658 0.927334 1 0 1.000000 +0 -0.776880 -0.021830 0.026680 -1.000000 -0.000002 0.000000 0.812665 0.890327 1 0 1.000000 +0 -0.776880 0.000000 0.000000 -1.000000 -0.000001 0.000000 0.775658 0.890327 1 0 1.000000 +axis_helper_white +0 0.931410 -0.021830 -0.026680 1.000000 0.000000 0.000000 0.832652 0.932331 1 0 1.000000 +0 0.931410 0.034950 -0.000000 1.000000 0.000000 0.000000 0.869659 0.895324 1 0 1.000000 +0 0.931410 -0.000000 -0.000000 1.000000 0.000000 0.000000 0.869659 0.932331 1 0 1.000000 +axis_helper_white +0 0.931410 0.034950 -0.000000 1.000000 0.000000 0.000000 0.827655 0.927334 1 0 1.000000 +0 0.931410 -0.021830 0.026680 1.000000 0.000000 0.000000 0.864662 0.890327 1 0 1.000000 +0 0.931410 -0.000000 -0.000000 1.000000 0.000000 0.000000 0.827655 0.890327 1 0 1.000000 +axis_helper_white +0 0.931410 -0.021830 0.026680 1.000000 0.000000 0.000000 0.879652 0.875337 1 0 1.000000 +0 0.931410 -0.021830 -0.026680 1.000000 0.000000 0.000000 0.916659 0.838330 1 0 1.000000 +0 0.931410 -0.000000 -0.000000 1.000000 0.000000 0.000000 0.879652 0.838330 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 -0.026680 -0.000000 0.425276 -0.905064 0.334334 0.855168 1 0 1.000000 +0 -0.776880 0.034950 0.000000 -0.000000 0.425276 -0.905064 0.334334 0.872998 1 0 1.000000 +0 0.931410 -0.021830 -0.026680 -0.000000 0.425275 -0.905064 0.664665 0.855168 1 0 1.000000 +axis_helper_white +0 -0.776880 0.034950 0.000000 -0.000000 0.425276 -0.905064 0.855168 0.831331 1 0 1.000000 +0 0.931410 0.034950 -0.000000 -0.000000 0.425275 -0.905064 0.855168 0.501001 1 0 1.000000 +0 0.931410 -0.021830 -0.026680 -0.000000 0.425275 -0.905064 0.872998 0.501001 1 0 1.000000 +axis_helper_white +0 -0.776880 0.034950 0.000000 0.000000 0.425275 0.905064 0.665666 0.915666 1 0 1.000000 +0 -0.776880 -0.021830 0.026680 0.000000 0.425275 0.905064 0.665666 0.897835 1 0 1.000000 +0 0.931410 0.034950 -0.000000 0.000000 0.425275 0.905064 0.335335 0.915666 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 0.026680 0.000000 0.425275 0.905064 0.664665 0.896834 1 0 1.000000 +0 0.931410 -0.021830 0.026680 0.000000 0.425275 0.905064 0.334334 0.896834 1 0 1.000000 +0 0.931410 0.034950 -0.000000 0.000000 0.425275 0.905064 0.334334 0.914665 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 -0.356750 1.000000 0.000000 0.000000 0.332332 0.998999 1 0 1.000000 +0 5.500000 0.356750 0.000000 1.000000 0.000000 0.000000 0.168669 0.998999 1 0 1.000000 +0 5.500000 -0.356750 0.000000 1.000000 0.000000 0.000000 0.332332 0.835335 1 0 1.000000 +axis_helper_white +0 5.500000 0.356750 0.000000 1.000000 0.000000 0.000000 0.331331 0.834334 1 0 1.000000 +0 5.500000 0.000000 0.356750 1.000000 0.000000 0.000000 0.167668 0.834334 1 0 1.000000 +0 5.500000 -0.356750 0.000000 1.000000 0.000000 0.000000 0.167668 0.997998 1 0 1.000000 +axis_helper_white +0 5.500000 -0.356750 0.000000 -0.046407 -0.706344 0.706346 0.164665 0.501001 1 0 1.000000 +0 5.500000 0.000000 0.356750 -0.046407 -0.706344 0.706346 0.001001 0.501001 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.046407 -0.706344 0.706346 0.001001 0.831331 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 0.026680 -0.000000 -1.000000 0.000000 0.334334 0.928084 1 0 1.000000 +0 -0.776880 -0.021830 -0.026680 -0.000000 -1.000000 0.000000 0.334334 0.935498 1 0 1.000000 +0 0.931410 -0.021830 0.026680 -0.000000 -1.000000 0.000000 0.664665 0.928084 1 0 1.000000 +axis_helper_white +0 -0.776880 -0.021830 -0.026680 -0.000000 -1.000000 0.000000 0.876001 0.831331 1 0 1.000000 +0 0.931410 -0.021830 -0.026680 -0.000000 -1.000000 0.000000 0.876001 0.501001 1 0 1.000000 +0 0.931410 -0.021830 0.026680 -0.000000 -1.000000 0.000000 0.883415 0.501001 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 0.356750 -0.046407 0.706345 0.706345 0.167668 0.831331 1 0 1.000000 +0 5.500000 0.356750 0.000000 -0.046407 0.706345 0.706345 0.167668 0.667668 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.046407 0.706345 0.706345 0.497998 0.667668 1 0 1.000000 +axis_helper_white +0 5.500000 0.356750 0.000000 -0.046407 0.706345 -0.706344 0.501001 0.167668 1 0 1.000000 +0 5.500000 0.000000 -0.356750 -0.046407 0.706345 -0.706344 0.664665 0.167668 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.046407 0.706345 -0.706344 0.501001 0.497998 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 0.364710 -1.000000 0.000000 0.000000 0.001001 0.834334 1 0 1.000000 +0 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.000000 0.001001 0.997998 1 0 1.000000 +0 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.000000 0.164665 0.834334 1 0 1.000000 +axis_helper_white +0 -5.500000 0.364710 0.000000 -1.000000 0.000000 0.000000 0.667668 0.497998 1 0 1.000000 +0 -5.500000 0.000000 -0.364710 -1.000000 0.000000 0.000000 0.667668 0.334334 1 0 1.000000 +0 -5.500000 -0.364710 0.000000 -1.000000 0.000000 0.000000 0.831331 0.334334 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 0.364710 0.047440 -0.706311 0.706310 0.002002 0.498999 1 0 1.000000 +0 -5.500000 -0.364710 0.000000 0.047440 -0.706311 0.706310 0.165666 0.498999 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.047440 -0.706311 0.706310 0.165666 0.168669 1 0 1.000000 +axis_helper_white +0 -5.500000 -0.364710 0.000000 0.047440 -0.706311 -0.706311 0.167668 0.331331 1 0 1.000000 +0 -5.500000 0.000000 -0.364710 0.047440 -0.706311 -0.706311 0.167668 0.167668 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.047440 -0.706311 -0.706311 0.497998 0.167668 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 -0.364710 0.047440 0.706310 -0.706311 0.001001 0.167668 1 0 1.000000 +0 -5.500000 0.364710 0.000000 0.047440 0.706310 -0.706311 0.164665 0.167668 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.047440 0.706310 -0.706311 0.001001 0.497998 1 0 1.000000 +axis_helper_white +0 -5.500000 0.364710 0.000000 0.047440 0.706311 0.706311 0.001001 0.164665 1 0 1.000000 +0 -5.500000 0.000000 0.364710 0.047440 0.706311 0.706311 0.001001 0.001001 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.047440 0.706311 0.706311 0.331331 0.001001 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 -0.356750 0.046407 -0.706344 -0.706345 0.834334 0.001001 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.046407 -0.706344 -0.706345 0.834334 0.331331 1 0 1.000000 +0 -5.500000 -0.356750 0.000000 0.046407 -0.706344 -0.706345 0.997998 0.001001 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 0.026680 1.000000 -0.000002 0.000000 0.812665 0.838330 1 0 1.000000 +0 0.776880 0.000000 0.000000 1.000000 -0.000001 0.000000 0.775658 0.838330 1 0 1.000000 +0 0.776880 0.034950 0.000000 1.000000 0.000000 0.000000 0.775658 0.875337 1 0 1.000000 +axis_helper_white +0 0.776880 0.034950 0.000000 1.000000 0.000000 0.000000 0.827655 0.875337 1 0 1.000000 +0 0.776880 0.000000 0.000000 1.000000 -0.000001 0.000000 0.827655 0.838330 1 0 1.000000 +0 0.776880 -0.021830 -0.026680 1.000000 -0.000002 0.000000 0.864662 0.838330 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 -0.026680 1.000000 -0.000002 0.000000 0.775658 0.927334 1 0 1.000000 +0 0.776880 0.000000 0.000000 1.000000 -0.000001 0.000000 0.775658 0.890327 1 0 1.000000 +0 0.776880 -0.021830 0.026680 1.000000 -0.000002 0.000000 0.812665 0.890327 1 0 1.000000 +axis_helper_white +0 -0.931410 -0.021830 -0.026680 -1.000000 0.000000 0.000000 0.832652 0.932331 1 0 1.000000 +0 -0.931410 -0.000000 -0.000000 -1.000000 0.000000 0.000000 0.869659 0.932331 1 0 1.000000 +0 -0.931410 0.034950 -0.000000 -1.000000 0.000000 0.000000 0.869659 0.895324 1 0 1.000000 +axis_helper_white +0 -0.931410 0.034950 -0.000000 -1.000000 0.000000 0.000000 0.827655 0.927334 1 0 1.000000 +0 -0.931410 -0.000000 -0.000000 -1.000000 0.000000 0.000000 0.827655 0.890327 1 0 1.000000 +0 -0.931410 -0.021830 0.026680 -1.000000 0.000000 0.000000 0.864662 0.890327 1 0 1.000000 +axis_helper_white +0 -0.931410 -0.021830 0.026680 -1.000000 0.000000 0.000000 0.879652 0.875337 1 0 1.000000 +0 -0.931410 -0.000000 -0.000000 -1.000000 0.000000 0.000000 0.879652 0.838330 1 0 1.000000 +0 -0.931410 -0.021830 -0.026680 -1.000000 0.000000 0.000000 0.916659 0.838330 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 -0.026680 0.000000 0.425276 -0.905064 0.334334 0.855168 1 0 1.000000 +0 -0.931410 -0.021830 -0.026680 0.000000 0.425275 -0.905064 0.664665 0.855168 1 0 1.000000 +0 0.776880 0.034950 0.000000 0.000000 0.425276 -0.905064 0.334334 0.872998 1 0 1.000000 +axis_helper_white +0 0.776880 0.034950 0.000000 0.000000 0.425276 -0.905064 0.855168 0.831331 1 0 1.000000 +0 -0.931410 -0.021830 -0.026680 0.000000 0.425275 -0.905064 0.872998 0.501001 1 0 1.000000 +0 -0.931410 0.034950 -0.000000 0.000000 0.425275 -0.905064 0.855168 0.501001 1 0 1.000000 +axis_helper_white +0 0.776880 0.034950 0.000000 -0.000000 0.425275 0.905064 0.665666 0.915666 1 0 1.000000 +0 -0.931410 0.034950 -0.000000 -0.000000 0.425275 0.905064 0.335335 0.915666 1 0 1.000000 +0 0.776880 -0.021830 0.026680 -0.000000 0.425275 0.905064 0.665666 0.897835 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 0.026680 -0.000000 0.425275 0.905064 0.664665 0.896834 1 0 1.000000 +0 -0.931410 0.034950 -0.000000 -0.000000 0.425275 0.905064 0.334334 0.914665 1 0 1.000000 +0 -0.931410 -0.021830 0.026680 -0.000000 0.425275 0.905064 0.334334 0.896834 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 -0.356750 -1.000000 0.000000 0.000000 0.332332 0.998999 1 0 1.000000 +0 -5.500000 -0.356750 0.000000 -1.000000 0.000000 0.000000 0.332332 0.835335 1 0 1.000000 +0 -5.500000 0.356750 0.000000 -1.000000 0.000000 0.000000 0.168669 0.998999 1 0 1.000000 +axis_helper_white +0 -5.500000 0.356750 0.000000 -1.000000 0.000000 0.000000 0.331331 0.834334 1 0 1.000000 +0 -5.500000 -0.356750 0.000000 -1.000000 0.000000 0.000000 0.167668 0.997998 1 0 1.000000 +0 -5.500000 0.000000 0.356750 -1.000000 0.000000 0.000000 0.167668 0.834334 1 0 1.000000 +axis_helper_white +0 -5.500000 -0.356750 0.000000 0.046407 -0.706344 0.706346 0.164665 0.501001 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.046407 -0.706344 0.706346 0.001001 0.831331 1 0 1.000000 +0 -5.500000 0.000000 0.356750 0.046407 -0.706344 0.706346 0.001001 0.501001 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 0.026680 0.000000 -1.000000 0.000000 0.334334 0.928084 1 0 1.000000 +0 -0.931410 -0.021830 0.026680 0.000000 -1.000000 0.000000 0.664665 0.928084 1 0 1.000000 +0 0.776880 -0.021830 -0.026680 0.000000 -1.000000 0.000000 0.334334 0.935498 1 0 1.000000 +axis_helper_white +0 0.776880 -0.021830 -0.026680 0.000000 -1.000000 0.000000 0.876001 0.831331 1 0 1.000000 +0 -0.931410 -0.021830 0.026680 0.000000 -1.000000 0.000000 0.883415 0.501001 1 0 1.000000 +0 -0.931410 -0.021830 -0.026680 0.000000 -1.000000 0.000000 0.876001 0.501001 1 0 1.000000 +axis_helper_white +0 -5.500000 0.000000 0.356750 0.046407 0.706345 0.706345 0.167668 0.831331 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.046407 0.706345 0.706345 0.497998 0.667668 1 0 1.000000 +0 -5.500000 0.356750 0.000000 0.046407 0.706345 0.706345 0.167668 0.667668 1 0 1.000000 +axis_helper_white +0 -5.500000 0.356750 0.000000 0.046407 0.706345 -0.706344 0.501001 0.167668 1 0 1.000000 +0 -0.070000 0.000000 0.000000 0.046407 0.706345 -0.706344 0.501001 0.497998 1 0 1.000000 +0 -5.500000 0.000000 -0.356750 0.046407 0.706345 -0.706344 0.664665 0.167668 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 0.364710 1.000000 0.000000 0.000000 0.001001 0.834334 1 0 1.000000 +0 5.500000 -0.364710 0.000000 1.000000 0.000000 0.000000 0.164665 0.834334 1 0 1.000000 +0 5.500000 0.364710 0.000000 1.000000 0.000000 0.000000 0.001001 0.997998 1 0 1.000000 +axis_helper_white +0 5.500000 0.364710 0.000000 1.000000 0.000000 0.000000 0.667668 0.497998 1 0 1.000000 +0 5.500000 -0.364710 0.000000 1.000000 0.000000 0.000000 0.831331 0.334334 1 0 1.000000 +0 5.500000 0.000000 -0.364710 1.000000 0.000000 0.000000 0.667668 0.334334 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 0.364710 -0.047440 -0.706311 0.706310 0.002002 0.498999 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.047440 -0.706311 0.706310 0.165666 0.168669 1 0 1.000000 +0 5.500000 -0.364710 0.000000 -0.047440 -0.706311 0.706310 0.165666 0.498999 1 0 1.000000 +axis_helper_white +0 5.500000 -0.364710 0.000000 -0.047440 -0.706311 -0.706311 0.167668 0.331331 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.047440 -0.706311 -0.706311 0.497998 0.167668 1 0 1.000000 +0 5.500000 0.000000 -0.364710 -0.047440 -0.706311 -0.706311 0.167668 0.167668 1 0 1.000000 +axis_helper_white +0 5.500000 0.000000 -0.364710 -0.047440 0.706310 -0.706311 0.001001 0.167668 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.047440 0.706310 -0.706311 0.001001 0.497998 1 0 1.000000 +0 5.500000 0.364710 0.000000 -0.047440 0.706310 -0.706311 0.164665 0.167668 1 0 1.000000 +axis_helper_white +0 5.500000 0.364710 0.000000 -0.047440 0.706311 0.706311 0.001001 0.164665 1 0 1.000000 +0 0.070000 0.000000 0.000000 -0.047440 0.706311 0.706311 0.331331 0.001001 1 0 1.000000 +0 5.500000 0.000000 0.364710 -0.047440 0.706311 0.706311 0.001001 0.001001 1 0 1.000000 +invisible +0 0.000000 0.000000 4.000000 0.000000 0.000000 1.000000 0.500000 0.500000 0 +0 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 0.000000 0.500000 0 +0 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.500000 0.000000 0 +invisible +0 4.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.500000 0 +0 0.000000 0.000000 4.000000 0.000000 0.000000 1.000000 0.500000 0.500000 0 +0 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.500000 0.000000 0 +invisible +0 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.500000 0.000000 0 +0 0.000000 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.500000 0.500000 0 +0 4.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.500000 0 +invisible +0 0.000000 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.500000 0.500000 0 +0 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.500000 0.000000 0 +0 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 0.000000 0.500000 0 +invisible +0 0.000000 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.500000 0.500000 0 +0 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 0.000000 0.500000 0 +0 0.000000 4.000000 0.000000 0.000000 1.000000 0.000000 0.500000 1.000000 0 +invisible +0 -4.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 0.000000 0.500000 0 +0 0.000000 0.000000 4.000000 0.000000 0.000000 1.000000 0.500000 0.500000 0 +0 0.000000 4.000000 0.000000 0.000000 1.000000 0.000000 0.500000 1.000000 0 +invisible +0 4.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.500000 0 +0 0.000000 4.000000 0.000000 0.000000 1.000000 0.000000 0.500000 1.000000 0 +0 0.000000 0.000000 4.000000 0.000000 0.000000 1.000000 0.500000 0.500000 0 +invisible +0 4.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.500000 0 +0 0.000000 0.000000 -4.000000 0.000000 0.000000 -1.000000 0.500000 0.500000 0 +0 0.000000 4.000000 0.000000 0.000000 1.000000 0.000000 0.500000 1.000000 0 +end diff --git a/modelsrc/editor/axis_helper_white/axis_helper_white.qc b/modelsrc/editor/axis_helper_white/axis_helper_white.qc new file mode 100644 index 000000000..c0f626bf1 --- /dev/null +++ b/modelsrc/editor/axis_helper_white/axis_helper_white.qc @@ -0,0 +1,28 @@ +// A version of the axis helper, designed to tint along with sprites etc. + +$modelname "editor/axis_helper_white.mdl" + +$bodygroup "Body" +{ + studio "axis_helper.smd" +} + + +$surfaceprop "default" + +$contents "solid" + +$illumposition 0 0 0 + +$cdmaterials "models\editor\" + +$cbox 0 0 0 0 0 0 + +$bbox -5.5 -5.5 -5.5 5.5 5.5 5.5 + +$sequence "idle" { + "idle.smd" + fadein 0.2 + fadeout 0.2 + fps 30 +} diff --git a/modelsrc/editor/axis_helper_white/helper.blend b/modelsrc/editor/axis_helper_white/helper.blend new file mode 100644 index 0000000000000000000000000000000000000000..42efe1f637c9372c50d10e86cd1060ce02e338a8 GIT binary patch literal 786588 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

>;$;$u*xe^81BTKYoxVhPM(L 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 Date: Wed, 5 Apr 2023 13:34:06 +1000 Subject: [PATCH 063/243] Add an option to specify the maximum distance for automatic combined props. --- CHANGELOG.md | 1 + src/hammeraddons/config.py | 17 ++++- src/hammeraddons/postcompiler.py | 7 ++- src/hammeraddons/propcombine.py | 105 +++++++++++++++++++------------ 4 files changed, 85 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab518322..5050dfa97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Add an option to allow the sources for compiled models to be preserved. * #210: Add `OnFinished` output to `comp_numeric_transition`. * #10: The center of the axis helper used for sprites can now be clicked on. +* Add an option to specify the maximum distance for automatic combined props. -------------------- diff --git a/src/hammeraddons/config.py b/src/hammeraddons/config.py index efe238a78..dd2d7ed6c 100644 --- a/src/hammeraddons/config.py +++ b/src/hammeraddons/config.py @@ -415,15 +415,26 @@ def packfile_filters(block: Keyvalues, kind: str) -> Iterator[re_Pattern[str]]: the combined version will be used. If negative, this will not be done. """ ) -PROPCOMBINE_AUTO_RANGE = Opt.integer( +PROPCOMBINE_MIN_AUTO_RANGE = Opt.integer( 'propcombine_auto_range', 0, """If greater than zero, combine props at least this close together.""", ) +PROPCOMBINE_MAX_AUTO_RANGE = Opt.integer_or_none( + 'propcombine_max_auto_range', + """If set, do not automatically combine props further away than this from each other.""", +) -PROPCOMBINE_MIN_CLLUSTER = Opt.integer( +PROPCOMBINE_MIN_CLUSTER = Opt.integer( 'propcombine_min_cluster', 2, """The minimum number of props required before propcombine will - bother merging them. Should be greater than 1. + bother merging them, in propcombine volumes. Should be greater than 1. + """, +) + +PROPCOMBINE_MIN_CLUSTER_AUTO = Opt.integer( + 'propcombine_min_cluster_auto', 0, + """The minimum number of props required before the automatic propcombine clustering will + merge the props. If less than or equal to 1, `propcombine_min_cluster` is used. """, ) diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index 2fd388bbd..c152bea3f 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -1,4 +1,5 @@ """Runs before VRAD, to run operations on the final BSP.""" +import math import shutil from pathlib import Path import sys @@ -221,8 +222,10 @@ async def main(argv: List[str]) -> None: qc_folders=conf.opts.get(config.PROPCOMBINE_QC_FOLDER).as_array(conv=conf.expand_path), decomp_cache_loc=decomp_cache_loc, crowbar_loc=crowbar_loc, - auto_range=conf.opts.get(config.PROPCOMBINE_AUTO_RANGE), - min_cluster=conf.opts.get(config.PROPCOMBINE_MIN_CLLUSTER), + min_auto_range=conf.opts.get(config.PROPCOMBINE_MIN_AUTO_RANGE), + max_auto_range=conf.opts.get(config.PROPCOMBINE_MAX_AUTO_RANGE) or math.inf, + min_cluster=conf.opts.get(config.PROPCOMBINE_MIN_CLUSTER), + min_cluster_auto=conf.opts.get(config.PROPCOMBINE_MIN_CLUSTER_AUTO), blacklist=conf.opts.get(config.PROPCOMBINE_BLACKLIST).as_array(), volume_tolerance=conf.opts.get(config.PROPCOMBINE_VOLUME_TOLERANCE), compile_dump=modelcompile_dump, diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 7f7707ed6..61b772b8e 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -3,6 +3,7 @@ This merges static props together, so they can be drawn with a single draw call. """ +import math from typing import ( Callable, Dict, FrozenSet, Iterable, Iterator, List, Literal, MutableMapping, Optional, Set, Tuple, @@ -952,13 +953,13 @@ def group_props_ent( def group_props_auto( prop_groups: Dict[Optional[tuple], List[StaticProp]], get_model: Callable[[str], Tuple[Optional[QC], Optional[Model]]], - dist: float, + min_dist: float, + max_dist: float, min_cluster: int, ) -> Iterator[List[StaticProp]]: """Given the groups of props, automatically find close props to merge.""" - # Each of these groups cannot be merged with other ones. - - dist_sq = dist * dist + min_dist_sq = min_dist * min_dist + max_dist_sq = max_dist * max_dist neighbours: Dict[StaticProp, Sequence[StaticProp]] = {} def find_neighbours(start: StaticProp) -> Sequence[StaticProp]: @@ -969,7 +970,7 @@ def find_neighbours(start: StaticProp) -> Sequence[StaticProp]: pass neigh = [ prop for prop in group - if (prop.origin - start.origin).mag_sq() <= dist_sq + if (prop.origin - start.origin).mag_sq() <= min_dist_sq ] neighbours[start] = neigh return neigh @@ -977,6 +978,7 @@ def find_neighbours(start: StaticProp) -> Sequence[StaticProp]: UNSET: Literal['unset'] = 'unset' NOISE: Literal['noise'] = 'noise' + # Each of these groups cannot be merged with other ones. for group in prop_groups.values(): # No point merging single/empty groups. if len(group) < 2: @@ -1018,36 +1020,55 @@ def find_neighbours(start: StaticProp) -> Sequence[StaticProp]: if type(key) is int: clusters[key].append(prop) + # We now have many potential groups, which may be extremely large. + # We want to split these up, so they don't extend too far. for cluster in clusters.values(): - total_verts = 0 - selected_props: List[StaticProp] = [] warned: bool = False - for prop in cluster: - qc, mdl = get_model(prop.model) - assert mdl is not None - total_verts += mdl.total_verts - if total_verts > MAX_VERTS: - # Make this just info level, just might be props nearby. - if not warned: - bb_min, bb_max = Vec.bbox(prop.origin for prop in cluster) - LOGGER.info( - 'Hit vert limit for auto group @ ({} - {}) with models {}' , - bb_min, bb_max, - {prop.model for prop in cluster}, - ) - warned = True - # Split the group here, create a new prop. - if len(selected_props) >= min_cluster: - yield selected_props - selected_props = [] - total_verts = mdl.total_verts - selected_props.append(prop) - - if len(selected_props) >= min_cluster: - yield selected_props - # Else, we grouped a too-small number of props - deliberately forget about them, they're - # likely isolated, meaning checking them in future is pointless. The main combine() - # function will re-add them to the map. + todo = set(cluster) + while len(todo) > min_cluster: + # First find the prop the furthest from the center-point. + average_pos = sum((prop.origin for prop in todo), Vec()) / len(todo) + central_prop = max(todo, key=lambda prop: (prop.origin - average_pos).mag_sq()) + + total_verts = 0 + selected_props: List[StaticProp] = [] + found_matches = False + for prop in list(todo): + # Exceeds the max radius? + if (prop.origin - central_prop.origin).mag_sq() > max_dist_sq: + continue + qc, mdl = get_model(prop.model) + assert mdl is not None + total_verts += mdl.total_verts + if total_verts > MAX_VERTS: + # Make this just info level, just might be props nearby. + if not warned: + bb_min, bb_max = Vec.bbox(prop.origin for prop in cluster) + LOGGER.info( + 'Hit vert limit for auto group @ ({} - {}) with models {}' , + bb_min, bb_max, + {prop.model for prop in cluster}, + ) + warned = True + # Split the group here, create a new prop. + if len(selected_props) >= min_cluster: + found_matches = True + todo.difference_update(selected_props) + yield selected_props + selected_props = [] + total_verts = mdl.total_verts + selected_props.append(prop) + + if len(selected_props) >= min_cluster: + yield selected_props + todo.difference_update(selected_props) + found_matches = True + if not found_matches: + # The selected prop was too far away to cluster. Discard it, so we pick a + # different one. It should be added by itself, it's on its own mostly. + todo.discard(central_prop) + # Once the while loop terminates, our group is too small to actually cluster any more. + # The main combine() function will re-add them to the map automatically. async def combine( @@ -1062,8 +1083,10 @@ async def combine( decomp_cache_loc: Optional[Path]=None, compile_dump: Optional[Path]=None, blacklist: Iterable[str]=(), - auto_range: float=0, + min_auto_range: float=0.0, + max_auto_range: float=math.inf, min_cluster: int=2, + min_cluster_auto: int=0, volume_tolerance: float=1.0, debug_dump: bool=False, pack_models: bool=True, @@ -1198,7 +1221,9 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: final_props: List[StaticProp] = [] grouper: Iterator[List[StaticProp]] grouper_ents = list(bsp_ents.by_class['comp_propcombine_set'] | bsp_ents.by_class['comp_propcombine_volume']) - if grouper_ents and auto_range > 0: + if min_cluster_auto <= 2: + min_cluster_auto = min_cluster + if grouper_ents and min_auto_range > 0: LOGGER.info('{} propcombine sets present and auto-grouping enabled, combining...', len(grouper_ents)) # Do ents first, that removes values from the lists in prop_groups, # then the auto grouper handles that. @@ -1212,8 +1237,8 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: group_props_auto( prop_groups, get_model, - auto_range, - min_cluster, + min_auto_range, max_auto_range, + min_cluster_auto or min_cluster, ) ) elif grouper_ents: @@ -1224,13 +1249,13 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: bsp.bmodels, grouper_ents, min_cluster, ) - elif auto_range > 0: + elif min_auto_range > 0: LOGGER.info('Automatically finding propcombine sets...') grouper = group_props_auto( prop_groups, get_model, - auto_range, - min_cluster, + min_auto_range, max_auto_range, + min_cluster_auto or min_cluster, ) else: # No way provided to choose props. From a1dd584647d4a8b2280bea1621a8bc749dc3a239 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Apr 2023 13:34:38 +1000 Subject: [PATCH 064/243] Allow combining models containing $collisionjoints. --- CHANGELOG.md | 1 + src/hammeraddons/propcombine.py | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5050dfa97..ce7a91a93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * #210: Add `OnFinished` output to `comp_numeric_transition`. * #10: The center of the axis helper used for sprites can now be clicked on. * Add an option to specify the maximum distance for automatic combined props. +* Allow combining models containing `$collisionjoints`. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 61b772b8e..168378ee1 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -642,12 +642,12 @@ def parse_qc(qc_loc: Path, qc_path: Path) -> Optional[Tuple[ elif body_type is not Token.NEWLINE: raise tok.error(body_type) - elif token_value == '$collisionmodel': + elif token_value in ('$collisionmodel', '$collisionjoints'): phy_smd = qc_loc / tok.expect(Token.STRING) phy_scale = scale_factor next_typ, next_val = next(tok.skipping_newlines()) if next_typ is Token.BRACE_OPEN: - for body_value in tok.block('$collisionmodel', consume_brace=False): + for body_value in tok.block(token_value, consume_brace=False): if body_value.casefold() == '$concave': is_concave = True else: @@ -655,7 +655,6 @@ def parse_qc(qc_loc: Path, qc_path: Path) -> Optional[Tuple[ # We can't support this. elif token_value in ( - '$collisionjoints', '$ikchain', '$weightlist', '$poseparameter', From b11d2c894fdd4180294e00ed7f505bd9c4c3f639 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 4 Apr 2023 22:20:13 -0700 Subject: [PATCH 065/243] Cleanup func_tracktrain i/o game support --- fgd/brush/func/func_tracktrain.fgd | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/fgd/brush/func/func_tracktrain.fgd b/fgd/brush/func/func_tracktrain.fgd index 91486cf61..0fd7fdc97 100644 --- a/fgd/brush/func/func_tracktrain.fgd +++ b/fgd/brush/func/func_tracktrain.fgd @@ -3,21 +3,25 @@ "NOTE: Build your train so that the front of the train is facing down the X axis. " + "When it spawns it will automatically rotate to face the next path_track on the path." [ + // These kvs/inputs were added to the 2007 branch post-release + // for TF2's Payload mode, but didn't make it into any later games. manualspeedchanges[until_L4D](boolean) : "Manual Train Speed" : "0" : "Train Speed is controlled through IO, handles accel, decel times." manualaccelspeed[until_L4D](float) : "Manual Accel Speed" : "0" : "Units per second to accelerate to target speed." manualdecelspeed[until_L4D](float) : "Manual Decel Speed" : "0" : "Units per second to decelerate to target speed." - // Inputs - input SetSpeedDirAccel[until_L4D](float) : "Accel/Decel to the specified speed, as a ratio of max speed. Negative values reverse the direction [-1, 1]" - // Todo - when did this get renamed?? input TeleportToPathTrack[until_L4D](string) : "Teleport train to the designated path track. This can be in a new path." - input TeleportToPathNode[since_L4D](string) : "Teleport to a destination and stop there. This can be in a new path." - input SetSpeedForwardModifier[!ASW, !L4D2](float) : "Applies the given modifier to all forward speeds. [0, 1]" - input MoveToPathNode[!L4D2](string) : "Start moving to a destination and stop when you get there. This must be in the same path." - input LockOrientation[P2](void) : "Lock the current orientation of the train." - input UnlockOrientation[P2](void) : "Unlock the current orientation of the train." - input SetMaxSpeed[P2](float) : "Set a new max speed for the train." + input SetSpeedForwardModifier[until_L4D](float) : "Applies the given modifier to all forward speeds. [0, 1]" + + // This specific input did get ported in L4D2 + input SetSpeedDirAccel[!L4D](float) : "Accel/Decel to the specified speed, as a ratio of max speed. Negative values reverse the direction [-1, 1]" + + // Portal 2 inputs, also in CSGO + input TeleportToPathNode[since_P2](string) : "Teleport to a destination and stop there. This can be in a new path." + input MoveToPathNode[since_P2](string) : "Start moving to a destination and stop when you get there. This must be in the same path." + input LockOrientation[since_P2](void) : "Lock the current orientation of the train." + input UnlockOrientation[since_P2](void) : "Unlock the current orientation of the train." + input SetMaxSpeed[since_P2](float) : "Set a new max speed for the train." input SetVelocityType[MESA](integer) : "" input EnableControls[MESA](void) : "" @@ -25,9 +29,9 @@ // Outputs output OnStart(void) : "Fired when the train starts moving in either direction." + // The default FGD mislabels this as OnNext output OnNextPoint(string) : "Fires continuously every frame when the train is moving to its next destination." - output OnArrivedAtDestinationNode[P2](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." - output OnNext[L4D2](string) : "Fires when this train picks a new point to move towards (and just after OnStart)." + output OnArrivedAtDestinationNode[since_P2](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." @resources [] ] From 620d90607b490179e6ce13f7d40017a63ab88c06 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 4 Apr 2023 22:25:59 -0700 Subject: [PATCH 066/243] These should be in base train --- fgd/bases/BaseTrain.fgd | 30 +++++++++++++++++++++++++++++ fgd/brush/func/func_tracktrain.fgd | 31 ------------------------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/fgd/bases/BaseTrain.fgd b/fgd/bases/BaseTrain.fgd index 99cedac0f..f46dbaea2 100644 --- a/fgd/bases/BaseTrain.fgd +++ b/fgd/bases/BaseTrain.fgd @@ -53,6 +53,12 @@ movesoundmintime(float) : "Min move sound interval" : 0 : "Minimum interval at which to play the move ping sound." movesoundmaxtime(float) : "Max move sound interval" : 0 : "Maximum interval at which to play the move ping sound." + // Added to 2007 post-release for TF2 Payload mode, didn't make it into later games + manualspeedchanges[until_L4D](boolean) : "Manual Train Speed" : 0 : "Train Speed is controlled through IO, handles accel, decel times." + + manualaccelspeed[until_L4D](float) : "Manual Accel Speed" : 0 : "Units per second to accelerate to target speed." + manualdecelspeed[until_L4D](float) : "Manual Decel Speed" : 0 : "Units per second to decelerate to target speed." + // Inputs input SetSpeed(float) : "Set the speed of the train, as a ratio of max speed [0, 1]" input SetSpeedDir(float) : "Set the speed of the train, as a ratio of max speed. Negative values reverse the direction [-1, 1]" @@ -63,4 +69,28 @@ input Resume(void) : "Resume the train moving in the current direction after it was stopped via the 'Stop' or 'Toggle' input." input Reverse(void) : "Reverse the direction of the train." input Toggle(void) : "Toggle the train between start and stop." + + // Added to 2007 post-release for TF2 Payload mode, didn't make it into later games + input TeleportToPathTrack[until_L4D](string) : "Teleport train to the designated path track. This can be in a new path." + input SetSpeedForwardModifier[until_L4D](float) : "Applies the given modifier to all forward speeds. [0, 1]" + + // This specific input did get ported in L4D2 + input SetSpeedDirAccel[!L4D](float) : "Accel/Decel to the specified speed, as a ratio of max speed. Negative values reverse the direction [-1, 1]" + + // Portal 2 inputs, also in CSGO + input TeleportToPathNode[since_P2](string) : "Teleport to a destination and stop there. This can be in a new path." + input MoveToPathNode[since_P2](string) : "Start moving to a destination and stop when you get there. This must be in the same path." + input LockOrientation[since_P2](void) : "Lock the current orientation of the train." + input UnlockOrientation[since_P2](void) : "Unlock the current orientation of the train." + input SetMaxSpeed[since_P2](float) : "Set a new max speed for the train." + + input SetVelocityType[MESA](integer) : "" + input EnableControls[MESA](void) : "" + input DisableControls[MESA](void) : "" + + // Outputs + output OnStart(void) : "Fired when the train starts moving in either direction." + // The default FGD mislabels this as OnNext + output OnNextPoint(string) : "Fires continuously every frame when the train is moving to its next destination." + output OnArrivedAtDestinationNode[since_P2](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." ] diff --git a/fgd/brush/func/func_tracktrain.fgd b/fgd/brush/func/func_tracktrain.fgd index 0fd7fdc97..2352a0f93 100644 --- a/fgd/brush/func/func_tracktrain.fgd +++ b/fgd/brush/func/func_tracktrain.fgd @@ -3,35 +3,4 @@ "NOTE: Build your train so that the front of the train is facing down the X axis. " + "When it spawns it will automatically rotate to face the next path_track on the path." [ - // These kvs/inputs were added to the 2007 branch post-release - // for TF2's Payload mode, but didn't make it into any later games. - manualspeedchanges[until_L4D](boolean) : "Manual Train Speed" : "0" : "Train Speed is controlled through IO, handles accel, decel times." - - manualaccelspeed[until_L4D](float) : "Manual Accel Speed" : "0" : "Units per second to accelerate to target speed." - manualdecelspeed[until_L4D](float) : "Manual Decel Speed" : "0" : "Units per second to decelerate to target speed." - - input TeleportToPathTrack[until_L4D](string) : "Teleport train to the designated path track. This can be in a new path." - input SetSpeedForwardModifier[until_L4D](float) : "Applies the given modifier to all forward speeds. [0, 1]" - - // This specific input did get ported in L4D2 - input SetSpeedDirAccel[!L4D](float) : "Accel/Decel to the specified speed, as a ratio of max speed. Negative values reverse the direction [-1, 1]" - - // Portal 2 inputs, also in CSGO - input TeleportToPathNode[since_P2](string) : "Teleport to a destination and stop there. This can be in a new path." - input MoveToPathNode[since_P2](string) : "Start moving to a destination and stop when you get there. This must be in the same path." - input LockOrientation[since_P2](void) : "Lock the current orientation of the train." - input UnlockOrientation[since_P2](void) : "Unlock the current orientation of the train." - input SetMaxSpeed[since_P2](float) : "Set a new max speed for the train." - - input SetVelocityType[MESA](integer) : "" - input EnableControls[MESA](void) : "" - input DisableControls[MESA](void) : "" - - // Outputs - output OnStart(void) : "Fired when the train starts moving in either direction." - // The default FGD mislabels this as OnNext - output OnNextPoint(string) : "Fires continuously every frame when the train is moving to its next destination." - output OnArrivedAtDestinationNode[since_P2](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." - - @resources [] ] From e2c091b718a616e2fc128480f8e084af18af7a0b Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 4 Apr 2023 23:23:07 -0700 Subject: [PATCH 067/243] These seem to be in every game actually --- fgd/bases/BaseTrain.fgd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fgd/bases/BaseTrain.fgd b/fgd/bases/BaseTrain.fgd index f46dbaea2..c44e9e9d2 100644 --- a/fgd/bases/BaseTrain.fgd +++ b/fgd/bases/BaseTrain.fgd @@ -53,11 +53,11 @@ movesoundmintime(float) : "Min move sound interval" : 0 : "Minimum interval at which to play the move ping sound." movesoundmaxtime(float) : "Max move sound interval" : 0 : "Maximum interval at which to play the move ping sound." - // Added to 2007 post-release for TF2 Payload mode, didn't make it into later games - manualspeedchanges[until_L4D](boolean) : "Manual Train Speed" : 0 : "Train Speed is controlled through IO, handles accel, decel times." + // Not in the default FGD post-Left 4 Dead, but seems to exist in engine + manualspeedchanges(boolean) : "Manual Train Speed" : 0 : "Train Speed is controlled through IO, handles accel, decel times." - manualaccelspeed[until_L4D](float) : "Manual Accel Speed" : 0 : "Units per second to accelerate to target speed." - manualdecelspeed[until_L4D](float) : "Manual Decel Speed" : 0 : "Units per second to decelerate to target speed." + manualaccelspeed(float) : "Manual Accel Speed" : 0 : "Units per second to accelerate to target speed." + manualdecelspeed(float) : "Manual Decel Speed" : 0 : "Units per second to decelerate to target speed." // Inputs input SetSpeed(float) : "Set the speed of the train, as a ratio of max speed [0, 1]" From 641dab4506eeb0674f5c13915fc3f869e79b6408 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 5 Apr 2023 18:12:43 -0700 Subject: [PATCH 068/243] Fix dumb camera output descriptions --- fgd/point/npc/npc_security_camera.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/npc/npc_security_camera.fgd b/fgd/point/npc/npc_security_camera.fgd index 6667b04c8..07b26fbfe 100644 --- a/fgd/point/npc/npc_security_camera.fgd +++ b/fgd/point/npc/npc_security_camera.fgd @@ -45,8 +45,8 @@ input LookAllTeams[P2](void) : "Make the camera follow all players." // Outputs - output OnDeploy(void) : "Camera is becoming active and dangerous." - output OnRetire(void) : "Camera is becoming inactive and harmless." + output OnDeploy(void) : "Fired in response to the Enable input" + output OnRetire(void) : "Fired in response to the Disable input" output OnTaunted[P2](void) : "A player STARTED taunting the camera." output OnTauntedBlue[P2](void) : "ATLAS STARTED taunting the camera." From e4c620e57af723f11dee8b44312dfa9896b74181 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 5 Apr 2023 20:39:37 -0700 Subject: [PATCH 069/243] Remove nonfunctional env_portal_path_track stuff --- fgd/point/env/env_portal_path_track.fgd | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/fgd/point/env/env_portal_path_track.fgd b/fgd/point/env/env_portal_path_track.fgd index bfe2accd8..d685a4921 100644 --- a/fgd/point/env/env_portal_path_track.fgd +++ b/fgd/point/env/env_portal_path_track.fgd @@ -4,14 +4,7 @@ = env_portal_path_track: "An unused variant of path_track, presumably intended for Unstationary Scaffolds. " + "It produces a beam between each node, as well as (somewhat buggy) endpoint effects which are based on the entity's angles." [ - track_beam_scale(float) : "Scale Track FX" : 0 : "The amount to scale the track FX size. Appears non-functional." - end_point_scale(float) : "Scale Endpoint FX" : 0 : "The amount to scale the endpoint FX size. Appears non-functional." - end_point_fadeout(float) : "Fade Out Endpoint" : 0 : "Amount of time to fade out the endpoint FX. Appears non-functional." - end_point_fadein(float) : "Fade In Endpoint" : 0 : "Amount of time to fade in the endpoint FX. Appears non-functional." - - // Inputs + // Most of the keyvalues and inputs in the original FGD are disabled in code input ActivateTrackFX(void) : "Enable the track beam FX." - input ActivateEndPointFX(void) : "Enable the endpoint FX. Appears non-functional." input DeactivateTrackFX(void) : "Disable the track beam FX." - input DeactivateEndPointFX(void) : "Disable the endpoint FX. Appears non-functional." ] From 9a2d75156ad6006c527fe0af68a6a0ea0d5105bd Mon Sep 17 00:00:00 2001 From: Adr Date: Thu, 6 Apr 2023 17:41:27 +0200 Subject: [PATCH 070/243] lines, lines, LINES!!! - added more line() parameters to entities --- fgd/bases/asw_alien.fgd | 1 + fgd/bases/trigger_asw_use_area.fgd | 2 +- fgd/brush/func/func_areaportal.fgd | 3 ++- fgd/brush/func/func_commandredirect.fgd | 1 + fgd/brush/func/func_fake_worldportal.fgd | 2 ++ fgd/brush/func/func_respawnroomvisualizer.fgd | 1 + fgd/brush/func/func_tankairboatgun.fgd | 1 + fgd/brush/func/func_tanklaser.fgd | 1 + fgd/brush/info/info_changelevel.fgd | 2 +- fgd/brush/trigger/trigger_asw_computer_area.fgd | 3 +++ fgd/brush/trigger/trigger_asw_jump.fgd | 1 + fgd/point/asw/asw_spawn_group.fgd | 8 ++++++++ fgd/point/env/env_soundscape_proxy.fgd | 8 ++++++++ fgd/point/env/env_tracer.fgd | 1 + fgd/point/filter/filter_activator_involume.fgd | 1 + fgd/point/game/game_player_team.fgd | 1 + fgd/point/info/info_ladder_dismount.fgd | 1 + fgd/point/info/info_lighting_relative.fgd | 1 + fgd/point/info/info_player_view_proxy.fgd | 4 +++- fgd/point/logic/logic_script.fgd | 1 + fgd/point/point/point_glow.fgd | 2 +- fgd/point/tf/tf_glow.fgd | 1 + 22 files changed, 42 insertions(+), 5 deletions(-) diff --git a/fgd/bases/asw_alien.fgd b/fgd/bases/asw_alien.fgd index 9769d02cc..db7b152fa 100644 --- a/fgd/bases/asw_alien.fgd +++ b/fgd/bases/asw_alien.fgd @@ -1,4 +1,5 @@ @BaseClass base(BaseNPC) + line(255 255 255, targetname, moveclone) appliesto(ASW) = asw_alien: "Alien Base class." [ diff --git a/fgd/bases/trigger_asw_use_area.fgd b/fgd/bases/trigger_asw_use_area.fgd index 9dbd152d3..57b3362ae 100644 --- a/fgd/bases/trigger_asw_use_area.fgd +++ b/fgd/bases/trigger_asw_use_area.fgd @@ -1,4 +1,4 @@ -@BaseClass base(trigger_multiple) = trigger_asw_use_area: "A trigger volume that is used to define areas for using various things." +@BaseClass base(trigger_multiple) line(255 255 0, targetname, usetargetname) = trigger_asw_use_area: "A trigger volume that is used to define areas for using various things." [ spawnflags(flags) : "spawnflags" = [ diff --git a/fgd/brush/func/func_areaportal.fgd b/fgd/brush/func/func_areaportal.fgd index ec27a6684..4b960150f 100644 --- a/fgd/brush/func/func_areaportal.fgd +++ b/fgd/brush/func/func_areaportal.fgd @@ -1,5 +1,6 @@ @SolidClass base(BaseEntity) - color(0 255 255) + color(0 255 255) + line(255 255 0, targetname, target) = func_areaportal: "A portal brush used to manage visibility in maps. " + "Portals define areas, which are spaces that are connected in the map. " + "Both sides of a portal cannot touch the same area, for example, a doughnut shaped map would require at least two portals to divide the map into two areas. " + diff --git a/fgd/brush/func/func_commandredirect.fgd b/fgd/brush/func/func_commandredirect.fgd index e3eb42a47..21f31f387 100644 --- a/fgd/brush/func/func_commandredirect.fgd +++ b/fgd/brush/func/func_commandredirect.fgd @@ -1,5 +1,6 @@ @SolidClass base(BaseEntityBrush, Origin, EnableDisable) + line(255 255 255, targetname, target) appliesto(MBase) = func_commandredirect : "A brush entity that redirects the player's squad commands. Also functions as a limited trigger for the player AND their squad members." diff --git a/fgd/brush/func/func_fake_worldportal.fgd b/fgd/brush/func/func_fake_worldportal.fgd index f31a77d5e..a88711ee5 100644 --- a/fgd/brush/func/func_fake_worldportal.fgd +++ b/fgd/brush/func/func_fake_worldportal.fgd @@ -1,5 +1,7 @@ @SolidClass base(func_brush) appliesto(MBase) + line(255 255 0, targetname, target) + line(255 255 255, targetname, FogController) = func_fake_worldportal: "Used to produce perfectly reflective glass that renders world + entities. " + "Typically, you want your glass brush to have nodraw on all non-reflective surfaces " + diff --git a/fgd/brush/func/func_respawnroomvisualizer.fgd b/fgd/brush/func/func_respawnroomvisualizer.fgd index 36e5e7d12..e128102d7 100644 --- a/fgd/brush/func/func_respawnroomvisualizer.fgd +++ b/fgd/brush/func/func_respawnroomvisualizer.fgd @@ -1,4 +1,5 @@ @SolidClass base(func_brush) + line(255 255 0, targetname, respawnroomname) appliesto(TF2) = func_respawnroomvisualizer: "Brushes that become visible to enemy players when they get close. " + "Use them to mark areas that they're unable to enter (i.e. respawn rooms)." diff --git a/fgd/brush/func/func_tankairboatgun.fgd b/fgd/brush/func/func_tankairboatgun.fgd index 9d393588b..c532ef617 100644 --- a/fgd/brush/func/func_tankairboatgun.fgd +++ b/fgd/brush/func/func_tankairboatgun.fgd @@ -1,4 +1,5 @@ @SolidClass base(BaseTank) + line(255 0 0, targetname, airboat_gun_model) appliesto(EP1, EP2, HL2, P1, ASW) = func_tankairboatgun: "Airboat Gun Turret" [ shootsound[MBase](sound) : "Shoot Sound" : "" : "Plays a specific sound each time this tank fires." diff --git a/fgd/brush/func/func_tanklaser.fgd b/fgd/brush/func/func_tanklaser.fgd index e5da61f4f..df90eab8f 100644 --- a/fgd/brush/func/func_tanklaser.fgd +++ b/fgd/brush/func/func_tanklaser.fgd @@ -1,4 +1,5 @@ @SolidClass base(BaseTank) + line(255 255 0, targetname, laserentity) appliesto(EP1, EP2, HL2, P1, ASW) = func_tanklaser: "Brush Laser Turret" [ laserentity(target_destination) : "env_laser Entity" diff --git a/fgd/brush/info/info_changelevel.fgd b/fgd/brush/info/info_changelevel.fgd index 530cb70ac..8051ed96c 100644 --- a/fgd/brush/info/info_changelevel.fgd +++ b/fgd/brush/info/info_changelevel.fgd @@ -1,4 +1,4 @@ -@SolidClass base(BaseEntityBrush) appliesto(L4D, L4D2) +@SolidClass base(BaseEntityBrush) line(255 255 255, targetname, landmark) appliesto(L4D, L4D2) = info_changelevel: "An entity that marks a level change.\n" + "Place an info_landmark in both maps that marks the 'same' location in each map.\n" + "TIPS & TRICKS: To fire events in the next level, use the OnLevelChange output to turn on an env_global in the current level. " + diff --git a/fgd/brush/trigger/trigger_asw_computer_area.fgd b/fgd/brush/trigger/trigger_asw_computer_area.fgd index 0448e7f83..5b3ce9a27 100644 --- a/fgd/brush/trigger/trigger_asw_computer_area.fgd +++ b/fgd/brush/trigger/trigger_asw_computer_area.fgd @@ -1,5 +1,8 @@ @SolidClass base(trigger_asw_use_area) + line(255 255 255, targetname, panelpropname) + line(255 255 255, targetname, securitycam1name) + line(255 255 255, targetname, turret1name) appliesto(ASW) = trigger_asw_computer_area: "A trigger volume in which marines can use a computer." [ locked[engine](boolean) : "Locked" : 1 diff --git a/fgd/brush/trigger/trigger_asw_jump.fgd b/fgd/brush/trigger/trigger_asw_jump.fgd index c41624362..73dc6e064 100644 --- a/fgd/brush/trigger/trigger_asw_jump.fgd +++ b/fgd/brush/trigger/trigger_asw_jump.fgd @@ -1,4 +1,5 @@ @SolidClass base(Trigger) + line(255 255 0, targetname, jumpdestname) appliesto(ASW) = trigger_asw_jump: "A trigger volume that causes Swarm Drones to jump when they come into contact with it" [ jumpdestname(target_destination) : "Jump Destination Name" : : "The name of the item Drones should jump to (use an info_target)." diff --git a/fgd/point/asw/asw_spawn_group.fgd b/fgd/point/asw/asw_spawn_group.fgd index d97a73452..fd85be1c8 100644 --- a/fgd/point/asw/asw_spawn_group.fgd +++ b/fgd/point/asw/asw_spawn_group.fgd @@ -1,5 +1,13 @@ @PointClass base(BaseEntityPoint) + line(255 255 255, targetname, spawnername00) + line(255 255 255, targetname, spawnername01) + line(255 255 255, targetname, spawnername02) + line(255 255 255, targetname, spawnername03) + line(255 255 255, targetname, spawnername04) + line(255 255 255, targetname, spawnername05) + line(255 255 255, targetname, spawnername06) + line(255 255 255, targetname, spawnername07) appliesto(ASW) = asw_spawn_group: "The named spawners will belong to this spawn group. In holdout mode, spawning is done via spawn groups rather than individual spawners." [ spawnername00(target_destination) : "Spawner 01" : : "The name of one or more spawners." diff --git a/fgd/point/env/env_soundscape_proxy.fgd b/fgd/point/env/env_soundscape_proxy.fgd index fa5e2ae3c..b944fe1a4 100644 --- a/fgd/point/env/env_soundscape_proxy.fgd +++ b/fgd/point/env/env_soundscape_proxy.fgd @@ -2,6 +2,14 @@ sphere() autovis(Sounds, Soundscapes) line(255 255 255, targetname, MainSoundscapeName) + line(128 128 128, targetname, position0) + line(128 128 128, targetname, position1) + line(128 128 128, targetname, position2) + line(128 128 128, targetname, position3) + line(128 128 128, targetname, position4) + line(128 128 128, targetname, position5) + line(128 128 128, targetname, position6) + line(128 128 128, targetname, position7) iconsprite("editor/env_soundscape_proxy.vmt") = env_soundscape_proxy: "An entity that acts like a soundscape but gets all of its sound parameters from another env_soundscape entity." [ diff --git a/fgd/point/env/env_tracer.fgd b/fgd/point/env/env_tracer.fgd index 97d0182f5..b1b165bf6 100644 --- a/fgd/point/env/env_tracer.fgd +++ b/fgd/point/env/env_tracer.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint) +line(255 255 255, targetname, target) appliesto(ASW, EP1, EP2, HL2, MBase, P1, CSGO, Mesa) = env_tracer: "An entity that fires a tracer between itself and a specified target, with a configurable delay." [ target(target_destination) : "Target" : : "The target to shoot a tracer at." diff --git a/fgd/point/filter/filter_activator_involume.fgd b/fgd/point/filter/filter_activator_involume.fgd index fac439ceb..7efa750bb 100644 --- a/fgd/point/filter/filter_activator_involume.fgd +++ b/fgd/point/filter/filter_activator_involume.fgd @@ -3,6 +3,7 @@ appliesto(MBase) iconsprite("editor/filter_involume.vmt") line(255 255 255, targetname, target) + line(0 255 255, targetname, tester) = filter_activator_involume : "A filter that tests whether the activator is within a volume." [ target(target_destination) : "Volume" : : "The volume to be tested with. This filter passes if the activator is within this volume. " + diff --git a/fgd/point/game/game_player_team.fgd b/fgd/point/game/game_player_team.fgd index 6a1a3cb2f..7d36a752f 100644 --- a/fgd/point/game/game_player_team.fgd +++ b/fgd/point/game/game_player_team.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint, MasterEnt) iconsprite("editor/ficool2/game_player_team") + line(255 255 0, targetname, target) color(200 0 0) = game_player_team: "An entity that changes the team of the player who activates it." [ diff --git a/fgd/point/info/info_ladder_dismount.fgd b/fgd/point/info/info_ladder_dismount.fgd index 1ee22bc9e..fa58007d8 100644 --- a/fgd/point/info/info_ladder_dismount.fgd +++ b/fgd/point/info/info_ladder_dismount.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint) size(-16 -16 0, 16 16 4) + line(255 255 255, targetname, target) color(255 128 255) = info_ladder_dismount: "An entity to handle endpoints for multiple ladders that are too close to each other." [ diff --git a/fgd/point/info/info_lighting_relative.fgd b/fgd/point/info/info_lighting_relative.fgd index c8c3f9226..637145a83 100644 --- a/fgd/point/info/info_lighting_relative.fgd +++ b/fgd/point/info/info_lighting_relative.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(until_L4D, +complete) + line(255 255 255, targetname, lightinglandmark) iconsprite("editor/ficool2/info_lighting_relative.vmt") halfgridsnap = info_lighting_relative diff --git a/fgd/point/info/info_player_view_proxy.fgd b/fgd/point/info/info_player_view_proxy.fgd index a08e06733..49334a281 100644 --- a/fgd/point/info/info_player_view_proxy.fgd +++ b/fgd/point/info/info_player_view_proxy.fgd @@ -1,5 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(MBase) - iconsprite("editor/vizzys/info_player_view_proxy.vmt") + line(255 255 0, targetname, MeasureReference) + line(255 255 0, targetname, TargetReference) + iconsprite("editor/vizzys/info_player_view_proxy.vmt") = info_player_view_proxy : "Copies a player's view as if they're at a different position. Intended to be used with script_intro." [ Enabled(boolean) : "Start Activated" : 0 : "Starts with position offsetting enabled." diff --git a/fgd/point/logic/logic_script.fgd b/fgd/point/logic/logic_script.fgd index d6ac4c408..2733636f0 100644 --- a/fgd/point/logic/logic_script.fgd +++ b/fgd/point/logic/logic_script.fgd @@ -18,6 +18,7 @@ line(200 200 200, targetname, group14) line(200 200 200, targetname, group07) line(200 200 200, targetname, group15) + line(200 200 200, targetname, group16) = logic_script: "An entity that acts as a container for scripts." [ group00(target_destination) : "EntityGroup[0]" : : "If set, the specified entity will be stored in the EntityGroup array. Unused slots before the last used slot will become null." diff --git a/fgd/point/point/point_glow.fgd b/fgd/point/point/point_glow.fgd index 556ac80bf..90e9944c1 100644 --- a/fgd/point/point/point_glow.fgd +++ b/fgd/point/point/point_glow.fgd @@ -1,5 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) - line(255 255 255, targetname, damagetarget) + line(255 255 255, targetname, target) iconsprite(editor/vizzys/point_glow) appliesto(MBase) = point_glow: "Mapbase off-shoot of tf_glow." diff --git a/fgd/point/tf/tf_glow.fgd b/fgd/point/tf/tf_glow.fgd index f5b1e0e49..9ec7e2f7c 100644 --- a/fgd/point/tf/tf_glow.fgd +++ b/fgd/point/tf/tf_glow.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, EnableDisable) + line(255 255 255, targetname, target) iconsprite("editor/ficool2/tf_glow") appliesto(TF2) = tf_glow [ From 1740b0a8bd76bb49f1ccc8c5a0d3ceac9bc9afcf Mon Sep 17 00:00:00 2001 From: Adr Date: Fri, 7 Apr 2023 15:21:15 +0200 Subject: [PATCH 071/243] extra line keyvalues --- fgd/point/env/env_headcrabcanister.fgd | 1 + fgd/point/env/env_speaker.fgd | 3 ++- fgd/point/halloween/halloween_zapper.fgd | 3 ++- fgd/point/info/info_player_teamspawn.fgd | 1 + fgd/point/tf/tf_logic_cp_timer.fgd | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fgd/point/env/env_headcrabcanister.fgd b/fgd/point/env/env_headcrabcanister.fgd index d2f322823..e7f619b14 100644 --- a/fgd/point/env/env_headcrabcanister.fgd +++ b/fgd/point/env/env_headcrabcanister.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityAnimating) appliesto(EP1, EP2, HL2, P1) sphere(DamageRadius) + line(255 255 0, targetname, launchpositionname) studio("models/props_combine/headcrabcannister01b.mdl") = env_headcrabcanister: "Headcrab canister" [ spawnflags(flags) : "spawnflags" = diff --git a/fgd/point/env/env_speaker.fgd b/fgd/point/env/env_speaker.fgd index fb23559d7..c11338598 100644 --- a/fgd/point/env/env_speaker.fgd +++ b/fgd/point/env/env_speaker.fgd @@ -1,4 +1,5 @@ -@PointClass base(BaseEntityPoint, ResponseContext) +@PointClass base(BaseEntityPoint, ResponseContext) + line(255 255 255, targetname, target) iconsprite("editor/ambient_generic.vmt") = env_speaker: "Announcement Speaker" [ delaymin(string) : "Min Delay Between Announcements" : 15 diff --git a/fgd/point/halloween/halloween_zapper.fgd b/fgd/point/halloween/halloween_zapper.fgd index 5c3fbcfa3..4ae4ccd08 100644 --- a/fgd/point/halloween/halloween_zapper.fgd +++ b/fgd/point/halloween/halloween_zapper.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint) - appliesto(TF2) + appliesto(TF2) + line(255 255 255, targetname, touch_trigger) iconsprite("editor/bullseye.vmt") = halloween_zapper: "Halloween Hell Zapper" [ touch_trigger(target_destination) : "Custom Touch Trigger" diff --git a/fgd/point/info/info_player_teamspawn.fgd b/fgd/point/info/info_player_teamspawn.fgd index 8c642463e..925e788b5 100644 --- a/fgd/point/info/info_player_teamspawn.fgd +++ b/fgd/point/info/info_player_teamspawn.fgd @@ -4,6 +4,7 @@ studio() line(255 0 0, targetname, round_redspawn) line(0 255 0, targetname, round_bluespawn) + line(255 255 255, targetname, controlpoint) = info_player_teamspawn: "This entity marks the spawn point for Team Fortress players." [ controlpoint(target_destination) : "Associated Control Point" : : "The team_control_point associated with this spawn. " + diff --git a/fgd/point/tf/tf_logic_cp_timer.fgd b/fgd/point/tf/tf_logic_cp_timer.fgd index 7fb197bb5..b44165694 100644 --- a/fgd/point/tf/tf_logic_cp_timer.fgd +++ b/fgd/point/tf/tf_logic_cp_timer.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint, TeamNum) iconsprite("editor/ficool2/tf_logic_cp_timer") + line(0 255 0, targetname, controlpoint) appliesto(TF2) = tf_logic_cp_timer: "Control Point Timer Entity." [ controlpoint(target_destination) : "Control Point" : : "The team_control_point associated with this timer." From 8b37bf611bd6ccd97cadc0e7e8261bb06fa287da Mon Sep 17 00:00:00 2001 From: TeamSpen210 Date: Mon, 10 Apr 2023 03:55:17 +1000 Subject: [PATCH 072/243] Improve rope's Break input description --- fgd/bases/RopeKeyFrame.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/bases/RopeKeyFrame.fgd b/fgd/bases/RopeKeyFrame.fgd index b028da2e7..0ca45e60d 100644 --- a/fgd/bases/RopeKeyFrame.fgd +++ b/fgd/bases/RopeKeyFrame.fgd @@ -53,7 +53,7 @@ // Inputs input SetScrollSpeed(float) : "Set the speed at which the texture scrolls." input SetForce(vector) : "Apply a force instantaneously to the rope. The parameter should be a vector containing the force to be applied (X Y Z)." - input Break(void) : "Break the rope, if it's marked to do so." + input Break(void) : "Detach this end of the rope, causing it to fall." input SetSlack[MBase](integer) : "Set the rope's slack. (Wind may need to be enabled for changes to apply.)" input SetWidth[MBase](float) : "Set the rope's width." input SetSubdivision[MBase](integer) : "Set the rope's subdivision. (Wind may need to be enabled for changes to apply.)" From eb16d7c15c76cbad8e44d95d324e8ccbb02336c1 Mon Sep 17 00:00:00 2001 From: Adr Date: Sun, 9 Apr 2023 21:43:04 +0200 Subject: [PATCH 073/243] Final Lines (hopefully) --- fgd/brush/func/func_tankphyscannister.fgd | 3 ++- fgd/point/env/env_rock_launcher.fgd | 1 + fgd/point/filter/filter_damage_transfer.fgd | 1 + fgd/point/mapobj_cart_dispenser.fgd | 1 + fgd/point/obj/obj_dispenser.fgd | 3 ++- fgd/point/pd_dispenser.fgd | 3 ++- fgd/point/rd_robot_dispenser.fgd | 3 ++- 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fgd/brush/func/func_tankphyscannister.fgd b/fgd/brush/func/func_tankphyscannister.fgd index be0d64e8e..bb9706e8d 100644 --- a/fgd/brush/func/func_tankphyscannister.fgd +++ b/fgd/brush/func/func_tankphyscannister.fgd @@ -1,5 +1,6 @@ -@SolidClass base(BaseTank) +@SolidClass base(BaseTank) + line(255 255 255, targetname, barrel_volume) appliesto(EP1, EP2, HL2, P1, ASW) = func_tankphyscannister: "Tank which launches phys_canister entities placed inside it." [ barrel_volume(target_destination) : "Barrel Volume" : : "Name of a trigger the specifies the volume in which cannisters must be placed." diff --git a/fgd/point/env/env_rock_launcher.fgd b/fgd/point/env/env_rock_launcher.fgd index e144e13f9..64094bac2 100644 --- a/fgd/point/env/env_rock_launcher.fgd +++ b/fgd/point/env/env_rock_launcher.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) appliesto(L4D, L4D2) studio("models/editor/cone_helper.mdl") + line(255 255 255, targetname, rocktargetname) = env_rock_launcher: "Rock launcher" [ rocktargetname(target_destination) : "Target Name" : : "The name of the entity to throw the rock at." diff --git a/fgd/point/filter/filter_damage_transfer.fgd b/fgd/point/filter/filter_damage_transfer.fgd index 037f1760e..1a31d5ce2 100644 --- a/fgd/point/filter/filter_damage_transfer.fgd +++ b/fgd/point/filter/filter_damage_transfer.fgd @@ -1,6 +1,7 @@ @FilterClass base(BaseRedirectFilter) autovis(Logic, Filters) appliesto(MBase) + line(255 255 255, targetname, target) iconsprite("editor/filter_damage_transfer.vmt") = filter_damage_transfer : "Whenever an entity using this as a damage filter takes damage, that damage is transferred to another entity. " + diff --git a/fgd/point/mapobj_cart_dispenser.fgd b/fgd/point/mapobj_cart_dispenser.fgd index 5cd38e7b4..dbbe7bdb3 100644 --- a/fgd/point/mapobj_cart_dispenser.fgd +++ b/fgd/point/mapobj_cart_dispenser.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityPoint, BaseObject) + line(255 255 255, targetname, touch_trigger) appliesto(TF2) iconsprite("editor/ficool2/mapobj_cart_dispenser.vmt") = mapobj_cart_dispenser: "TF2 Dispenser" [ diff --git a/fgd/point/obj/obj_dispenser.fgd b/fgd/point/obj/obj_dispenser.fgd index 710894f64..94590804c 100644 --- a/fgd/point/obj/obj_dispenser.fgd +++ b/fgd/point/obj/obj_dispenser.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityAnimating, BaseObject) - appliesto(TF2) + appliesto(TF2) + line(255 255 255, targetname, touch_trigger) studioprop() = obj_dispenser: "TF2 Dispenser" [ diff --git a/fgd/point/pd_dispenser.fgd b/fgd/point/pd_dispenser.fgd index 3dc62f248..577e42362 100644 --- a/fgd/point/pd_dispenser.fgd +++ b/fgd/point/pd_dispenser.fgd @@ -1,4 +1,5 @@ -@PointClass base(BaseEntityPoint, BaseObject) +@PointClass base(BaseEntityPoint, BaseObject) + line(255 255 255, targetname, touch_trigger) appliesto(TF2) = pd_dispenser: "Player Destruction Leader Dispenser" [ diff --git a/fgd/point/rd_robot_dispenser.fgd b/fgd/point/rd_robot_dispenser.fgd index 198f93d4a..5b5e196f3 100644 --- a/fgd/point/rd_robot_dispenser.fgd +++ b/fgd/point/rd_robot_dispenser.fgd @@ -1,4 +1,5 @@ -@PointClass base(BaseEntityPoint, BaseObject) +@PointClass base(BaseEntityPoint, BaseObject) + line(255 255 255, targetname, touch_trigger) appliesto(TF2) = rd_robot_dispenser: "Robot Destruction Robot Dispenser Level 0.5" [ From 58bc5c862256ec5a870e4d876c08b86e63b8f8be Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Apr 2023 01:30:28 -0700 Subject: [PATCH 074/243] HL2 player inputs also apply to P1 --- fgd/point/logic/PlayerInputs.fgd | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fgd/point/logic/PlayerInputs.fgd b/fgd/point/logic/PlayerInputs.fgd index 3309a396a..4d0d00b15 100644 --- a/fgd/point/logic/PlayerInputs.fgd +++ b/fgd/point/logic/PlayerInputs.fgd @@ -1,7 +1,7 @@ @BaseClass base(BaseEntityInputs) = PlayerInputs: "The inputs that can be fired on the player, for entities that pass through those." [ - input IgnoreFallDamage[HL2](float) : "Prevent the player from taking fall damage for [n] seconds, but reset back to taking fall damage after the first impact (so players will be hurt if they bounce off what they hit)." - input IgnoreFallDamageWithoutReset[HL2](float) : "Absolutely prevent the player from taking fall damage for [n] seconds. " + input IgnoreFallDamage[HL2, P1](float) : "Prevent the player from taking fall damage for [n] seconds, but reset back to taking fall damage after the first impact (so players will be hurt if they bounce off what they hit)." + input IgnoreFallDamageWithoutReset[HL2, P1](float) : "Absolutely prevent the player from taking fall damage for [n] seconds. " input SetHUDVisibility(bool) : "Set if the player's HUD is visible or not." input SetFogController(target_destination) : "Set the current env_fog_controller entity." @@ -11,9 +11,9 @@ input HandleMapEvent(string) : "Generic hook for triggering game/mod-specific events like achievements for a player." input SetSuppressAttacks(bool) : "Prevent or re-allow the player from being able to use weapons." - input DisableFlashlight[HL2](void) : "Disable the player's flashlight." - input EnableFlashlight[HL2](void) : "Disable the player's flashlight." - input ForceDropPhysObjects[HL2](void) : "Force the player to drop any physics objects they're carrying" + input DisableFlashlight[HL2, P1](void) : "Disable the player's flashlight." + input EnableFlashlight[HL2, P1](void) : "Disable the player's flashlight." + input ForceDropPhysObjects[HL2, P1](void) : "Force the player to drop any physics objects they're carrying" input SetBodyGroup(integer) : "HACK: Sets this player's body group (from 0 - n). You'd better know what you are doing!" input Ignite(void) : "Ignite, burst into flames" @@ -21,8 +21,8 @@ input IgniteNumHitboxFires(integer) : "Ignite, with a parameter number of hitbox fires." input IgniteHitboxFireScalev(float) : "Ignite, with a parameter hitbox fire scale." - input GiveWeapon[HL2](string) : "Gives the player a weapon of the specified class name." - input DropWeapon[HL2](string) : "Causes the player to drop its current weapon in front of them." + input GiveWeapon[HL2, P1](string) : "Gives the player a weapon of the specified class name." + input DropWeapon[HL2, P1](string) : "Causes the player to drop its current weapon in front of them." input AddArmor[MBase](integer) : "Adds to the player's current armor value. " + "Total armor cannot exceed 100 unless a different value is set in logic_playerproxy." From 7b9d3ced8a303f88199313f95cfefb915d0c5dee Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Apr 2023 01:30:44 -0700 Subject: [PATCH 075/243] Fetch event data is in TF2 --- fgd/point/logic/logic_eventlistener.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/logic/logic_eventlistener.fgd b/fgd/point/logic/logic_eventlistener.fgd index 927e9b879..8bbe55f49 100644 --- a/fgd/point/logic/logic_eventlistener.fgd +++ b/fgd/point/logic/logic_eventlistener.fgd @@ -33,7 +33,7 @@ ] // TODO: CSGO-only? - fetcheventdata[since_CSGO](boolean) : "Fetch Event Data" : "0" : "If set, will write the data from the event into the table 'event_data' on this entity." + fetcheventdata[since_CSGO, TF2](boolean) : "Fetch Event Data" : "0" : "If set, will write the data from the event into the table 'event_data' on this entity." // Inputs input Enable(void) : "Enable the logic_eventlistener." From 9daa4471d69ac59eed61536d621e16f4ab002290 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Apr 2023 01:30:57 -0700 Subject: [PATCH 076/243] Clarify what logic_relay only trigger once does --- fgd/point/logic/logic_relay.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/logic/logic_relay.fgd b/fgd/point/logic/logic_relay.fgd index 9c9692831..c25874b80 100644 --- a/fgd/point/logic/logic_relay.fgd +++ b/fgd/point/logic/logic_relay.fgd @@ -7,7 +7,7 @@ [ spawnflags(flags) : "spawnflags" = [ - 1: "Only trigger once" : 0 + 1: "Only trigger once (remove after OnSpawn or OnTrigger outputs fire)" : 0 2: "Allow fast retrigger" : 0 ] From e7eedfb9fdc1e38ce6ecb98089858a6c2ed82da0 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Apr 2023 01:31:29 -0700 Subject: [PATCH 077/243] Add disable respawn flag to weapons and items --- fgd/bases/Item.fgd | 3 ++- fgd/bases/Weapon.fgd | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fgd/bases/Item.fgd b/fgd/bases/Item.fgd index 0878bebbf..27b52e53a 100644 --- a/fgd/bases/Item.fgd +++ b/fgd/bases/Item.fgd @@ -11,7 +11,8 @@ 4 : "Not puntable by Gravity Gun" : 0 [MBase] 8 : "Deny NPC pickup (reserve for player)" : 0 [MBase] 64 : "Always touchable (no obstruction checking)" : 0 [MBase] - 1073741824: "Never Respawn" : 0 [TF2] + // Technically in every game, but most won't use it + 1073741824: "Never respawn in multiplayer" : 0 [HL2DM, TF2, complete] ] powerup_model[TF2](string) : "Model" : : "Change the model to something other than the default model." diff --git a/fgd/bases/Weapon.fgd b/fgd/bases/Weapon.fgd index 2c7d47aab..b928c81cc 100644 --- a/fgd/bases/Weapon.fgd +++ b/fgd/bases/Weapon.fgd @@ -13,6 +13,8 @@ 16 : "Preserve ammo values when picked up" : 0 [MBase] 32 : "Preserve name on player pickup" : 0 [MBase] 64 : "Always touchable (no obstruction checking)" : 0 [MBase] + // Technically in every game, but most won't use it + 1073741824 : "Never respawn in multiplayer" : 0 [HL2DM, TF2, complete] ] SetAmmo1[MBase](integer) : "Ammo 1 Override" : 0 : "Overrides the amount of primary ammo this weapon has. Be sure to set 'Preserve ammo values when picked up' for this to be maintained upon pickup." From ba07859e9087d2992fb084463d9cd3f12ed74713 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 12 Apr 2023 12:31:37 +1000 Subject: [PATCH 078/243] Document that modelcompile_dump deletes files in the folder. --- src/hammeraddons/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hammeraddons/config.py b/src/hammeraddons/config.py index dd2d7ed6c..7f574b2b9 100644 --- a/src/hammeraddons/config.py +++ b/src/hammeraddons/config.py @@ -375,7 +375,8 @@ def packfile_filters(block: Keyvalues, kind: str) -> Iterator[re_Pattern[str]]: MODEL_COMPILE_DUMP = Opt.string( 'modelcompile_dump', '', """If set, models will be compiled as subfolders of this folder, instead of in a - temporary directory. + temporary directory. The specified folder will be emptied at the start of each compile, to + prevent it filling up with old model sources. Move things out that you want to keep. """) USE_COMMA_SEP = Opt.boolean_or_none( From 5bef02499691f6a3778554bfa729cdb48dce5f37 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 12 Apr 2023 13:16:20 +1000 Subject: [PATCH 079/243] Add missing bunting definition to comp_prop_cable --- CHANGELOG.md | 1 + fgd/point/comp/comp_prop_cable.fgd | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce7a91a93..ce703f8f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * #10: The center of the axis helper used for sprites can now be clicked on. * Add an option to specify the maximum distance for automatic combined props. * Allow combining models containing `$collisionjoints`. +* Add missing `bunting` keyvalue to `comp_prop_cable`. -------------------- diff --git a/fgd/point/comp/comp_prop_cable.fgd b/fgd/point/comp/comp_prop_cable.fgd index 7461537aa..6c6c1909b 100644 --- a/fgd/point/comp/comp_prop_cable.fgd +++ b/fgd/point/comp/comp_prop_cable.fgd @@ -48,7 +48,9 @@ ] u_min(float): "Width Start" : 0.0 : "The distance along the texture to start. 0 is the left/bottom side, 1 is the right/top side. This allows using only part of the texture, if it contains multiple different cable styles." u_max(float): "Width End" : 1.0 : "The distance along the texture to end. 0 is the left/bottom side, 1 is the right/top side. This allows using only part of the texture, if it contains multiple different cable styles." - + + bunting(target_destination) : "Bunting Definition" : : "Set to the name of a comp_prop_rope_bunting, to define models which will be placed at each segment across the rope." + // A selection of prop_static's keyvalues, excluding some that are rather irrelevant. linedivider_staticprop[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" From 037a7716cf629db43c3ed51be09080a341bc58be Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 12 Apr 2023 13:17:48 +1000 Subject: [PATCH 080/243] Allow bunting models to be blank, deliberately skipping a position. --- fgd/point/comp/comp_prop_rope_bunting.fgd | 2 +- transforms/geocable.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fgd/point/comp/comp_prop_rope_bunting.fgd b/fgd/point/comp/comp_prop_rope_bunting.fgd index 7983204d2..d61ff160e 100644 --- a/fgd/point/comp/comp_prop_rope_bunting.fgd +++ b/fgd/point/comp/comp_prop_rope_bunting.fgd @@ -9,7 +9,7 @@ distance(float) : "Placement Distance" : 0 : "If greater than zero, override Placement Interval, and instead place every this many units." model(studio) : "Model" : : "Specifies the model to place. This can either be an MDL which is placed as invidual prop_statics, " + - "or a SMD (relative to a game folder) which is merged into the rope model." + "or a SMD (relative to a game folder) which is merged into the rope model. Alternatively make it entirely blank to have a chance to randomly skip placing ropes." angles(angle) : "Rotation" : "0 0 0" : "Rotate the model by this much, before applying the orientation of the rope. " + "After this is applied, the X axis should be aligned with the rope direction." diff --git a/transforms/geocable.py b/transforms/geocable.py index babba5381..c43913daf 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -997,6 +997,9 @@ def place_seg_props(nodes: Iterable[Node], fsys: FileSystem, mesh: Mesh) -> Iter )) conf = rand.choice(weights) + if not conf.model: + # Deliberately skip placing a prop. + continue if conf.orient is SegPropOrient.RAND_FULL: # We cover all orientations, so pre-rotation value is irrelevant. angles = Matrix.from_angle( From 37475be043d513c577784e9cbf4d44d3ef279303 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 14 Apr 2023 13:13:49 +1000 Subject: [PATCH 081/243] Disable collision automatically on brushes used for areaportal windows --- CHANGELOG.md | 2 ++ transforms/areaportals.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 transforms/areaportals.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ce703f8f3..0136cc51e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. * Limit the size of propcombined groups to avoid hitting vertex limits. +* Weapon scripts are now packed along with the models/sounds they use. * Prevent automatically packing models specified only as Hammer previews in various entities. * Fix propcombine sometimes removing collisions entirely from component props. * Add an option to allow the sources for compiled models to be preserved. @@ -13,6 +14,7 @@ * Add an option to specify the maximum distance for automatic combined props. * Allow combining models containing `$collisionjoints`. * Add missing `bunting` keyvalue to `comp_prop_cable`. +* Areaportal windows will automatically force the brushes used to nonsolid, and clear some physics data. -------------------- diff --git a/transforms/areaportals.py b/transforms/areaportals.py new file mode 100644 index 000000000..218437937 --- /dev/null +++ b/transforms/areaportals.py @@ -0,0 +1,28 @@ +"""Optimise brushes used for areaportal windows.""" +from hammeraddons.bsp_transform import Context, trans +from srctools.logger import get_logger + +LOGGER = get_logger(__name__) + + +@trans('Optimise Areaportals') +def optimise_areaportals(ctx: Context) -> None: + """Optimise brushes used for areaportal windows. + + This forces off solidity for func_brush, and clears the physics data. + """ + for ap_ent in ctx.vmf.by_class['func_areaportalwindow']: + if ap_ent['target']: + for fade_ent in ctx.vmf.search(ap_ent['target']): + if fade_ent['classname'] in ['prop_dynamic', 'prop_dynamic_override']: + fade_ent['solid'] = '0' + elif fade_ent['classname'] in ['func_brush']: + fade_ent['solidity'] = '1' # Never Solid + + try: + bmodel = ctx.bsp.bmodels[fade_ent] + except KeyError: + continue # Model ent? + else: + # For brush models, delete the VPhysics data. + bmodel.clear_physics() From a8c23d26f24d233ac3a968c50c8b7bcc4e7f1a0d Mon Sep 17 00:00:00 2001 From: TeamSpen210 Date: Sat, 15 Apr 2023 18:11:07 +1000 Subject: [PATCH 082/243] Tweak comp_prop_rope_dynamic description --- fgd/point/comp/comp_prop_rope_dynamic.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/comp/comp_prop_rope_dynamic.fgd b/fgd/point/comp/comp_prop_rope_dynamic.fgd index d4ae7e6f8..4b146c5b3 100644 --- a/fgd/point/comp/comp_prop_rope_dynamic.fgd +++ b/fgd/point/comp/comp_prop_rope_dynamic.fgd @@ -3,7 +3,7 @@ sphere(fademaxdist) studio("models/editor/comp_prop_rope_dynamic.mdl") appliesto(srctools) -= comp_prop_rope_dynamic: "Allows using comp_prop_rope/comp_prop_cable as a dynamic prop." += comp_prop_rope_dynamic: "Allows using comp_prop_rope/comp_prop_cable as a prop_dynamic." [ spawnflags(flags) = [ @@ -11,7 +11,7 @@ ] angles(angle) readonly: "Orientation" : "0 0 0" : "The starting orientation can't be changed, simply move the nodes." - group(target_source): "Rope Group" : : "Specify the name of the group that will be compiled into this entity." + group(target_source): "Rope Group" : : "Specify the same group name as in a comp_prop_rope/comp_prop_cable. The ropes will be compiled with this entity as their origin." // Not inheriting from prop_dynamic_base, since most of the animation KVs are pointless. glowbackfacemult[L4D2](float) : "Glow backface Multiplier" : "1.0" : "What to multiply glow by on backfaces." From 5ca3c437f1c38d342363fb5113e65a1026882887 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 26 Apr 2023 12:11:22 +1000 Subject: [PATCH 083/243] Add entity shadow and shadow origin offset to lights Also split into more bases to reduce duplication See StrataSource/FGD#181. --- fgd/bases/BaseLight.fgd | 37 ++------------------------- fgd/bases/LightPattern.fgd | 28 ++++++++++++++++++++ fgd/bases/LightShadows.fgd | 12 +++++++++ fgd/point/light/light.fgd | 8 +++--- fgd/point/light/light_directional.fgd | 6 ++--- fgd/point/light/light_environment.fgd | 35 +------------------------ fgd/point/light/light_spot.fgd | 7 +++-- 7 files changed, 52 insertions(+), 81 deletions(-) create mode 100644 fgd/bases/LightPattern.fgd create mode 100644 fgd/bases/LightShadows.fgd diff --git a/fgd/bases/BaseLight.fgd b/fgd/bases/BaseLight.fgd index a4c1b1fa7..e32f803ba 100644 --- a/fgd/bases/BaseLight.fgd +++ b/fgd/bases/BaseLight.fgd @@ -1,32 +1,14 @@ -@BaseClass +@BaseClass sphere(_fifty_percent_distance) sphere(_zero_percent_distance) sphere(_distance) color(255 255 0) -= BaseLight += BaseLight: "Parameters common to light and light_spot." [ _light(color255) : "Brightness" : "255 255 255 200" : "Color and brightness of the light." _lightHDR(color255) : "BrightnessHDR" : "-1 -1 -1 1" _lightscaleHDR(float) : "BrightnessScaleHDR" : "1" : "Amount to scale the light by when compiling for HDR." - style[engine](integer) : "Appearance" : 0 - style(choices) : "Appearance" : 0 = - [ - 0 : "Normal" - 10: "Fluorescent flicker" - 2 : "Slow, strong pulse" - 11: "Slow pulse, noblack" - 5 : "Gentle pulse" - 1 : "Flicker A" - 6 : "Flicker B" - 3 : "Candle A" - 7 : "Candle B" - 8 : "Candle C" - 4 : "Fast strobe" - 9 : "Slow strobe" - 12 : "Underwater light mutation" - ] - pattern(string) : "Custom Appearance" : "" : "Set a custom pattern of light brightness for this light. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." _constant_attn(string) : "Constant" : "0" _linear_attn(string) : "Linear" : "0" _quadratic_attn(string) : "Quadratic" : "1" @@ -35,19 +17,4 @@ _hardfalloff(integer) : "Hard Falloff" : 0 : "If set, causes lights to fall to exactly zero beyond the zero percent distance. May cause unrealistic lightijng if not used carefully." _distance(integer) : "Maximum Distance" : 0 : "The distance that light is allowed to cast." - - // GMOD: GOPORT - _castentityshadow[engine](boolean) - _castentityshadow[GMod](Choices) : "Cast entity shadows" : 0 : "Objects illuminated by this light will cast a directional shadow." = - [ - 0 : "Don't affect entity shadow angles" - 1 : "Affect entity shadow angles" - ] - - // Inputs - input TurnOn(void) : "Turn the light on." - input TurnOff(void) : "The the light off." - input Toggle(void) : "Toggle the light's current state." - input SetPattern(string) : "Set a custom pattern of light brightness for this light. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." - input FadeToPattern(string) : "Fades from first value in old pattern, to first value in the new given pattern. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." ] diff --git a/fgd/bases/LightPattern.fgd b/fgd/bases/LightPattern.fgd new file mode 100644 index 000000000..bce54c37a --- /dev/null +++ b/fgd/bases/LightPattern.fgd @@ -0,0 +1,28 @@ +@BaseClass = LightPattern +[ + style[engine](integer) : "Appearance" : 0 + style(choices) : "Appearance" : 0 = + [ + 0 : "Normal" + 10: "Fluorescent flicker" + 2 : "Slow, strong pulse" + 11: "Slow pulse, noblack" + 5 : "Gentle pulse" + 1 : "Flicker A" + 6 : "Flicker B" + 3 : "Candle A" + 7 : "Candle B" + 8 : "Candle C" + 4 : "Fast strobe" + 9 : "Slow strobe" + 12 : "Underwater light mutation" + ] + pattern(string) : "Custom Appearance" : "" : "Set a custom pattern of light brightness for this light. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." + + // Inputs + input TurnOn(void) : "Turn the light on." + input TurnOff(void) : "The the light off." + input Toggle(void) : "Toggle the light's current state." + input SetPattern(string) : "Set a custom pattern of light brightness for this light. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." + input FadeToPattern(string) : "Fades from first value in old pattern, to first value in the new given pattern. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." +] diff --git a/fgd/bases/LightShadows.fgd b/fgd/bases/LightShadows.fgd new file mode 100644 index 000000000..4000955dc --- /dev/null +++ b/fgd/bases/LightShadows.fgd @@ -0,0 +1,12 @@ +@BaseClass = LightShadows +[ + // GMod backports this feature. + _castentityshadow[engine](boolean) + _castentityshadow[since_L4D, GMod](Choices) : "Cast entity shadows" : 0 : "Objects illuminated by this light can be set to cast a fake directional shadow away from this light." = + [ + 0 : "Don't affect entity shadow angles" + 1 : "Affect entity shadow angles" + ] + + _shadoworiginoffset[since_L4D, GMod](vec_dir) : "Shadow Cast Offset" : "0 0 0" : "A world-space offset that gets applied to the light origin when casting entity shadows. Useful for dealing with funny-looking shadows from very low lights: Just offset up the z axis. Default 0 0 0." +] diff --git a/fgd/point/light/light.fgd b/fgd/point/light/light.fgd index 51f616eaf..906edd09e 100644 --- a/fgd/point/light/light.fgd +++ b/fgd/point/light/light.fgd @@ -1,9 +1,7 @@ -@PointClass base(BaseEntityPoint, BaseLight) +@PointClass base(BaseEntityPoint, BaseLight, LightPattern, LightShadows) autovis(Lights, Omnidirectional Light) - light() - iconsprite("editor/light.vmt") - sphere(_distance) - line(255 255 255, targetname, target) + light() + iconsprite("editor/light.vmt") = light: "An invisible omnidirectional lightsource." [ spawnflags(flags) = diff --git a/fgd/point/light/light_directional.fgd b/fgd/point/light/light_directional.fgd index 266ac374d..19c72344d 100644 --- a/fgd/point/light/light_directional.fgd +++ b/fgd/point/light/light_directional.fgd @@ -1,10 +1,10 @@ -@PointClass base(Angles) +@PointClass base(Angles) autovis(Lights, Directional Light) color(255 255 0) - appliesto(since_L4D) + appliesto(since_L4D) iconsprite("editor/light_directional.vmt") = light_directional: "A directional light with no falloff. Similar to sunlight in light_environment." [ - pitch(integer) : "Pitch" : 0 : "The downward pitch of the light from the sun. 0 is horizontal, -90 is straight down." + pitch(angle_negative_pitch) : "Pitch" : 0 : "The downward pitch of the light from the sun. 0 is horizontal, -90 is straight down." _light(color255) : "Brightness" : "255 255 255 200" _lighthdr(color255) : "BrightnessHDR" : "-1 -1 -1 1" _lightscalehdr(float) : "BrightnessScaleHDR" : 0.7 : "Amount to scale the light by when compiling for HDR." diff --git a/fgd/point/light/light_environment.fgd b/fgd/point/light/light_environment.fgd index 3c15e24ee..3a62654da 100644 --- a/fgd/point/light/light_environment.fgd +++ b/fgd/point/light/light_environment.fgd @@ -1,4 +1,4 @@ -@PointClass base(BaseEntityPoint, Angles) +@PointClass base(BaseEntityPoint, Angles, LightPattern, LightShadows) autovis(Lights, Environment Light) iconsprite("editor/light_environment.vmt") color(255 255 0) @@ -15,38 +15,5 @@ _ambientscalehdr(float) : "AmbientScaleHDR" : 1 : "Amount to scale the ambient light by when compiling for hdr." sunspreadangle(float) : "SunSpreadAngle" : 5 : "The angular extent of the sun for casting soft shadows. Higher numbers are more diffuse. 5 is a good starting value." - style[engine](integer) : "Appearance" : 0 - style(choices) : "Appearance" : 0 = - [ - 0 : "Normal" - 10: "Fluorescent flicker" - 2 : "Slow, strong pulse" - 11: "Slow pulse, noblack" - 5 : "Gentle pulse" - 1 : "Flicker A" - 6 : "Flicker B" - 3 : "Candle A" - 7 : "Candle B" - 8 : "Candle C" - 4 : "Fast strobe" - 9 : "Slow strobe" - 12 : "Underwater light mutation" - ] - pattern(string) : "Custom Appearance" : "" : "Set a custom pattern of light brightness for this light. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." - - _castentityshadow[engine](boolean) - _castentityshadow[GMod](Choices) : "Cast entity shadows" : 0 : "Objects illuminated by this light will cast a directional shadow." = - [ - 0 : "Don't affect entity shadow angles" - 1 : "Affect entity shadow angles" - ] - - // Inputs - input TurnOn(void) : "Turn the Sun on." - input TurnOff(void) : "The the Sun off." - input Toggle(void) : "Toggle the Sun's current state." - input SetPattern(string) : "Set a custom pattern of light brightness for the Sun. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." - input FadeToPattern(string) : "Fades from first value in old pattern, to first value in the new given pattern. Pattern format is a string of characters, where 'a' is total darkness, 'z' fully bright. i.e. 'aaggnnttzz' would be a steppy fade in from dark to light." - @resources [] ] diff --git a/fgd/point/light/light_spot.fgd b/fgd/point/light/light_spot.fgd index c275f56bf..107579d81 100644 --- a/fgd/point/light/light_spot.fgd +++ b/fgd/point/light/light_spot.fgd @@ -1,8 +1,7 @@ -@PointClass base(BaseEntityPoint, BaseLight) +@PointClass base(BaseEntityPoint, BaseLight, LightPattern, LightShadows) autovis(Lights, Spotlight) - lightprop("models/editor/spot.mdl") - lightcone() - sphere(_distance) + lightprop("models/editor/spot.mdl") + lightcone() line(255 255 255, targetname, target) = light_spot: "An invisible and directional spotlight." [ From f4c2b37e27325c174651cc99891b6243985f874f Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 26 Apr 2023 15:57:10 -0700 Subject: [PATCH 084/243] Fix prop_physics_respawnable and hl2 turret appliesto --- fgd/point/npc/npc_turret_floor.fgd | 2 +- fgd/point/prop/prop_physics_respawnable.fgd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/npc/npc_turret_floor.fgd b/fgd/point/npc/npc_turret_floor.fgd index 451f498d8..88b01ed41 100644 --- a/fgd/point/npc/npc_turret_floor.fgd +++ b/fgd/point/npc/npc_turret_floor.fgd @@ -1,5 +1,5 @@ @PointClass base(BaseNPC) - appliesto(EP1, EP2, HL2, P1) + appliesto(EP1, EP2, HL2, P1, P2) studioprop("models/combine_turrets/floor_turret.mdl") frustum(_frustum_fov, _frustum_near, _frustum_far, 255 0 0, -1) autovis(Entities, NPCs, Combine, Floor Turret) diff --git a/fgd/point/prop/prop_physics_respawnable.fgd b/fgd/point/prop/prop_physics_respawnable.fgd index 1c8690eed..753870b2b 100644 --- a/fgd/point/prop/prop_physics_respawnable.fgd +++ b/fgd/point/prop/prop_physics_respawnable.fgd @@ -1,4 +1,4 @@ -@PointClass base(prop_physics) appliesto(Mesa) +@PointClass base(prop_physics) studioprop() = prop_physics_respawnable : "This class is the same as prop_physics, except it respawns after it breaks" [ From d3e10bac865df0e8bcc251afbc411658e1e1c69e Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 26 Apr 2023 16:04:01 -0700 Subject: [PATCH 085/243] prop_glados_core improvements --- fgd/point/prop/prop_glados_core.fgd | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/fgd/point/prop/prop_glados_core.fgd b/fgd/point/prop/prop_glados_core.fgd index 6e19c617e..6d10eff0b 100644 --- a/fgd/point/prop/prop_glados_core.fgd +++ b/fgd/point/prop/prop_glados_core.fgd @@ -7,15 +7,27 @@ @PointClass base(BasePropPhysics, _prop_glados_core_p1_mdl, _prop_glados_core_p2_mdl) appliesto(P1, P2) = prop_glados_core: "The P1 personality cores for GlaDOS. Resemble little eyeballs with handles. " + - "These play lines and look around when near the player. " + "These play lines and look around when near the player. " + + "Portal 2 uses the wrong core model, so this will need to be swapped back with VScript." [ coretype[engine](integer) : "Core Personality" : 1 - coretype(choices) : "Core Personality" : 1 : "Which personality VO set the core is set to." = + coretype(choices) : "Core Personality" : 3 : "Which personality the core is set to, determines the voice lines and skin." = [ - 0: "Curious" - 1: "Aggressive" - 2: "Crazy" - 3: "None" + // Ordered to match P1 boss fight + 3: "Morality Core" + 0: "Curiosity Core" + 2: "Intelligence/Cake Core" + 1: "Anger Core" + ] + + skin[engine](integer) : "Skin" : 0 + skin(choices) : "[H] Skin" : 3 : "Skin to show in Hammer." = + [ + // Ordered to match P1 boss fight + 0: "Morality Core" + 1: "Curiosity Core" + 3: "Intelligence/Cake Core" + 2: "Anger Core" ] delaybetweenlines(float) : "Pause (in secs) between VO Lines." : 0.4 : "When the core is talking, this is the number of seconds delay between it's spoken lines." From 44cf9f452a55fafe0358b1b1b46271ffb0ba45d2 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 27 Apr 2023 17:37:48 -0700 Subject: [PATCH 086/243] Remove point_survey - P2 got rid of it in an update --- fgd/point/point/point_survey.fgd | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 fgd/point/point/point_survey.fgd diff --git a/fgd/point/point/point_survey.fgd b/fgd/point/point/point_survey.fgd deleted file mode 100644 index f7890b3f5..000000000 --- a/fgd/point/point/point_survey.fgd +++ /dev/null @@ -1,8 +0,0 @@ -@PointClass base(BaseEntityPoint) appliesto(P2) = point_survey : "Displays a survey to the player." -[ - surveyname(string) : "Survey Script Name" : "end_puzzle_survey" : "Name of a survey keyvalues file, from the 'scripts/surveys' folder." - - input ShowSurvey(void) : "Displays the survey." - - output OnSurveyComplete(void) : "Fired when the player completes or cancels the survey." -] From b22b176ef379a73cd88a6dcaea4a62e528d962bf Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 3 May 2023 17:10:40 +1000 Subject: [PATCH 087/243] Fix invisible collision for axis helpers showing in some cases --- hammer/materials/models/editor/invisible.vmt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hammer/materials/models/editor/invisible.vmt b/hammer/materials/models/editor/invisible.vmt index 009ff83f2..1f14216aa 100644 --- a/hammer/materials/models/editor/invisible.vmt +++ b/hammer/materials/models/editor/invisible.vmt @@ -1,5 +1,6 @@ -WriteZ +UnlitGeneric { - // Only writes to Z buffer, so it's invisible but can be selected. - "%noToolTexture" 1 + // Don't draw this at all, but it'll be traced for selection checks. + $no_draw 1 + $model 1 } From c30bebc7b81efdce9d8c539edc046ba4dad5113b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 6 May 2023 10:07:08 +1000 Subject: [PATCH 088/243] Include `$donotcastshadows` in propcombine QCs where required --- src/hammeraddons/propcombine.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 168378ee1..938c36b56 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -426,6 +426,9 @@ async def compile_func( if ModelFlags.ambient_boost in combined_flags: f.write('$ambientboost\n') + if ModelFlags.do_not_cast_shadows in combined_flags: + f.write('$donotcastshadows\n') + for mat in sorted(cdmats): f.write('$cdmaterials "{}"\n'.format(mat)) From 8cb637586b1dc443be40db2b25d9086c9bcfdc01 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 6 May 2023 10:13:20 +1000 Subject: [PATCH 089/243] Only propcombine props inside the same areaportal areas --- CHANGELOG.md | 2 ++ src/hammeraddons/propcombine.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0136cc51e..195381661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ * Allow combining models containing `$collisionjoints`. * Add missing `bunting` keyvalue to `comp_prop_cable`. * Areaportal windows will automatically force the brushes used to nonsolid, and clear some physics data. +* Propcombine will no longer merge props found in different areaportal areas. This allows props on + the outside of a building to be culled when inside, or vice versa. -------------------- diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index 938c36b56..eb78f6d43 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -1205,6 +1205,8 @@ def get_grouping_key(prop: StaticProp) -> Optional[tuple]: model.iter_textures([prop.skin]) }), (prop.flags & relevant_flags).value, + # Do not allow combining across an areaportal boundary. + frozenset({leaf.area for leaf in prop.visleafs}), model.contents, model.surfaceprop, prop.renderfx, From 314f77ce0f0168f133dbfa9745df6b6ae604dfa9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 12 May 2023 15:28:57 +1000 Subject: [PATCH 090/243] Specifically define the weapon scripts used, instead of looking them up. This makes it work correctly when spawning weapons via other ents --- fgd/bases/Weapon.fgd | 2 -- fgd/point/weapon/weapon_357.fgd | 6 +++++- fgd/point/weapon/weapon_adrenaline_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_ak47.fgd | 1 - fgd/point/weapon/weapon_alyxgun.fgd | 5 ++++- fgd/point/weapon/weapon_ammo_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_annabelle.fgd | 5 ++++- fgd/point/weapon/weapon_ar2.fgd | 1 + fgd/point/weapon/weapon_ar2_prototype.fgd | 6 ++++++ fgd/point/weapon/weapon_autoshotgun_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_bugbait.fgd | 1 + fgd/point/weapon/weapon_chainsaw_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_citizenpackage.fgd | 5 ++++- fgd/point/weapon/weapon_citizensuitcase.fgd | 5 ++++- fgd/point/weapon/weapon_crossbow.fgd | 1 + fgd/point/weapon/weapon_crowbar.fgd | 6 +++++- fgd/point/weapon/weapon_cubemap.fgd | 5 ++++- fgd/point/weapon/weapon_custom_scripted1.fgd | 5 ++++- fgd/point/weapon/weapon_defibrillator_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_displacer_pistol.fgd | 4 ++++ fgd/point/weapon/weapon_endgame.fgd | 5 +++++ fgd/point/weapon/weapon_first_aid_kit.fgd | 4 ++++ fgd/point/weapon/weapon_first_aid_kit_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_flaregun.fgd | 4 ++++ fgd/point/weapon/weapon_flashbang.fgd | 4 ++++ fgd/point/weapon/weapon_frag.fgd | 3 +++ fgd/point/weapon/weapon_gascan_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_grenade_launcher.fgd | 4 ++++ fgd/point/weapon/weapon_grenade_launcher_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_hopwire.fgd | 1 + fgd/point/weapon/weapon_hunting_rifle_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_manhacktoss.fgd | 4 ++++ fgd/point/weapon/weapon_molotov_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_pain_pills_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_physcannon.fgd | 2 ++ fgd/point/weapon/weapon_pipe_bomb_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_pistol.fgd | 5 ++++- fgd/point/weapon/weapon_pistol_magnum_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_pistol_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_pulsepistol.fgd | 4 ++++ fgd/point/weapon/weapon_pumpshotgun_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_rifle_ak47_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_rifle_desert_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_rifle_sg552_spawn.fgd | 8 ++++++-- fgd/point/weapon/weapon_rifle_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_rpg.fgd | 1 + fgd/point/weapon/weapon_shotgun.fgd | 5 ++++- fgd/point/weapon/weapon_shotgun_chrome_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_shotgun_spas_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_slam.fgd | 1 + fgd/point/weapon/weapon_smg1.fgd | 1 + fgd/point/weapon/weapon_smg2.fgd | 4 ++++ fgd/point/weapon/weapon_smg_mp5_spawn.fgd | 8 ++++++-- fgd/point/weapon/weapon_smg_silenced_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_smg_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_sniper_awp_spawn.fgd | 8 ++++++-- fgd/point/weapon/weapon_sniper_military_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_stunstick.fgd | 1 + fgd/point/weapon/weapon_upgradepack_explosive_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_upgradepack_incendiary_spawn.fgd | 4 ++++ fgd/point/weapon/weapon_vomitjar_spawn.fgd | 4 ++++ 61 files changed, 220 insertions(+), 19 deletions(-) diff --git a/fgd/bases/Weapon.fgd b/fgd/bases/Weapon.fgd index 2c7d47aab..d4abd4eaa 100644 --- a/fgd/bases/Weapon.fgd +++ b/fgd/bases/Weapon.fgd @@ -41,7 +41,5 @@ @resources [ - // Weapons have a script file defining sounds and models used. - func weapon_script ] ] diff --git a/fgd/point/weapon/weapon_357.fgd b/fgd/point/weapon/weapon_357.fgd index 0916c3e90..9ffe7d626 100644 --- a/fgd/point/weapon/weapon_357.fgd +++ b/fgd/point/weapon/weapon_357.fgd @@ -3,5 +3,9 @@ autovis(Weapons, HL2 Weapons, Magnum Pistol) studioprop("models/weapons/w_357.mdl") = weapon_357: "357" [ - @resources [] + @resources + [ + weapon_script "scripts/gameplay/weapons/weapon_357.dmx" [+mesa] + weapon_script "scripts/weapon_357.txt" [-mesa] + ] ] diff --git a/fgd/point/weapon/weapon_adrenaline_spawn.fgd b/fgd/point/weapon/weapon_adrenaline_spawn.fgd index 60f676eca..13be5f619 100644 --- a/fgd/point/weapon/weapon_adrenaline_spawn.fgd +++ b/fgd/point/weapon/weapon_adrenaline_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, L4D Items) = weapon_adrenaline_spawn: "Adrenaline" [ + @resources + [ + weapon_script "scripts/weapon_adrenaline.txt" + ] ] diff --git a/fgd/point/weapon/weapon_ak47.fgd b/fgd/point/weapon/weapon_ak47.fgd index 1acb8d73d..9109cb9a3 100644 --- a/fgd/point/weapon/weapon_ak47.fgd +++ b/fgd/point/weapon/weapon_ak47.fgd @@ -1,4 +1,3 @@ - @PointClass base(Weapon) appliesto(CSGO) autovis(Weapons, CSGO Weapons, CS Assault Rifles) diff --git a/fgd/point/weapon/weapon_alyxgun.fgd b/fgd/point/weapon/weapon_alyxgun.fgd index e27de130d..17fb21233 100644 --- a/fgd/point/weapon/weapon_alyxgun.fgd +++ b/fgd/point/weapon/weapon_alyxgun.fgd @@ -3,5 +3,8 @@ autovis(Weapons, HL2 Weapons, Alyx's Gun) studioprop("models/weapons/W_Alyx_Gun.mdl") = weapon_alyxgun: "Alyx's Gun" [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_alyxgun.txt" + ] ] diff --git a/fgd/point/weapon/weapon_ammo_spawn.fgd b/fgd/point/weapon/weapon_ammo_spawn.fgd index a44fe8dc6..f374ef728 100644 --- a/fgd/point/weapon/weapon_ammo_spawn.fgd +++ b/fgd/point/weapon/weapon_ammo_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_ammo_spawn: "Ammo" [ + @resources + [ + weapon_script "scripts/weapon_ammo_pack.txt" + ] ] diff --git a/fgd/point/weapon/weapon_annabelle.fgd b/fgd/point/weapon/weapon_annabelle.fgd index a789ad50d..61aa5fc50 100644 --- a/fgd/point/weapon/weapon_annabelle.fgd +++ b/fgd/point/weapon/weapon_annabelle.fgd @@ -3,5 +3,8 @@ autovis(Weapons, HL2 Weapons, Annabelle) studioprop("models/weapons/W_annabelle.mdl") = weapon_annabelle: "Annabelle (Grigori)" [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_annabelle.txt" + ] ] diff --git a/fgd/point/weapon/weapon_ar2.fgd b/fgd/point/weapon/weapon_ar2.fgd index c8806d4c8..407055d54 100644 --- a/fgd/point/weapon/weapon_ar2.fgd +++ b/fgd/point/weapon/weapon_ar2.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_ar2.txt" entity prop_combine_ball entity env_entity_dissolver ] diff --git a/fgd/point/weapon/weapon_ar2_prototype.fgd b/fgd/point/weapon/weapon_ar2_prototype.fgd index a42ef7475..8813d0597 100644 --- a/fgd/point/weapon/weapon_ar2_prototype.fgd +++ b/fgd/point/weapon/weapon_ar2_prototype.fgd @@ -3,4 +3,10 @@ autovis(Weapons, EZ Weapons) studioprop("models/weapons/w_irifle.mdl") = weapon_ar2_proto: "Assault Rifle 2 Prototype" [ + @resources + [ + weapon_script "scripts/weapon_ar2_proto.txt" + entity prop_combine_ball + entity env_entity_dissolver + ] ] diff --git a/fgd/point/weapon/weapon_autoshotgun_spawn.fgd b/fgd/point/weapon/weapon_autoshotgun_spawn.fgd index b6a4f30cc..213c7a0c3 100644 --- a/fgd/point/weapon/weapon_autoshotgun_spawn.fgd +++ b/fgd/point/weapon/weapon_autoshotgun_spawn.fgd @@ -4,4 +4,8 @@ studioprop("models/w_models/Weapons/w_autoshot_m4super.mdl") = weapon_autoshotgun_spawn: "Auto Shotgun" [ + @resources + [ + weapon_script "scripts/weapon_autoshotgun.txt" + ] ] diff --git a/fgd/point/weapon/weapon_bugbait.fgd b/fgd/point/weapon/weapon_bugbait.fgd index 6a28ba0de..6430afdc1 100644 --- a/fgd/point/weapon/weapon_bugbait.fgd +++ b/fgd/point/weapon/weapon_bugbait.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_bugbait.txt" sound "Weapon_Bugbait.Splat" // Squeeze! entity npc_grenade_bugbait ] diff --git a/fgd/point/weapon/weapon_chainsaw_spawn.fgd b/fgd/point/weapon/weapon_chainsaw_spawn.fgd index c6047cca3..922fb5b7e 100644 --- a/fgd/point/weapon/weapon_chainsaw_spawn.fgd +++ b/fgd/point/weapon/weapon_chainsaw_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_chainsaw_spawn: "Chainsaw" [ + @resources + [ + weapon_script "scripts/weapon_chainsaw.txt" + ] ] diff --git a/fgd/point/weapon/weapon_citizenpackage.fgd b/fgd/point/weapon/weapon_citizenpackage.fgd index 8c88cfb10..144b9de25 100644 --- a/fgd/point/weapon/weapon_citizenpackage.fgd +++ b/fgd/point/weapon/weapon_citizenpackage.fgd @@ -2,5 +2,8 @@ appliesto(HL2, EP1, EP2, P1) studioprop("models/weapons/w_package.mdl") = weapon_citizenpackage: "A Combine ration Citizens are seen retrieving at the start of HL2." [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_citizenpackage.txt" + ] ] diff --git a/fgd/point/weapon/weapon_citizensuitcase.fgd b/fgd/point/weapon/weapon_citizensuitcase.fgd index 9d6d1ad27..4d1674c15 100644 --- a/fgd/point/weapon/weapon_citizensuitcase.fgd +++ b/fgd/point/weapon/weapon_citizensuitcase.fgd @@ -2,5 +2,8 @@ appliesto(HL2, EP1, EP2, P1) studioprop("models/weapons/w_suitcase_passenger.mdl") = weapon_citizensuitcase: "A generic suitcase carried by Citizens." [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_citizensuitcase.txt" + ] ] diff --git a/fgd/point/weapon/weapon_crossbow.fgd b/fgd/point/weapon/weapon_crossbow.fgd index 71ddfda0f..00ddbe32c 100644 --- a/fgd/point/weapon/weapon_crossbow.fgd +++ b/fgd/point/weapon/weapon_crossbow.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_crossbow.txt" material "sprites/blueflare1.vmt" material "sprites/light_glow02_noz.vmt" sound "Weapon_Crossbow.BoltHitBody" diff --git a/fgd/point/weapon/weapon_crowbar.fgd b/fgd/point/weapon/weapon_crowbar.fgd index 8aece4f9e..414fb66c1 100644 --- a/fgd/point/weapon/weapon_crowbar.fgd +++ b/fgd/point/weapon/weapon_crowbar.fgd @@ -3,5 +3,9 @@ autovis(Weapons, HL2 Weapons, Crowbar) studioprop("models/weapons/w_crowbar.mdl") = weapon_crowbar: "Crowbar" [ - @resources [] + @resources + [ + weapon_script "scripts/gameplay/weapons/weapon_crowbar.dmx" [+mesa] + weapon_script "scripts/weapon_crowbar.txt" [-mesa] + ] ] diff --git a/fgd/point/weapon/weapon_cubemap.fgd b/fgd/point/weapon/weapon_cubemap.fgd index 34093943d..8fa5915ca 100644 --- a/fgd/point/weapon/weapon_cubemap.fgd +++ b/fgd/point/weapon/weapon_cubemap.fgd @@ -3,5 +3,8 @@ appliesto(until_L4D, ASW, P2, -MESA, -TF2) studioprop("models/shadertest/envballs.mdl") = weapon_cubemap: "Debugging weapon used to show cubemaps in a region." [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_cubemap.txt" + ] ] diff --git a/fgd/point/weapon/weapon_custom_scripted1.fgd b/fgd/point/weapon/weapon_custom_scripted1.fgd index 89f101c88..ace9afaa8 100644 --- a/fgd/point/weapon/weapon_custom_scripted1.fgd +++ b/fgd/point/weapon/weapon_custom_scripted1.fgd @@ -3,4 +3,7 @@ appliesto(MBase) studioprop() sphere(fademindist) sphere(fademaxdist) -= weapon_custom_scripted1: "Scripted Weapon 1" [] += weapon_custom_scripted1: "Scripted Weapon 1" + [ + + ] diff --git a/fgd/point/weapon/weapon_defibrillator_spawn.fgd b/fgd/point/weapon/weapon_defibrillator_spawn.fgd index a88b0b9f3..a939fff6d 100644 --- a/fgd/point/weapon/weapon_defibrillator_spawn.fgd +++ b/fgd/point/weapon/weapon_defibrillator_spawn.fgd @@ -3,4 +3,8 @@ studioprop("models/w_models/weapons/w_eq_defibrillator.mdl") = weapon_defibrillator_spawn: "Defibrillator" [ + @resources + [ + weapon_script "scripts/weapon_defibrillator.txt" + ] ] diff --git a/fgd/point/weapon/weapon_displacer_pistol.fgd b/fgd/point/weapon/weapon_displacer_pistol.fgd index f6f41d9b9..06567ba29 100644 --- a/fgd/point/weapon/weapon_displacer_pistol.fgd +++ b/fgd/point/weapon/weapon_displacer_pistol.fgd @@ -3,4 +3,8 @@ autovis(Weapons, EZ Weapons) studioprop("models/weapons/w_flaregun.mdl") = weapon_displacer_pistol: "Displacer Pistol" [ + @resources + [ + weapon_script "scripts/weapon_displacer_pistol.txt" + ] ] diff --git a/fgd/point/weapon/weapon_endgame.fgd b/fgd/point/weapon/weapon_endgame.fgd index 625295585..2097c71c3 100644 --- a/fgd/point/weapon/weapon_endgame.fgd +++ b/fgd/point/weapon/weapon_endgame.fgd @@ -4,4 +4,9 @@ studioprop("models/weapons/w_xengrenade.mdl") = weapon_endgame: "Special ending Xen Grenade" [ output OnEndGame(void) : "Fired when the endgame Xen Relay Grenade is thrown." + + @resources + [ + weapon_script "scripts/weapon_endgame.txt" + ] ] diff --git a/fgd/point/weapon/weapon_first_aid_kit.fgd b/fgd/point/weapon/weapon_first_aid_kit.fgd index 3e30dc09b..9e7400dee 100644 --- a/fgd/point/weapon/weapon_first_aid_kit.fgd +++ b/fgd/point/weapon/weapon_first_aid_kit.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Scripted Weapons) = weapon_first_aid_kit: "Physics First Aid Kit - This entity is intended to be used in scripted events where a single item needs to spawned with physics active." [ + @resources + [ + weapon_script "scripts/weapon_first_aid_kit.txt" + ] ] diff --git a/fgd/point/weapon/weapon_first_aid_kit_spawn.fgd b/fgd/point/weapon/weapon_first_aid_kit_spawn.fgd index 5f0eea94c..7b9b20dfe 100644 --- a/fgd/point/weapon/weapon_first_aid_kit_spawn.fgd +++ b/fgd/point/weapon/weapon_first_aid_kit_spawn.fgd @@ -4,4 +4,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_first_aid_kit_spawn: "First Aid Kit" [ + @resources + [ + weapon_script "scripts/weapon_first_aid_kit.txt" + ] ] diff --git a/fgd/point/weapon/weapon_flaregun.fgd b/fgd/point/weapon/weapon_flaregun.fgd index d6e30243b..bea758907 100644 --- a/fgd/point/weapon/weapon_flaregun.fgd +++ b/fgd/point/weapon/weapon_flaregun.fgd @@ -3,4 +3,8 @@ autovis(Weapons, HL2 Weapons) studioprop("models/weapons/w_pistol.mdl") = weapon_flaregun : "Flaregun" [ + @resources + [ + weapon_script "scripts/weapon_flaregun.txt" + ] ] diff --git a/fgd/point/weapon/weapon_flashbang.fgd b/fgd/point/weapon/weapon_flashbang.fgd index 60c6dd960..f55dfd2b0 100644 --- a/fgd/point/weapon/weapon_flashbang.fgd +++ b/fgd/point/weapon/weapon_flashbang.fgd @@ -4,4 +4,8 @@ autovis(Weapons, CSGO Weapons, CS Equipment) studioprop("models/weapons/w_eq_flashbang_dropped.mdl") = weapon_flashbang: "Flashbang" [ + @resources + [ + weapon_script "scripts/weapon_flashbang.txt" + ] ] diff --git a/fgd/point/weapon/weapon_frag.fgd b/fgd/point/weapon/weapon_frag.fgd index dfa665881..a7791079d 100644 --- a/fgd/point/weapon/weapon_frag.fgd +++ b/fgd/point/weapon/weapon_frag.fgd @@ -5,6 +5,9 @@ [ @resources [ + weapon_script "scripts/gameplay/weapons/weapon_frag.dmx" [+mesa] + weapon_script "scripts/weapon_frag.txt" [-mesa] + sound "WeaponFrag.Throw" sound "WeaponFrag.Roll" entity npc_grenade_frag diff --git a/fgd/point/weapon/weapon_gascan_spawn.fgd b/fgd/point/weapon/weapon_gascan_spawn.fgd index 5d2ae554d..29fbbd49b 100644 --- a/fgd/point/weapon/weapon_gascan_spawn.fgd +++ b/fgd/point/weapon/weapon_gascan_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D, Weapon Spawners) = weapon_gascan_spawn: "Gas Can" [ + @resources + [ + weapon_script "scripts/weapon_gascan.txt" + ] ] diff --git a/fgd/point/weapon/weapon_grenade_launcher.fgd b/fgd/point/weapon/weapon_grenade_launcher.fgd index eaaea0ae9..a2b735ea9 100644 --- a/fgd/point/weapon/weapon_grenade_launcher.fgd +++ b/fgd/point/weapon/weapon_grenade_launcher.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Scripted Weapons) = weapon_grenade_launcher: "Grenade Launcher - This entity is intended to be used in scripted events where a single item needs to spawned with physics active." [ + @resources + [ + weapon_script "scripts/weapon_grenade_launcher.txt" + ] ] diff --git a/fgd/point/weapon/weapon_grenade_launcher_spawn.fgd b/fgd/point/weapon/weapon_grenade_launcher_spawn.fgd index b26b7927e..12894feb9 100644 --- a/fgd/point/weapon/weapon_grenade_launcher_spawn.fgd +++ b/fgd/point/weapon/weapon_grenade_launcher_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_grenade_launcher_spawn: "Grenade Launcher" [ + @resources + [ + weapon_script "scripts/weapon_grenade_launcher.txt" + ] ] diff --git a/fgd/point/weapon/weapon_hopwire.fgd b/fgd/point/weapon/weapon_hopwire.fgd index 4a660859b..1727b1b41 100644 --- a/fgd/point/weapon/weapon_hopwire.fgd +++ b/fgd/point/weapon/weapon_hopwire.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "items/weapon_hopwire.txt" // This also precaches the recipe table, not easy to do here though. sound "WeaponFrag.Throw" sound "WeaponFrag.Roll" diff --git a/fgd/point/weapon/weapon_hunting_rifle_spawn.fgd b/fgd/point/weapon/weapon_hunting_rifle_spawn.fgd index c520c50ed..b598dda57 100644 --- a/fgd/point/weapon/weapon_hunting_rifle_spawn.fgd +++ b/fgd/point/weapon/weapon_hunting_rifle_spawn.fgd @@ -4,4 +4,8 @@ studioprop("models/w_models/Weapons/w_sniper_mini14.mdl") = weapon_hunting_rifle_spawn: "Hunting Rifle" [ + @resources + [ + weapon_script "scripts/weapon_hunting_rifle.txt" + ] ] diff --git a/fgd/point/weapon/weapon_manhacktoss.fgd b/fgd/point/weapon/weapon_manhacktoss.fgd index 0a781caea..0b4a9fad0 100644 --- a/fgd/point/weapon/weapon_manhacktoss.fgd +++ b/fgd/point/weapon/weapon_manhacktoss.fgd @@ -3,4 +3,8 @@ autovis(Weapons, EZ Weapons) studioprop("models/manhack.mdl") = weapon_manhacktoss: "Deployable Manhack" [ + @resources + [ + weapon_script "scripts/weapon_manhacktoss.txt" + ] ] diff --git a/fgd/point/weapon/weapon_molotov_spawn.fgd b/fgd/point/weapon/weapon_molotov_spawn.fgd index dde2a3e91..e3c9cc9a4 100644 --- a/fgd/point/weapon/weapon_molotov_spawn.fgd +++ b/fgd/point/weapon/weapon_molotov_spawn.fgd @@ -4,4 +4,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_molotov_spawn: "Molotov" [ + @resources + [ + weapon_script "scripts/weapon_molotov.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pain_pills_spawn.fgd b/fgd/point/weapon/weapon_pain_pills_spawn.fgd index 185e666f2..cb8e2b672 100644 --- a/fgd/point/weapon/weapon_pain_pills_spawn.fgd +++ b/fgd/point/weapon/weapon_pain_pills_spawn.fgd @@ -5,4 +5,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_pain_pills_spawn: "Painkillers" [ + @resources + [ + weapon_script "scripts/weapon_pain_pills.txt" + ] ] diff --git a/fgd/point/weapon/weapon_physcannon.fgd b/fgd/point/weapon/weapon_physcannon.fgd index 2adbc5a25..a0d8f1a5a 100644 --- a/fgd/point/weapon/weapon_physcannon.fgd +++ b/fgd/point/weapon/weapon_physcannon.fgd @@ -5,6 +5,8 @@ [ @resources [ + weapon_script "scripts/weapon_physcannon.txt" + material "materials/sprites/orangelight1.vmt" material "materials/sprites/glow04_noz.vmt" material "materials/sprites/orangeflare1.vmt" diff --git a/fgd/point/weapon/weapon_pipe_bomb_spawn.fgd b/fgd/point/weapon/weapon_pipe_bomb_spawn.fgd index ee997d683..1340841ae 100644 --- a/fgd/point/weapon/weapon_pipe_bomb_spawn.fgd +++ b/fgd/point/weapon/weapon_pipe_bomb_spawn.fgd @@ -4,4 +4,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_pipe_bomb_spawn: "Pipe Bomb" [ + @resources + [ + weapon_script "scripts/weapon_pipe_bomb.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pistol.fgd b/fgd/point/weapon/weapon_pistol.fgd index 3e71813c9..67227762b 100644 --- a/fgd/point/weapon/weapon_pistol.fgd +++ b/fgd/point/weapon/weapon_pistol.fgd @@ -3,5 +3,8 @@ autovis(Weapons, HL2 Weapons, Pistol) studioprop("models/weapons/w_pistol.mdl") = weapon_pistol: "Pistol" [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_pistol.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pistol_magnum_spawn.fgd b/fgd/point/weapon/weapon_pistol_magnum_spawn.fgd index 8f9a127e4..3f92ea5f0 100644 --- a/fgd/point/weapon/weapon_pistol_magnum_spawn.fgd +++ b/fgd/point/weapon/weapon_pistol_magnum_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_pistol_magnum_spawn: "Magnum Pistol" [ + @resources + [ + weapon_script "scripts/weapon_pistol_magnum.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pistol_spawn.fgd b/fgd/point/weapon/weapon_pistol_spawn.fgd index 11585566e..599e7c703 100644 --- a/fgd/point/weapon/weapon_pistol_spawn.fgd +++ b/fgd/point/weapon/weapon_pistol_spawn.fgd @@ -11,4 +11,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_pistol_spawn: "Pistols" [ + @resources + [ + weapon_script "scripts/weapon_pistol.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pulsepistol.fgd b/fgd/point/weapon/weapon_pulsepistol.fgd index a1b9e0241..0f1b117f2 100644 --- a/fgd/point/weapon/weapon_pulsepistol.fgd +++ b/fgd/point/weapon/weapon_pulsepistol.fgd @@ -3,4 +3,8 @@ autovis(Weapons, EZ Weapons) studioprop("models/weapons/w_pulsepistol.mdl") = weapon_pulsepistol: "Prototype Gauss Pistol" [ + @resources + [ + weapon_script "scripts/weapon_pulsepistol.txt" + ] ] diff --git a/fgd/point/weapon/weapon_pumpshotgun_spawn.fgd b/fgd/point/weapon/weapon_pumpshotgun_spawn.fgd index 443bfc74b..985f47b68 100644 --- a/fgd/point/weapon/weapon_pumpshotgun_spawn.fgd +++ b/fgd/point/weapon/weapon_pumpshotgun_spawn.fgd @@ -4,4 +4,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_pumpshotgun_spawn: "Pump Shotgun" [ + @resources + [ + weapon_script "scripts/weapon_pumpshotgun.txt" + ] ] diff --git a/fgd/point/weapon/weapon_rifle_ak47_spawn.fgd b/fgd/point/weapon/weapon_rifle_ak47_spawn.fgd index c262f66b3..6669f9037 100644 --- a/fgd/point/weapon/weapon_rifle_ak47_spawn.fgd +++ b/fgd/point/weapon/weapon_rifle_ak47_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_rifle_ak47_spawn: "AK47" [ + @resources + [ + weapon_script "scripts/weapon_rifle_ak47.txt" + ] ] diff --git a/fgd/point/weapon/weapon_rifle_desert_spawn.fgd b/fgd/point/weapon/weapon_rifle_desert_spawn.fgd index 96225e4a1..7e24d9830 100644 --- a/fgd/point/weapon/weapon_rifle_desert_spawn.fgd +++ b/fgd/point/weapon/weapon_rifle_desert_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_rifle_desert_spawn: "Desert Rifle" [ + @resources + [ + weapon_script "scripts/weapon_rifle_desert.txt" + ] ] diff --git a/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd b/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd index fc16574d1..c8c2cadb1 100644 --- a/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd +++ b/fgd/point/weapon/weapon_rifle_sg552_spawn.fgd @@ -3,5 +3,9 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) studioprop("models/w_models/Weapons/w_rifle_sg552.mdl") = weapon_rifle_sg552_spawn: "SG552" -[ -] \ No newline at end of file + [ + @resources + [ + weapon_script "scripts/weapon_rifle_sg552.txt" + ] + ] diff --git a/fgd/point/weapon/weapon_rifle_spawn.fgd b/fgd/point/weapon/weapon_rifle_spawn.fgd index 6b704eb95..9339c4d79 100644 --- a/fgd/point/weapon/weapon_rifle_spawn.fgd +++ b/fgd/point/weapon/weapon_rifle_spawn.fgd @@ -4,4 +4,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_rifle_spawn: "Assault Rifle" [ + @resources + [ + weapon_script "scripts/weapon_rifle.txt" + ] ] diff --git a/fgd/point/weapon/weapon_rpg.fgd b/fgd/point/weapon/weapon_rpg.fgd index f7ce6da16..56604510c 100644 --- a/fgd/point/weapon/weapon_rpg.fgd +++ b/fgd/point/weapon/weapon_rpg.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_rpg.txt" sound "Missile.Ignite" sound "Missile.Accelerate" material "materials/effects/laser1_noz.vmt" diff --git a/fgd/point/weapon/weapon_shotgun.fgd b/fgd/point/weapon/weapon_shotgun.fgd index 5eb6e4bde..52940e640 100644 --- a/fgd/point/weapon/weapon_shotgun.fgd +++ b/fgd/point/weapon/weapon_shotgun.fgd @@ -3,5 +3,8 @@ autovis(Weapons, HL2 Weapons, Shotgun) studioprop("models/weapons/w_shotgun.mdl") = weapon_shotgun: "Shotgun" [ - @resources [] + @resources + [ + weapon_script "scripts/weapon_shotgun.txt" + ] ] diff --git a/fgd/point/weapon/weapon_shotgun_chrome_spawn.fgd b/fgd/point/weapon/weapon_shotgun_chrome_spawn.fgd index 24884888d..9c6a07952 100644 --- a/fgd/point/weapon/weapon_shotgun_chrome_spawn.fgd +++ b/fgd/point/weapon/weapon_shotgun_chrome_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_shotgun_chrome_spawn: "Chrome Shotgun" [ + @resources + [ + weapon_script "scripts/weapon_shotgun_chrome.txt" + ] ] diff --git a/fgd/point/weapon/weapon_shotgun_spas_spawn.fgd b/fgd/point/weapon/weapon_shotgun_spas_spawn.fgd index bd396eef1..aab068d99 100644 --- a/fgd/point/weapon/weapon_shotgun_spas_spawn.fgd +++ b/fgd/point/weapon/weapon_shotgun_spas_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_shotgun_spas_spawn: "SPAS Shotgun" [ + @resources + [ + weapon_script "scripts/weapon_shotgun_spas.txt" + ] ] diff --git a/fgd/point/weapon/weapon_slam.fgd b/fgd/point/weapon/weapon_slam.fgd index c16bb09c9..7d567a64a 100644 --- a/fgd/point/weapon/weapon_slam.fgd +++ b/fgd/point/weapon/weapon_slam.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_slam.txt" sound "Weapon_SLAM.ThrowMode" sound "Weapon_SLAM.TripMineMode" sound "Weapon_SLAM.SatchelDetonate" diff --git a/fgd/point/weapon/weapon_smg1.fgd b/fgd/point/weapon/weapon_smg1.fgd index e49e30fcc..0166c67ea 100644 --- a/fgd/point/weapon/weapon_smg1.fgd +++ b/fgd/point/weapon/weapon_smg1.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_smg1.txt" entity grenade_ar2 ] ] diff --git a/fgd/point/weapon/weapon_smg2.fgd b/fgd/point/weapon/weapon_smg2.fgd index d6202f4a1..ee328a3c8 100644 --- a/fgd/point/weapon/weapon_smg2.fgd +++ b/fgd/point/weapon/weapon_smg2.fgd @@ -3,4 +3,8 @@ autovis(Weapons, EZ Weapons) studioprop("models/weapons/w_mp5k.mdl") = weapon_smg2: "MP5K" [ + @resources + [ + weapon_script "scripts/weapon_smg2.txt" + ] ] diff --git a/fgd/point/weapon/weapon_smg_mp5_spawn.fgd b/fgd/point/weapon/weapon_smg_mp5_spawn.fgd index 7d5f34be7..667f671ff 100644 --- a/fgd/point/weapon/weapon_smg_mp5_spawn.fgd +++ b/fgd/point/weapon/weapon_smg_mp5_spawn.fgd @@ -3,5 +3,9 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) studioprop("models/w_models/Weapons/w_smg_mp5.mdl") = weapon_smg_mp5_spawn: "MP5" -[ -] \ No newline at end of file + [ + @resources + [ + weapon_script "scripts/weapon_smg_mp5.txt" + ] + ] diff --git a/fgd/point/weapon/weapon_smg_silenced_spawn.fgd b/fgd/point/weapon/weapon_smg_silenced_spawn.fgd index fecfffaa4..eeb5e205e 100644 --- a/fgd/point/weapon/weapon_smg_silenced_spawn.fgd +++ b/fgd/point/weapon/weapon_smg_silenced_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_smg_silenced_spawn: "MicroUZI" [ + @resources + [ + weapon_script "scripts/weapon_smg_silenced.txt" + ] ] diff --git a/fgd/point/weapon/weapon_smg_spawn.fgd b/fgd/point/weapon/weapon_smg_spawn.fgd index 53936759e..c235ab9c5 100644 --- a/fgd/point/weapon/weapon_smg_spawn.fgd +++ b/fgd/point/weapon/weapon_smg_spawn.fgd @@ -4,4 +4,8 @@ studioprop("models/w_models/Weapons/w_smg_uzi.mdl") = weapon_smg_spawn: "Submachinegun" [ + @resources + [ + weapon_script "scripts/weapon_smg.txt" + ] ] diff --git a/fgd/point/weapon/weapon_sniper_awp_spawn.fgd b/fgd/point/weapon/weapon_sniper_awp_spawn.fgd index 9cfd63f87..2b5579ef6 100644 --- a/fgd/point/weapon/weapon_sniper_awp_spawn.fgd +++ b/fgd/point/weapon/weapon_sniper_awp_spawn.fgd @@ -3,5 +3,9 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) studioprop("models/w_models/Weapons/w_sniper_awp.mdl") = weapon_sniper_awp_spawn: "AWP Sniper" -[ -] \ No newline at end of file + [ + @resources + [ + weapon_script "scripts/weapon_sniper_awp.txt" + ] + ] diff --git a/fgd/point/weapon/weapon_sniper_military_spawn.fgd b/fgd/point/weapon/weapon_sniper_military_spawn.fgd index 4e2484da7..dec56ad87 100644 --- a/fgd/point/weapon/weapon_sniper_military_spawn.fgd +++ b/fgd/point/weapon/weapon_sniper_military_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_sniper_military_spawn: "Military Sniper Rifle" [ + @resources + [ + weapon_script "scripts/weapon_sniper_military.txt" + ] ] diff --git a/fgd/point/weapon/weapon_stunstick.fgd b/fgd/point/weapon/weapon_stunstick.fgd index 274c38819..899526dd5 100644 --- a/fgd/point/weapon/weapon_stunstick.fgd +++ b/fgd/point/weapon/weapon_stunstick.fgd @@ -5,6 +5,7 @@ [ @resources [ + weapon_script "scripts/weapon_stunstick.txt" sound "Weapon_StunStick.Activate" sound "Weapon_StunStick.Deactivate" ] diff --git a/fgd/point/weapon/weapon_upgradepack_explosive_spawn.fgd b/fgd/point/weapon/weapon_upgradepack_explosive_spawn.fgd index dea206da8..692fa6c01 100644 --- a/fgd/point/weapon/weapon_upgradepack_explosive_spawn.fgd +++ b/fgd/point/weapon/weapon_upgradepack_explosive_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, L4D Items) = weapon_upgradepack_explosive_spawn: "Upgrade Pack - Explosive" [ + @resources + [ + weapon_script "scripts/weapon_upgradepack_explosive.txt" + ] ] diff --git a/fgd/point/weapon/weapon_upgradepack_incendiary_spawn.fgd b/fgd/point/weapon/weapon_upgradepack_incendiary_spawn.fgd index 17499e537..298202e2a 100644 --- a/fgd/point/weapon/weapon_upgradepack_incendiary_spawn.fgd +++ b/fgd/point/weapon/weapon_upgradepack_incendiary_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, L4D Items) = weapon_upgradepack_incendiary_spawn: "Upgrade Pack - Incendiary" [ + @resources + [ + weapon_script "scripts/weapon_upgradepack_incendiary.txt" + ] ] diff --git a/fgd/point/weapon/weapon_vomitjar_spawn.fgd b/fgd/point/weapon/weapon_vomitjar_spawn.fgd index f10312920..7eba75334 100644 --- a/fgd/point/weapon/weapon_vomitjar_spawn.fgd +++ b/fgd/point/weapon/weapon_vomitjar_spawn.fgd @@ -3,4 +3,8 @@ autovis(Weapons, L4D Weapons, Weapon Spawners) = weapon_vomitjar_spawn: "Vomit Jar" [ + @resources + [ + weapon_script "scripts/weapon_vomitjar.txt" + ] ] From 8f80fb908d56b67c61d2edf06ea2046377172886 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 12 May 2023 15:29:34 +1000 Subject: [PATCH 091/243] Specify these keyvalues --- fgd/bases/Breakable.fgd | 2 +- fgd/point/point/point_worldtext.fgd | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fgd/bases/Breakable.fgd b/fgd/bases/Breakable.fgd index 105c70f32..96f51cfa2 100644 --- a/fgd/bases/Breakable.fgd +++ b/fgd/bases/Breakable.fgd @@ -7,7 +7,7 @@ explodemagnitude(integer) : "Explode Magnitude" : 0 : "If non-zero, when this entity breaks it will create an explosion that causes the specified amount of damage." performancemode[engine](integer) : "Performance Mode" : 0 - performancemode[complete](choices) : "Performance Mode" : 0 : "Used to limit the amount of gibs produced when this entity breaks, for performance reasons." = + performancemode(choices) : "Performance Mode" : 0 : "Used to limit the amount of gibs produced when this entity breaks, for performance reasons." = [ 0: "Normal" 1: "No Gibs" diff --git a/fgd/point/point/point_worldtext.fgd b/fgd/point/point/point_worldtext.fgd index 61ace2454..cc02dcba0 100644 --- a/fgd/point/point/point_worldtext.fgd +++ b/fgd/point/point/point_worldtext.fgd @@ -20,6 +20,7 @@ color[!TF2](color255) : "Color" : "255 255 255" : "Color of the text" color[TF2](color255) : "Color" : "255 255 255 255" : "Color and opacity of the text" + font[engine](integer) : "Font" : 0 font[TF2](choices) : "Font" : 0 : "The font to use for the text" = [ 0 : "TF2 Build" @@ -37,6 +38,7 @@ 12 : "TF2 Build (soft edges)" ] + orientation[engine](integer) : "Orientation" : 0 orientation[TF2](choices) : "Orientation" : 0 : "Text orientation mode. Fixed orientation left aligns the text, the others center align it." = [ 0 : "Fixed orientation" From 7a8693486e682e5e0f9d970bd35e86d1097b0d60 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 12 May 2023 15:29:48 +1000 Subject: [PATCH 092/243] Add filter_script to visgroups --- fgd/visgroups.cfg | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/fgd/visgroups.cfg b/fgd/visgroups.cfg index 43748d0bc..2476ab174 100644 --- a/fgd/visgroups.cfg +++ b/fgd/visgroups.cfg @@ -501,17 +501,6 @@ * `logic_script` * `lua_run` - Filters - * `filter_damage_class` - * `filter_damage_mod` - * `filter_damage_transfer` - * `filter_damage_type` - * `filter_tf_damaged_by_weapon_in_slot` - * `filter_melee_damage` - * `filter_health` - * `filter_redirect_inflictor` - * `filter_redirect_owner` - * `filter_redirect_weapon` - * `filter_blood_control` * `filter_activator_class` * `filter_activator_classify` * `filter_activator_context` @@ -529,13 +518,25 @@ * `filter_activator_team` * `filter_activator_tfteam` * `filter_base` + * `filter_blood_control` * `filter_combineball_type` + * `filter_damage_class` + * `filter_damage_mod` + * `filter_damage_transfer` + * `filter_damage_type` * `filter_enemy` + * `filter_health` + * `filter_melee_damage` * `filter_multi` * `filter_player_held` + * `filter_redirect_inflictor` + * `filter_redirect_owner` + * `filter_redirect_weapon` + * `filter_script` * `filter_tf_bot_has_tag` * `filter_tf_class` * `filter_tf_condition` + * `filter_tf_damaged_by_weapon_in_slot` * `filter_tf_player_can_cap` - Globals * `logic_achievement` From ce538ddb0ef655602c469b4a417f21fbbcec6e01 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 12 May 2023 15:35:35 +1000 Subject: [PATCH 093/243] Add class resource function for prop_door_rotating --- fgd/point/prop/prop_door_rotating.fgd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fgd/point/prop/prop_door_rotating.fgd b/fgd/point/prop/prop_door_rotating.fgd index b746e3a92..fd0ed0278 100644 --- a/fgd/point/prop/prop_door_rotating.fgd +++ b/fgd/point/prop/prop_door_rotating.fgd @@ -130,4 +130,10 @@ output OnRotationDone[GMod, since_P2](void) : "Fired when the door arrives at it's goal angle." output OnBreak[L4D, L4D2](void) : "Fired when the door changes damage states." output OnKicked[EZ2](void) : "(EZ2) Fired when the door is kicked open." + + @resources + [ + sound "DoorSound.Null" + func prop_door_rotating + ] ] From ceaaf12c8bba35fe04cf43914f24e074b4a0a280 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 15 May 2023 21:44:02 -0700 Subject: [PATCH 094/243] Add line helper to comp_precache_model --- fgd/point/comp/comp_precache_model.fgd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fgd/point/comp/comp_precache_model.fgd b/fgd/point/comp/comp_precache_model.fgd index a6f12a8b1..5587b889b 100644 --- a/fgd/point/comp/comp_precache_model.fgd +++ b/fgd/point/comp/comp_precache_model.fgd @@ -3,6 +3,7 @@ studioprop() appliesto(srctools) autovis(Postcompiler, Precacher) + line(255 255 255, targetname, lineent) = comp_precache_model: "Force a specific model to load, for runtime switching. Duplicates will be removed." [ @@ -10,4 +11,5 @@ skin(integer): "Skin": : "Skin to show." skinset(string) : "Used Skins" : : "Set this to a space seperated list of all the skin numbers which will be used. " + "This allows auto-packing to skip unused ones. If blank all skins are assumed to be used." + lineent(target_destination) : "Line Entity" : : "Draws a line to the specified entity. Use this to indicate in Hammer if the model is meant for a specific entity." ] From f880a7a1229ed2cddafa794c84ae11709e0ae506 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 15 May 2023 21:44:29 -0700 Subject: [PATCH 095/243] info_lighting_relative shouldn't need complete FGD --- fgd/point/info/info_lighting_relative.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/info/info_lighting_relative.fgd b/fgd/point/info/info_lighting_relative.fgd index 637145a83..23dfeca8e 100644 --- a/fgd/point/info/info_lighting_relative.fgd +++ b/fgd/point/info/info_lighting_relative.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPoint) - appliesto(until_L4D, +complete) + appliesto(until_L4D) line(255 255 255, targetname, lightinglandmark) iconsprite("editor/ficool2/info_lighting_relative.vmt") halfgridsnap From a62127e79839135bcf6ec43a3331bcfce42c2715 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 25 May 2023 13:56:44 +1000 Subject: [PATCH 096/243] Add support for swapping skins for geocables --- CHANGELOG.md | 1 + fgd/point/comp/comp_prop_cable_dynamic.fgd | 7 ++++ transforms/geocable.py | 42 +++++++++++++++------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 195381661..4b6d4ae28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Version (dev) * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. +* Added ability to specify alt skins when using `comp_prop_cable_dynamic`. * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. diff --git a/fgd/point/comp/comp_prop_cable_dynamic.fgd b/fgd/point/comp/comp_prop_cable_dynamic.fgd index e2f0e587f..72b06adcc 100644 --- a/fgd/point/comp/comp_prop_cable_dynamic.fgd +++ b/fgd/point/comp/comp_prop_cable_dynamic.fgd @@ -13,12 +13,19 @@ group(target_source): "Cable Group" : : "Specify the name of the group that will be compiled into this entity." + skin1(material) : "Alt Skin 1" : "" : "If set, an alternate material to use for Skin 1. Must be a model material, and the rope must use the same material for the entire prop." + skin2(material) : "Alt Skin 2" : "" : "If set, an alternate material to use for Skin 2." + skin3(material) : "Alt Skin 3" : "" : "If set, an alternate material to use for Skin 3." + skin4(material) : "Alt Skin 4" : "" : "If set, an alternate material to use for Skin 4." + skin5(material) : "Alt Skin 5" : "" : "If set, an alternate material to use for Skin 5. If you need more, add the keyvalue with SmartEdit off." + // Not inheriting from prop_dynamic_base, since most of the animation KVs are pointless. glowbackfacemult[L4D2](float) : "Glow backface Multiplier" : "1.0" : "What to multiply glow by on backfaces." // Inputs input TurnOn(void) : "Make the cable set visible." input TurnOff(void) : "Make the cable set invisible." + input Skin(integer) : "Change to an alternate skin for the cable set." input EnableCollision(void) : "Enable collision on the cable set." input DisableCollision(void) : "Disable collision on the cable set." input BecomeRagdoll[since_P2](void) : "Change into a ragdoll immediately." diff --git a/transforms/geocable.py b/transforms/geocable.py index c43913daf..bd9ae0e88 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -316,12 +316,7 @@ class NodeEnt: def relative_to(self, off: Vec) -> 'NodeEnt': """Return a copy relative to the specified origin.""" - return NodeEnt( - self.pos - off, - self.config, - self.id, - self.group, - ) + return attrs.evolve(self, pos=self.pos - off) def __hash__(self) -> int: """Hash the vector with the rest of the values.""" @@ -385,14 +380,14 @@ def __repr__(self) -> str: async def build_rope( - nodes_and_conn: Tuple[FrozenSet[NodeEnt], FrozenSet[Tuple[NodeID, NodeID]]], + rope_key: Tuple[FrozenSet[NodeEnt], FrozenSet[Tuple[NodeID, NodeID]], Tuple[str, ...]], temp_folder: Path, mdl_name: str, args: Tuple[Vec, FileSystem], ) -> Tuple[Vec, List[Tuple[Vec, float, Vec, float]], List[SegProp], List[List[Vec]]]: """Construct the geometry for a rope.""" LOGGER.info('Building rope {}', mdl_name) - ents, connections = nodes_and_conn + ents, connections, skins = rope_key offset, fsys = args mesh = Mesh.blank('root') @@ -450,6 +445,19 @@ async def build_rope( if is_vactube and hasattr(Mesh, 'NEED_TRANSLUCENT_MOSTLYOPAQUE'): f.write('$mostlyopaque\n') f.write(QC_TEMPLATE.format(path=mdl_name, light_origin=light_origin)) + if skins: + f.write('$texturegroup "skinfamilies" {\n') + try: + [first_mat] = {node.config.material for node in nodes} + except ValueError: + raise NotImplementedError( + 'Using multiple skins for a rope using different materials ' + 'for different segments is not supported.' + ) from None + f.write(f' {{ "{first_mat}" }}\n') + for mat in skins: + f.write(f' {{ "{mat}" }}\n') + f.write('}\n') if coll_nodes: f.write(QC_TEMPLATE_PHYS.format(count=sum(node.next is not None for node in coll_nodes))) @@ -648,7 +656,7 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: def interpolate_all(nodes: Set[Node]) -> None: """Produce nodes in-between each user-made node.""" - # Create the nodes and put them in a seperate list, then add them + # Create the nodes and put them in a separate list, then add them # to the actual nodes list second. This way sections that have been interpolated # don't affect the interpolation of neighbouring sections. @@ -674,12 +682,13 @@ def interpolate_all(nodes: Set[Node]) -> None: points[-1].next.prev = points[-1] # Finally, split nodes with too much of an angle between them - we can't smooth. + # Don't bother if they're real small angles though, that's fine. for node in list(nodes): if node.prev is None or node.next is None: continue off1 = node.pos - node.prev.pos off2 = node.next.pos - node.pos - if Vec.dot(off1, off2) < 0.7: + if Vec.dot(off1, off2) < 0.7 and off1.mag_sq() > 8 and off2.mag_sq() > 8: new_node = Node(node.pos.copy(), node.config, node.radius) nodes.add(new_node) new_node.next = node.next @@ -1084,14 +1093,21 @@ async def compile_rope( node.relative_to(origin) for node in nodes }) + skins = [] + for i in itertools.count(1): + skin_str = ent[f'skin{i}'] + if not skin_str: + break + skins.append(skin_str) + model_name, _ = await compiler.get_model( - (dyn_nodes, frozenset(connections)), + (dyn_nodes, frozenset(connections), tuple(skins)), build_rope, (origin, ctx.pack.fsys), ) ent['model'] = model_name ang = Angle.from_str(ent['angles']) - ang.yaw -= 90.0 + ang.yaw += 270.0 ent['angles'] = ang if not dyn_ents: # Static prop. @@ -1106,7 +1122,7 @@ async def compile_rope( has_coll = True model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( - (frozenset(local_nodes), frozenset(connections)), + (frozenset(local_nodes), frozenset(connections), ()), build_rope, (center, ctx.pack.fsys), ) From 323108a6fb3355211dde0c4dbecd3b208d332ca8 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 25 May 2023 13:57:13 +1000 Subject: [PATCH 097/243] Add an option to skip actually writing the BSP, mainly for testing the compiler --- src/hammeraddons/postcompiler.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index c152bea3f..6accf6f89 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -55,6 +55,12 @@ async def main(argv: List[str]) -> None: action="store_false", help="Prevent packing of files found in the map." ) + parser.add_argument( + "--nosaving", + dest="allow_save", + action="store_false", + help="For testing purposes, allow skipping saving the BSP.", + ) parser.add_argument( "--propcombine", action="store_true", @@ -317,8 +323,9 @@ def pack_callback(path: str) -> Optional[bool]: for name, exts in sorted(ext_for_name.items()) ]))) - LOGGER.info('Writing BSP...') - bsp_file.save() + if args.allow_save: + LOGGER.info('Writing BSP...') + bsp_file.save() try: from srctools.fgd import _engine_db_stats # noqa From d4cf35dbe3aa283bb810b3963edc78eeceb40b4a Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 25 May 2023 14:19:07 +1000 Subject: [PATCH 098/243] Rotate geocable geometry, instead of rotating the final prop --- transforms/geocable.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index bd9ae0e88..73869cdd6 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -420,7 +420,7 @@ async def build_rope( coll_mesh.triangles.extend(generate_straights(coll_nodes)) generate_caps(coll_nodes, coll_mesh, is_coll=True) - # Move the UVs around so they don't extend too far. + # Move the UVs around, so they don't extend too far. for tri in mesh.triangles: u = math.floor(min(point.tex_u for point in tri)) v = math.floor(min(point.tex_v for point in tri)) @@ -434,11 +434,22 @@ async def build_rope( # the whole model. light_origin = min((node.pos for node in nodes), key=Vec.mag_sq) + # Studiomdl seems to rotate everything 90 degrees... + orient_fix = Matrix.from_yaw(270) + + fixed_visual_mesh = Mesh.blank('root') + fixed_visual_mesh.append_model(mesh, rotation=orient_fix) + with (temp_folder / 'cable.smd').open('wb') as fb: - mesh.export(fb) + fixed_visual_mesh.export(fb) if coll_nodes: + fixed_coll_mesh = Mesh.blank('root') + fixed_coll_mesh.append_model(coll_mesh, rotation=orient_fix) with (temp_folder / 'cable_phy.smd').open('wb') as fb: - coll_mesh.export(fb) + fixed_coll_mesh.export(fb) + del coll_mesh, fixed_coll_mesh + + del mesh, fixed_visual_mesh with (temp_folder / 'model.qc').open('w') as f: # Desolation needs this hint. @@ -459,7 +470,7 @@ async def build_rope( f.write(f' {{ "{mat}" }}\n') f.write('}\n') if coll_nodes: - f.write(QC_TEMPLATE_PHYS.format(count=sum(node.next is not None for node in coll_nodes))) + f.write(QC_TEMPLATE_PHYS.format(count=sum(node.next is not None for node in coll_nodes) + 8)) # For visleaf computation, build a list of all the actual segments generated. coll_data = [ @@ -1106,9 +1117,6 @@ async def compile_rope( (origin, ctx.pack.fsys), ) ent['model'] = model_name - ang = Angle.from_str(ent['angles']) - ang.yaw += 270.0 - ent['angles'] = ang if not dyn_ents: # Static prop. bbox_min, bbox_max = Vec.bbox(node.pos for node in nodes) @@ -1147,7 +1155,7 @@ async def compile_rope( ctx.bsp.props.append(StaticProp( model=model_name, origin=center, - angles=Angle(0, 270, 0), + angles=Angle(0, 0, 0), scaling=1.0, visleafs=leafs, solidity=6 if has_coll else 0, @@ -1163,7 +1171,7 @@ async def compile_rope( ctx.bsp.props.append(StaticProp( model=seg_prop.model, origin=center + seg_prop.offset, - angles=(seg_prop.orient @ Matrix.from_yaw(270)).to_angle(), + angles=seg_prop.orient.to_angle(), scaling=1.0, visleafs=leafs, # TODO: compute individual leafs here? solidity=6, @@ -1272,7 +1280,7 @@ async def comp_prop_rope(ctx: Context) -> None: # To group nodes, take each group out, then search recursively through # all connections from it to other nodes. todo = set(all_nodes.values()) - with ModelCompiler.from_ctx(ctx, 'ropes', version=2) as compiler: + with ModelCompiler.from_ctx(ctx, 'ropes', version=3) as compiler: async with trio.open_nursery() as nursery: while todo: dyn_ents: List[Entity] = [] From 1ec2616e876a37496f24bcf9b22c538d1542a800 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 3 Jun 2023 13:34:50 +1000 Subject: [PATCH 099/243] Add entity func for default citizen type --- fgd/point/hl2_gamerules.fgd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fgd/point/hl2_gamerules.fgd b/fgd/point/hl2_gamerules.fgd index 3f3d3956b..35e0bfc87 100644 --- a/fgd/point/hl2_gamerules.fgd +++ b/fgd/point/hl2_gamerules.fgd @@ -64,4 +64,10 @@ input SetLegacyFlashlight(bool) : "Sets legacy flashlight." input SetStunstickPickupBehavior(bool) : "Sets stunstick pickup behavior." input SetAllowSPRespawn(bool) : "Sets SP respawning." + + @resources + [ + // We want to include the resources for the specified citizen type. + func hl2_gamerules + ] ] From ae4ad9905b10b425de4ffe3423588bf5e08127d2 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 3 Jun 2023 14:25:03 +1000 Subject: [PATCH 100/243] Bump srctools version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 47f10d60e..b86a114bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs >= 21.4.0 typing_extensions >= 4.2.0 -srctools >= 2.3.10 +srctools >= 2.3.12 trio >= 0.20.0 trio-typing >= 0.7.0 pyinstaller >= 5.7.0 From a6c3408ee02bbcf3bec56379b4c2612c21646511 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 3 Jun 2023 17:19:49 +1000 Subject: [PATCH 101/243] comp_kv_setter supports the control parameters --- fgd/point/comp/comp_kv_setter.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/comp/comp_kv_setter.fgd b/fgd/point/comp/comp_kv_setter.fgd index b5513b292..2ca2be928 100644 --- a/fgd/point/comp/comp_kv_setter.fgd +++ b/fgd/point/comp/comp_kv_setter.fgd @@ -1,4 +1,4 @@ -@PointClass base(Angles) +@PointClass base(Angles, ControlEnables) iconsprite("editor/comp_kv_setter") appliesto(srctools) autovis(Postcompiler, KV Setter) From aec996a0d4983cb86410a4ceebc2555d10c18d6a Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 3 Jun 2023 18:14:26 +1000 Subject: [PATCH 102/243] Add `comp_adv_output`, which allows adding a single output with complex behaviour --- CHANGELOG.md | 1 + comp_adv_output.png | Bin 0 -> 337 bytes fgd/point/comp/comp_adv_output.fgd | 40 +++++++++ hammer/materials/editor/comp_adv_output.vmt | 8 ++ hammer/materials/editor/comp_adv_output.vtf | Bin 0 -> 22052 bytes materialsrc/editor/comp_adv_output.pdn | Bin 0 -> 7032 bytes transforms/comp_adv_output.py | 94 ++++++++++++++++++++ transforms/comp_kv_setter.py | 5 +- 8 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 comp_adv_output.png create mode 100644 fgd/point/comp/comp_adv_output.fgd create mode 100644 hammer/materials/editor/comp_adv_output.vmt create mode 100644 hammer/materials/editor/comp_adv_output.vtf create mode 100644 materialsrc/editor/comp_adv_output.pdn create mode 100644 transforms/comp_adv_output.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b6d4ae28..ff9f2c4b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Version (dev) +* #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Added ability to specify alt skins when using `comp_prop_cable_dynamic`. * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. diff --git a/comp_adv_output.png b/comp_adv_output.png new file mode 100644 index 0000000000000000000000000000000000000000..716a5b612db635be403719dd6f674a982db19a56 GIT binary patch literal 337 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 literal 0 HcmV?d00001 diff --git a/transforms/comp_adv_output.py b/transforms/comp_adv_output.py new file mode 100644 index 000000000..c74af8a0f --- /dev/null +++ b/transforms/comp_adv_output.py @@ -0,0 +1,94 @@ +"""Adds a single output to an entity, with precise control over fixup behaviour. + +""" +import itertools +import string +from typing import Any, Mapping, Sequence, Union + +from srctools import EmptyMapping, conv_float, conv_int +from srctools.vmf import Output, Entity +from srctools.logger import get_logger + +from hammeraddons.bsp_transform import trans, Context, check_control_enabled + + +class SimpleFormatter(string.Formatter): + """Use 1-based indexes for the args, instead of 0-based.""" + def get_value(self, key: Union[int, str], args: Sequence[Any], kwargs: Mapping[str, Any]) -> Any: + """Adjust the key.""" + if isinstance(key, int) and key > 0: + return args[key - 1] + raise KeyError(key) + + +LOGGER = get_logger(__name__) +FORMATTER = SimpleFormatter() + + +@trans('comp_adv_output') +def advanced_output(ctx: Context) -> None: + """Adds a single output to an entity, with precise control over fixup behaviour.""" + adv_out: Entity + for adv_out in ctx.vmf.by_class['comp_adv_output']: + adv_out.remove() + if not check_control_enabled(adv_out): + continue + + output_name = adv_out['out_name'] + input_name = adv_out['inp_name'] + target_name = adv_out['target_local'] or adv_out['target_global'] + times = conv_int(adv_out['times'], -1) + if not target_name: + LOGGER.warning( + 'No target entity for conv_adv_output at ({})!', + adv_out['origin'], + ) + continue + + if target_instname := adv_out['target_instname']: + target_name = f'{target_name}-{target_instname}' + + delay = conv_float(adv_out['delay']) + delay += conv_float(adv_out['delay2']) + if delay < 0.0: + LOGGER.warning( + 'conv_adv_output at ({}) has a negative delay!', + adv_out['origin'], + ) + delay = 0.0 + + param_args: list[str] = [] + for ind in itertools.count(1): + val = adv_out[f'params_local{ind}'] or adv_out[f'params_global{ind}'] + if not val: + break + param_args.append(val) + + parameter = adv_out['params_fmt'] + if parameter: + try: + parameter = FORMATTER.vformat(parameter, param_args, EmptyMapping) + except Exception: + LOGGER.exception( + 'Failed to format comp_adv_output at ({}) using format "{}" and parameters {!r}', + adv_out['origin'], parameter, param_args + ) + continue + + found_ent = None + + for found_ent in ctx.vmf.search(adv_out['out_ent']): + found_ent.add_out(Output( + output_name, + target_name, + input_name, + parameter, + delay, + times=times, + )) + + if found_ent is None: + LOGGER.warning( + 'No entities found named "{}" for comp_adv_output at ({})!', + adv_out['target'], adv_out['origin'], + ) diff --git a/transforms/comp_kv_setter.py b/transforms/comp_kv_setter.py index 563985ad6..922caa6e2 100644 --- a/transforms/comp_kv_setter.py +++ b/transforms/comp_kv_setter.py @@ -55,7 +55,7 @@ def kv_setter(ctx: Context) -> None: except (TypeError, ValueError): LOGGER.warning( 'Invalid spawnflags mask for comp_kv_setter at ({})!\n' - 'Provide an integer, 0x45 hex or 0b01101 binary value.', + 'Provide an decimal integer, 0x45 hex or 0b01101 binary value.', mode, setter['origin'], ) @@ -66,7 +66,6 @@ def kv_setter(ctx: Context) -> None: flag_enabled = False kv_name = kv_name.strip() - found_ent = None if not is_flags and not kv_name and not setter.outputs: # We have nothing to do? @@ -77,6 +76,8 @@ def kv_setter(ctx: Context) -> None: setter['origin'], ) + found_ent = None + for found_ent in ctx.vmf.search(setter['target']): if is_flags: spawnflags = conv_int(found_ent['spawnflags', '0']) From c56c05effe44991095c2513ffa48dca1e9ed1e09 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 14:25:31 +1000 Subject: [PATCH 103/243] Tweak type hints --- src/hammeraddons/bsp_transform/packing.py | 2 +- src/hammeraddons/mdl_compiler.py | 2 +- src/hammeraddons/postcompiler.py | 11 +++++++---- src/hammeraddons/propcombine.py | 24 ++++++++++++++--------- src/hammeraddons/props_config.py | 8 ++++---- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/hammeraddons/bsp_transform/packing.py b/src/hammeraddons/bsp_transform/packing.py index 69985d94c..ec1f5672a 100644 --- a/src/hammeraddons/bsp_transform/packing.py +++ b/src/hammeraddons/bsp_transform/packing.py @@ -65,7 +65,7 @@ def make_precache_prop(ent: Entity) -> None: def comp_precache_sound(ctx: Context): """Force precaching a set of sounds.""" # Match normalised sound to the original filename. - sounds: dict[str, str] = {} + sounds: Dict[str, str] = {} for ent in ctx.vmf.by_class['comp_precache_sound']: ent.remove() if not check_control_enabled(ent): diff --git a/src/hammeraddons/mdl_compiler.py b/src/hammeraddons/mdl_compiler.py index 2143841c5..e1e94bff6 100644 --- a/src/hammeraddons/mdl_compiler.py +++ b/src/hammeraddons/mdl_compiler.py @@ -234,7 +234,7 @@ async def _compile( break # If compile dir is specified, create the folder/clear it, but don't delete once done. - ctx_man: ContextManager[Union[str, bytes, Path]] + ctx_man: ContextManager[Union[str, Path]] if self.compile_dir is not None: path = Path(self.compile_dir, mdl_name) ctx_man = contextlib.nullcontext(path) diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index 6accf6f89..46c95963f 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -219,6 +219,9 @@ async def main(argv: List[str]) -> None: crowbar_loc = None LOGGER.info('Combining props...') + max_auto_range: Optional[float] = conf.opts.get(config.PROPCOMBINE_MAX_AUTO_RANGE) + if not max_auto_range: + max_auto_range = math.inf await propcombine.combine( bsp_file, bsp_file.ents, @@ -229,7 +232,7 @@ async def main(argv: List[str]) -> None: decomp_cache_loc=decomp_cache_loc, crowbar_loc=crowbar_loc, min_auto_range=conf.opts.get(config.PROPCOMBINE_MIN_AUTO_RANGE), - max_auto_range=conf.opts.get(config.PROPCOMBINE_MAX_AUTO_RANGE) or math.inf, + max_auto_range=max_auto_range, min_cluster=conf.opts.get(config.PROPCOMBINE_MIN_CLUSTER), min_cluster_auto=conf.opts.get(config.PROPCOMBINE_MIN_CLUSTER_AUTO), blacklist=conf.opts.get(config.PROPCOMBINE_BLACKLIST).as_array(), @@ -304,13 +307,13 @@ def pack_callback(path: str) -> Optional[bool]: # List out all the files, but group together files with the same extension. ext_for_name: Dict[str, List[str]] = defaultdict(list) - for file in bsp_file.pakfile.infolist(): - filename = Path(file.filename) + for zip_info in bsp_file.pakfile.infolist(): + filename = Path(zip_info.filename) if '.' in filename.name: stem, ext = filename.name.split('.', 1) file_path = str(filename.parent / stem) else: - file_path = file.filename + file_path = zip_info.filename ext = '' ext_for_name[file_path].append(ext) diff --git a/src/hammeraddons/propcombine.py b/src/hammeraddons/propcombine.py index eb78f6d43..46672b88a 100644 --- a/src/hammeraddons/propcombine.py +++ b/src/hammeraddons/propcombine.py @@ -230,8 +230,10 @@ async def combine_group( prop_pos = set() for prop in props: origin = round((prop.origin - avg_pos) @ yaw_rot, 7) - angles = round(Vec(prop.angles), 7) - angles.y -= avg_yaw + angles = prop.angles + angles.pitch = round(angles.pitch, 7) + angles.yaw = round(angles.yaw - avg_yaw, 7) + angles.roll = round(angles.roll, 7) try: coll = CollType(prop.solidity) except ValueError: @@ -245,15 +247,21 @@ async def combine_group( ) qc, mdl = lookup_model(prop.model) assert mdl is not None, prop.model + + scale = prop.scaling + if isinstance(scale, float): + scale_x = scale_y = scale_z = scale + else: + scale_x, scale_y, scale_z = scale prop_pos.add(PropPos( origin.x, origin.y, origin.z, - angles.x, angles.y, angles.z, + angles.pitch, angles.yaw, angles.roll, prop.model, mdl.checksum, prop.skin, - prop.scaling.x, - prop.scaling.y, - prop.scaling.z, + scale_x, + scale_y, + scale_z, coll, )) # We don't want to build collisions if it's not used. @@ -296,8 +304,6 @@ async def compile_func( contents: Set[int] = set() combined_flags = ModelFlags(0) - qc: QC - mdl: Model for prop in prop_pos: qc, mdl = lookup_model(prop.model) assert qc is not None, prop.model @@ -920,7 +926,7 @@ def group_props_ent( if not found: # No point checking an empty list. continue - actual = [] + actual: List[StaticProp] = [] total_verts = 0 for prop in found: qc, mdl = get_model(prop.model) diff --git a/src/hammeraddons/props_config.py b/src/hammeraddons/props_config.py index 8ebf7b130..31f9ce4ee 100644 --- a/src/hammeraddons/props_config.py +++ b/src/hammeraddons/props_config.py @@ -120,8 +120,8 @@ def vector(cls, opt_id: str, default: Vec, doc: str, *, fallback: Optional[str] return OptWithDefault(opt_id, Vec, default, doc, fallback) -@attrs.define(init=False) -class OptWithDefault(Opt[OptionT], Generic[OptionT]): +@attrs.define(init=False) # __attrs_init__() is incompatible with the superclass. +class OptWithDefault(Opt[OptionT], Generic[OptionT]): # type: ignore[override] """An option, with a default.""" default: OptionT def __init__( @@ -254,8 +254,8 @@ def get(self, option: Opt[OptionT]) -> Optional[Option]: raise TypeError(f'Option "{option.name}" does not exist!') from None if val is None: - if option.kind is Keyvalues: # Type checker doesn't understand isinstance here. - return Keyvalues(option.name, []) # type: ignore + if option.kind is Keyvalues: + return Keyvalues(option.name, []) else: return None From 554d45d42a509a2051ffe77f24374677894b6f98 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 14:59:39 +1000 Subject: [PATCH 104/243] Add --verbose parameter to postcompiler. --- CHANGELOG.md | 1 + src/hammeraddons/postcompiler.py | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff9f2c4b2..d7216f1e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Version (dev) * #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. +* Added `--verbose` parameter, for showing DEBUG messages. * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Added ability to specify alt skins when using `comp_prop_cable_dynamic`. * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index 46c95963f..ede51fde8 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -1,6 +1,4 @@ """Runs before VRAD, to run operations on the final BSP.""" -import math -import shutil from pathlib import Path import sys import warnings @@ -15,7 +13,9 @@ from typing import Dict, List, Optional from collections import defaultdict -from logging import FileHandler +from logging import FileHandler, StreamHandler +import math +import shutil import argparse import os import re @@ -61,6 +61,11 @@ async def main(argv: List[str]) -> None: action="store_false", help="For testing purposes, allow skipping saving the BSP.", ) + parser.add_argument( + '-v', '--verbose', + action="store_true", + help="Show DEBUG level messages.", + ) parser.add_argument( "--propcombine", action="store_true", @@ -86,6 +91,14 @@ async def main(argv: List[str]) -> None: if args.showgroups: LOGGER.warning('--showgroups is not implemented. r_colorstaticprops does the same thing ingame.') + if args.verbose: + # Find the stdout handler, make it DEBUG mode. + for handler in LOGGER.handlers: + if isinstance(handler, StreamHandler) and handler.stream is sys.stdout: + handler.setLevel('DEBUG') + break + else: + LOGGER.warning('Could not set stdout handler to DEBUG mode.') # The path is the last argument to the compiler. # Hammer adds wrong slashes sometimes, so fix that. From db375b7808f3522d5b9eae467003f59c2a3f33bf Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 15:51:44 +1000 Subject: [PATCH 105/243] Tweak these descriptions to make it clear that these are simply shortcuts --- fgd/point/comp/comp_flicker.fgd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/point/comp/comp_flicker.fgd b/fgd/point/comp/comp_flicker.fgd index a433773ba..720637903 100644 --- a/fgd/point/comp/comp_flicker.fgd +++ b/fgd/point/comp/comp_flicker.fgd @@ -6,9 +6,9 @@ = comp_flicker: "Fires on/off inputs repeatedly to simulate a flicker-on effect. " + "This converts to an info_target, and uses all of the FireUserX inputs/outputs." [ - target_mdl(target_destination) : "Model to Control" : : "An entity which will have skins swapped to turn on/off." - mdl_skin_on(integer) : "On Skin" : 0 : "The 'on' skin for the model." - mdl_skin_off(integer) : "Off Skin" : 1 : "The 'on' skin for the model." + target_mdl(target_destination) : "Model to Control" : : "An entity which will have skins swapped to turn on/off. This is simply a shortcut for adding OnTurnedOn/Off outputs." + mdl_skin_on(integer) : "On Skin" : 0 : "The 'on' skin for the model. This is a shortcut for adding OnTurnedOn -> Skin outputs." + mdl_skin_off(integer) : "Off Skin" : 1 : "The 'on' skin for the model. This is a shortcut for adding OnTurnedOff -> Skin outputs." total_time(float) : "Total Time" : 1.5 : "The overall time taken to complete the on or off flicker." flicker_min(float) : "Flicker Min" : 0.05 : "The delay used at the start of the on cycle, or at the end of the off cycle." From 4af2e1ac0c8326b93fef4a58745af11178888ed4 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 16:06:55 +1000 Subject: [PATCH 106/243] dispenser_touch_trigger is an internal entity --- fgd/brush/dispenser_touch_trigger.fgd | 5 ----- fgd/engine/dispenser_touch_trigger.fgd | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 fgd/brush/dispenser_touch_trigger.fgd create mode 100644 fgd/engine/dispenser_touch_trigger.fgd diff --git a/fgd/brush/dispenser_touch_trigger.fgd b/fgd/brush/dispenser_touch_trigger.fgd deleted file mode 100644 index 449a10cef..000000000 --- a/fgd/brush/dispenser_touch_trigger.fgd +++ /dev/null @@ -1,5 +0,0 @@ - -@SolidClass base(Trigger) - appliesto(TF2) = dispenser_touch_trigger: "Trigger for dispenser healing bounds" - [ - ] diff --git a/fgd/engine/dispenser_touch_trigger.fgd b/fgd/engine/dispenser_touch_trigger.fgd new file mode 100644 index 000000000..9f4275f16 --- /dev/null +++ b/fgd/engine/dispenser_touch_trigger.fgd @@ -0,0 +1,4 @@ +@SolidClass base(Trigger) + appliesto(+engine, TF2) = dispenser_touch_trigger: "Trigger for dispenser healing bounds" + [ + ] From 606e2e5c72d48981eb5154545c679e685a3d664a Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 16:36:28 +1000 Subject: [PATCH 107/243] Add more entities to the visgroups --- fgd/visgroups.cfg | 211 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 172 insertions(+), 39 deletions(-) diff --git a/fgd/visgroups.cfg b/fgd/visgroups.cfg index 2476ab174..ebbfde20d 100644 --- a/fgd/visgroups.cfg +++ b/fgd/visgroups.cfg @@ -165,6 +165,7 @@ * `npc_arbeit_helicopter` - Rocket Turrets * `npc_rocket_tripwire` + * `prop_rocket_tripwire` * `npc_rocket_turret` * `rocket_turret_projectile` - Wheatley (`npc_wheatley_boss`) @@ -253,24 +254,38 @@ - Simple NextBot (`simple_bot`) - Spy NextBot (`bot_npc_decoy`) - Nodes - * `info_node_air` + * `info_bigmomma` + * `info_marine_hint` * `info_node_air_hint` + * `info_node_air` * `info_node_climb` - * `info_bigmomma` - * `info_node` * `info_node_hint` - * `info_node_marine_hint` - * `info_node_link` * `info_node_link_controller` * `info_node_link_filtered` * `info_node_link_logic` * `info_node_link_oneway` - * `info_template_link_controller` + * `info_node_link` + * `info_node_marine_hint` + * `info_node` * `info_radial_link_controller` + * `info_template_link_controller` - Brush Entities - Clips + * `func_bulletshield` + * `func_clip_client` * `func_clip_vphysics` + * `func_precipitation_blocker` * `func_vehicleclip` + - Gameplay Zones + # Marks game-specific locations. + * `func_achievement` + * `func_bomb_target` + * `func_buyzone` + * `func_capturezone` + * `func_changeclass` + * `func_respawnroom` + * `func_powerupvolume` + * `func_respawnflag` - Triggers - Death Triggers * `trigger_hurt` @@ -303,6 +318,7 @@ * `trigger_changelevel` * `trigger_finale` * `trigger_transition` + * `game_zone_player` * `trigger_standoff` * `trigger_active_weapon_detect` * `trigger_add_tf_player_condition` @@ -348,23 +364,74 @@ * `trigger_teleport_relative` * `trigger_timer_door` * `trigger_togglesave` - * `trigger_tonemap` * `trigger_upgrade_laser_sight` * `trigger_weaponfire` - Func Brush + * `func_brush_kzmod` * `func_brush` - * `func_wall` * `func_wall_toggle` + * `func_wall` + * `func_illusionary` - LOD Brushes (`func_lod`) + - FX Swap Triggers + * `trigger_tonemap` + * `color_correction_volume` + * `fog_volume` - Conveyors * `func_conveyor` * `func_conveyor_bms` - Physbox * `func_physbox` * `func_physbox_multiplayer` + * `func_pushable` - Breakables * `func_breakable` * `func_breakable_surf` + - FX + * `env_bubbles` + * `env_embers` + * `func_dustcloud` + * `func_dustmotes` + * `func_forcefield` + * `func_precipitation` + * `func_respawnroomvisualizer` + * `func_fish_pool` + * `newxog_volume` + - Dynamic Water + * `func_water` + * `func_water_analog` + - Linear Movement + * `func_buildable_button` + * `func_button` + * `func_door` + * `func_elevator` + * `func_movelinear` + * `func_plat` + * `func_platrot` + * `momentary_door` + - Rotational Movement + * `func_door_rotating` + * `func_platrot` + * `func_rotating` + * `momentary_rot_button` + - Tanks + * `func_tank_combine_cannon` + * `func_tank` + * `func_tankairboatgun` + * `func_tankapcrocket` + * `func_tanklaser` + * `func_tanklogic` + * `func_tankmortar` + * `func_tankphyscannister` + * `func_tankpulselaser` + * `func_tankrocket` + * `func_tanktrain` + - Trains + * `func_tanktrain` + * `func_tracktrain` + * `func_train` + * `func_traincontrols` + * `func_guntarget` - Point Entities - Physics @@ -429,6 +496,10 @@ * `env_shooter` * `env_smokestack` * `env_smoketrail` + * `env_effectscript` + * `env_dustpuff` + * `env_entity_igniter` + * `env_entity_dissolver` - Sprites * `env_sprite_clientside` * `env_sprite_oriented` @@ -437,6 +508,7 @@ - Logic * `logic_auto` * `logic_relay` + * `sixense_logic_relay` * `logic_relay_queue` * `comp_relay` * `logic_branch` @@ -587,7 +659,10 @@ * `game_weapon_manager` * `wheel_of_doom` * `env_wind` + * `point_bonusmaps_accessor` + * `point_changelevel` * `point_clientcommand` + * `point_broadcastclientcommand` * `point_servercommand` * `postprocess_controller` * `shadow_control` @@ -669,14 +744,15 @@ * `npc_maker` * `npc_template_maker` - VGUI - * `vgui_movie_display` * `prop_portal_stats_display` * `vgui_level_placard_display` + * `vgui_movie_display` * `vgui_mp_lobby_display` * `vgui_neurotoxin_countdown` * `vgui_screen` - * `func_monitor` - * `point_camera` + * `vgui_slideshow_display` + * `vgui_text_display` + * `vgui_world_text_panel` * `env_xen_healpool` * `env_xen_healshower` * `info_darknessmode_lightsource` @@ -698,8 +774,11 @@ * `comp_propcombine_volume` - Sceneset (`comp_choreo_sceneset`) - Logic - * `comp_sequential_call` + * `comp_adv_output` + * `comp_flicker` * `comp_numeric_transition` + * `comp_player_input_helper` + * `comp_sequential_call` - Setters * `comp_scriptvar_setter` * `comp_kv_setter` @@ -750,26 +829,27 @@ * `point_worldtext` * `point_message` * `point_message_localized` - - Doors - * `func_door` - * `func_door_rotating` - * `prop_door_rotating_checkpoint` - * `prop_door_rotating` - * `prop_testchamber_door` - * `prop_linked_portal_door` - * `func_lookdoor` - * `asw_door` - Teleporters * `trigger_teleport` * `trigger_teleport_relative` * `point_teleport` * `prop_linked_portal_door` * `linked_portal_door` + * `func_fake_worldportal` - Ladders * `func_ladder` * `func_ladderendpoint` * `func_useableladder` * `info_ladder_dismount` + - Doors + * `func_door` + * `func_door_rotating` + * `prop_door_rotating_checkpoint` + * `prop_door_rotating` + * `prop_testchamber_door` + * `prop_linked_portal_door` + * `func_lookdoor` + * `asw_door` - Buttons * `func_button` * `momentary_rot_button` @@ -778,6 +858,7 @@ * `func_weight_button` * `infra_button` * `prop_interactable` + * `prop_tsp_button` - Autosaves * `trigger_autosave` * `logic_autosave` @@ -795,10 +876,16 @@ * `item_suitcharger` * `prop_hev_charger` - Thumpers (`prop_thumper`) - - Portals (`prop_portal`) + - Portals + * `prop_portal` + * `prop_linked_portal_door` + * `linked_portal_door` + * `func_fake_worldportal` - Portal Control * `func_noportal_volume` * `func_portal_bumper` + * `info_placement_helper` + * `sixense_info_placement_helper` - Radiation Ammo Charger (`prop_radiation_charger`) - Headcrab Canister (`env_headcrabcanister`) - Cubes @@ -865,7 +952,12 @@ * `info_coop_spawn` * `info_player_start` * `info_player_teamspawn` + * `info_l4d1_survivor_spawn` - Wilson Scanners (`prop_wilson_scanner`) + - RT Camera + * `func_monitor` + * `info_camera_link` + * `point_camera` - Tool Brushes - Areaportals * `func_areaportal` @@ -873,9 +965,11 @@ * `func_areaportal_oneway` - Occluders (`func_occluder`) - Propcombine (`comp_propcombine_volume`) + - Viscluster (`func_viscluster`) - Vehicles * `prop_vehicle_airboat` * `prop_vehicle_apc` + * `prop_vehicle_drivable_apc` * `prop_vehicle_crane` * `infra_crane` * `infra_boat` @@ -959,27 +1053,24 @@ * `weapon_slam` # L4D - * `prop_minigun` * `prop_minigun_l4d1` - * `weapon_pain_pills_spawn` + * `prop_minigun` * `weapon_adrenaline_spawn` - * `weapon_item_spawn` - * `weapon_scavenge_item_spawn` - * `weapon_upgradepack_explosive_spawn` - * `weapon_upgradepack_incendiary_spawn` - * `weapon_first_aid_kit` - * `weapon_grenade_launcher` * `weapon_ammo_spawn` * `weapon_autoshotgun_spawn` * `weapon_chainsaw_spawn` * `weapon_defibrillator_spawn` * `weapon_first_aid_kit_spawn` + * `weapon_first_aid_kit` * `weapon_gascan_spawn` * `weapon_grenade_launcher_spawn` + * `weapon_grenade_launcher` * `weapon_hunting_rifle_spawn` + * `weapon_item_spawn` * `weapon_melee_spawn` * `weapon_molotov_spawn` * `weapon_pain_pills_spawn` + * `weapon_pain_pills_spawn` * `weapon_pipe_bomb_spawn` * `weapon_pistol_magnum_spawn` * `weapon_pistol_spawn` @@ -987,13 +1078,20 @@ * `weapon_rifle_ak47_spawn` * `weapon_rifle_desert_spawn` * `weapon_rifle_m60_spawn` + * `weapon_rifle_sg552_spawn` * `weapon_rifle_spawn` + * `weapon_scavenge_item_spawn` * `weapon_shotgun_chrome_spawn` * `weapon_shotgun_spas_spawn` + * `weapon_smg_mp5_spawn` * `weapon_smg_silenced_spawn` * `weapon_smg_spawn` + * `weapon_sniper_awp_spawn` * `weapon_sniper_military_spawn` + * `weapon_sniper_scout_spawn` * `weapon_spawn` + * `weapon_upgradepack_explosive_spawn` + * `weapon_upgradepack_incendiary_spawn` * `weapon_vomitjar_spawn` # Mesa @@ -1021,6 +1119,19 @@ * `weapon_manhacktoss` * `weapon_pulsepistol` * `weapon_smg2` + + # Alien Swarm + * `asw_pickup_autogun` + * `asw_pickup_chainsaw` + * `asw_pickup_flamer` + * `asw_pickup_mines` + * `asw_pickup_mining_laser` + * `asw_pickup_pdw` + * `asw_pickup_pistol` + * `asw_pickup_prifle` + * `asw_pickup_rifle` + * `asw_pickup_shotgun` + * `asw_pickup_vindicator` - Items - Ammunition * `item_ammo_357` @@ -1042,6 +1153,15 @@ * `item_box_lrounds` * `item_box_mrounds` * `item_box_srounds` + * `asw_ammo_autogun` + * `asw_ammo_drop` + * `asw_ammo_flamer` + * `asw_ammo_mining_laser` + * `asw_ammo_pdw` + * `asw_ammo_pistol` + * `asw_ammo_rifle` + * `asw_ammo_shotgun` + * `asw_ammo_vindicator` - Large Ammunition * `item_ammo_357_large` * `item_ammo_ar2_large` @@ -1120,6 +1240,16 @@ * `tf_projectile_throwable_repel` - Health and Ammo Kit (`item_healthammokit`) + - Pickups + * `asw_pickup_medkit` + * `asw_pickup_fire_extinguisher` + * `asw_pickup_flares` + * `asw_pickup_flashlight` + * `asw_pickup_grenades` + * `asw_pickup_sentry` + * `asw_pickup_stim` + * `asw_pickup_welder` + - AA Batteries (`item_aa_batteries`) - D Batteries (`item_d_batteries`) - Geocache (`infra_geocache`) @@ -1143,17 +1273,20 @@ - World Details - Props - Dynamic - * `prop_dynamic` + * `dynamic_prop` + * `prop_car_alarm` + * `prop_dropship_container` * `prop_dynamic_glow` + * `prop_dynamic_kzmod` * `prop_dynamic_ornament` * `prop_dynamic_override` - * `dynamic_prop` - * `prop_car_alarm` - * `prop_train_awesome` - * `prop_train_apprehension` - * `prop_retinalscanner` + * `prop_dynamic` + * `prop_hallucination` * `prop_interactable` + * `prop_retinalscanner` * `prop_scalable` + * `prop_train_apprehension` + * `prop_train_awesome` - Nihilanth Battle * `nihilanth_pylon` * `nihiportalsbase` @@ -1163,17 +1296,17 @@ - Ragdoll * `prop_ragdoll` * `prop_ragdoll_original` - * `prop_physics` + * `physics_cannister` * `physics_prop` + * `prop_car_glass` * `prop_physics_multiplayer` * `prop_physics_override` * `prop_physics_paintable` * `prop_physics_respawnable` + * `prop_physics` * `prop_sphere` * `simple_physics_brush` * `simple_physics_prop` - * `prop_car_glass` - * `physics_cannister` - Static * `prop_detail` * `prop_detail_sprite` From 44dd623e7f4bf32737f2ff9965670f4fb5792600 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 5 Jun 2023 17:10:10 +1000 Subject: [PATCH 108/243] Make these constants final --- transforms/comp_flicker.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/transforms/comp_flicker.py b/transforms/comp_flicker.py index d5804af2e..12d5fa31a 100644 --- a/transforms/comp_flicker.py +++ b/transforms/comp_flicker.py @@ -1,4 +1,5 @@ """Fires on/off inputs repeatedly to simulate a flicker-on effect.""" +from typing_extensions import Final import random from srctools import Output, lerp, logger, conv_float @@ -6,15 +7,15 @@ from hammeraddons.bsp_transform import trans, Context LOGGER = logger.get_logger(__name__) -INP_TURN_OFF = 'FireUser1' -OUT_TURN_OFF = 'OnUser1' -INP_TURN_ON = 'FireUser2' -OUT_TURN_ON = 'OnUser2' +INP_TURN_OFF: Final = 'FireUser1' +OUT_TURN_OFF: Final = 'OnUser1' +INP_TURN_ON: Final = 'FireUser2' +OUT_TURN_ON: Final = 'OnUser2' -INP_FLICK_OFF = 'FireUser3' -OUT_FLICK_OFF = 'OnUser3' -INP_FLICK_ON = 'FireUser4' -OUT_FLICK_ON = 'OnUser4' +INP_FLICK_OFF: Final = 'FireUser3' +OUT_FLICK_OFF: Final = 'OnUser3' +INP_FLICK_ON: Final = 'FireUser4' +OUT_FLICK_ON: Final = 'OnUser4' @trans('comp_flicker') @@ -60,7 +61,10 @@ def comp_flicker(ctx: Context) -> None: out.output = OUT_FLICK_ON out.delay += total_time else: - LOGGER.warning('Unknown comp_flicker output "{}" for "{}"', out.output, ent_name) + LOGGER.warning( + 'Unknown comp_flicker output "{}" for "{}"', + out.output, ent_name, + ) mdl_name = ent['target_mdl'] if mdl_name: @@ -78,7 +82,11 @@ def comp_flicker(ctx: Context) -> None: limit = 0 while time < total_time: state = not state - ent.add_out(Output(out_name, '!self', INP_TURN_ON if state else INP_TURN_OFF, delay=time)) + ent.add_out(Output( + out_name, '!self', + INP_TURN_ON if state else INP_TURN_OFF, + delay=time, + )) delay = lerp(time, min_point, max_point, flicker_min, flicker_max) time += delay + random.uniform(-variance, variance) @@ -105,4 +113,8 @@ def comp_flicker(ctx: Context) -> None: break # Force on exactly at the end time. - ent.add_out(Output(out_name, '!self', INP_TURN_OFF if start_state else INP_TURN_ON, delay=time)) + ent.add_out(Output( + out_name, '!self', + INP_TURN_OFF if start_state else INP_TURN_ON, + delay=time, + )) From 626915e9ad7803c22c4dd19f689a8318b6fb716d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 7 Jun 2023 11:02:41 +1000 Subject: [PATCH 109/243] Change sky_camera to be more visible with tools/skybox behind it --- CHANGELOG.md | 1 + .../materials/models/editor/ts/sky_camera.vtf | Bin 87588 -> 11144 bytes materialsrc/models/editor/ts/sky_camera.pdn | Bin 0 -> 27236 bytes 3 files changed, 1 insertion(+) create mode 100644 materialsrc/models/editor/ts/sky_camera.pdn diff --git a/CHANGELOG.md b/CHANGELOG.md index d7216f1e1..5b56297e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Areaportal windows will automatically force the brushes used to nonsolid, and clear some physics data. * Propcombine will no longer merge props found in different areaportal areas. This allows props on the outside of a building to be culled when inside, or vice versa. +* Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. -------------------- diff --git a/hammer/materials/models/editor/ts/sky_camera.vtf b/hammer/materials/models/editor/ts/sky_camera.vtf index 15a77645182da7db552f4ea967ba38387002a77f..d9c5670698632273e6e10ea7a80f9f72a8246a6b 100644 GIT binary patch literal 11144 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

a)5e>xq+>geWJ@Z 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 literal 0 HcmV?d00001 From 13f148f8b0193438c238db16dcaa262e8289e2ce Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 8 Jun 2023 16:23:57 +1000 Subject: [PATCH 110/243] This spawnflag only affects trigger_push --- fgd/bases/TriggerOnce.fgd | 3 +-- fgd/brush/trigger/trigger_push.fgd | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/bases/TriggerOnce.fgd b/fgd/bases/TriggerOnce.fgd index 10834abc0..e8dd0fe21 100644 --- a/fgd/bases/TriggerOnce.fgd +++ b/fgd/bases/TriggerOnce.fgd @@ -11,7 +11,7 @@ 2: "NPCs" : 0 [!ASW] 2: "Marines and Aliens" : 0 [ASW] 4: "func_pushable" : 0 - 8: "Physics Objects" : 0 + 8: "VPhysics Objects" : 0 8192: "Items (weapons, items, projectiles)" : 0 [MBase] 16: "Only player ally NPCs" : 0 [!ASW] 16: "Only marines" : 1 [ASW] @@ -20,7 +20,6 @@ 512: "Only clients *not* in vehicles" : 0 [!ASW] 1024: "Physics debris" : 0 2048: "Only NPCs in vehicles (respects player ally flag)" : 0 - 4096: "Correctly account for object mass (trigger_push used to assume 100Kg) and multiple component physobjs (car, blob...)" : 1 [!CSGO, !TF2] 4096: "Disallow Bots" : 0 [CSGO, TF2, MESA] ] diff --git a/fgd/brush/trigger/trigger_push.fgd b/fgd/brush/trigger/trigger_push.fgd index 13a25e94e..daeb99cb7 100644 --- a/fgd/brush/trigger/trigger_push.fgd +++ b/fgd/brush/trigger/trigger_push.fgd @@ -4,9 +4,10 @@ pushdir(angle) : "Push Direction (Pitch Yaw Roll)" : "0 0 0" : "Angles indicating the direction to push touched entities." spawnflags(flags) = [ - 128: "Once Only" : 0 + 128: "Fire once, then delete trigger" : 0 256: "Affects Ladders (Half-Life 2)" : 0 512: "No gravity while in contact (Players only)" : 0 [KZ] + 4096: "Correctly account for object mass (trigger_push used to assume 100Kg) and multiple component physobjs (car, blob...)" : 1 [!CSGO, !TF2] ] surfacecontactmode[engine](integer): "Require player contact with a surface?" : 0 : "Push triggers by default apply a boost to the player once they exit the bounds of the push. This boost is the effective force that launches the player. However, by doubleducking in a short push trigger, a player may be able to cause the boost to apply twice rapidly, causing unintended velocity to be applied. These settings allow you to require the player to be in contact with a surface to receive velocity (and not receive it if in the air, such as during a doubleduck)." From a961eba3316b631281ba64567fac642b44077c57 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 8 Jun 2023 18:35:27 +1000 Subject: [PATCH 111/243] Restore missing projected texture shadow keyvalues. --- CHANGELOG.md | 1 + fgd/bases/BaseEntityAnimating.fgd | 12 ++++++++---- fgd/point/prop/prop_static.fgd | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b56297e7..5283d2739 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Areaportal windows will automatically force the brushes used to nonsolid, and clear some physics data. * Propcombine will no longer merge props found in different areaportal areas. This allows props on the outside of a building to be culled when inside, or vice versa. +* Restore missing projected texture shadow keyvalues. * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. -------------------- diff --git a/fgd/bases/BaseEntityAnimating.fgd b/fgd/bases/BaseEntityAnimating.fgd index ac3ccc884..db793a5e2 100644 --- a/fgd/bases/BaseEntityAnimating.fgd +++ b/fgd/bases/BaseEntityAnimating.fgd @@ -63,9 +63,13 @@ fadescale(float) : "Fade Scale" : : "If specified in the worldspawn, or if the engine is running below DirectX 8, entities will fade out even if the fade distances above aren't specified. This value gives more control over when this happens: numbers smaller than 1 cause the entity to fade out at further distances, and greater than 1 cause it to fade out at closer distances. Using 0 turns off the forced fade altogether." shadowcastdist[complete](integer) : "Shadow Cast Distance" : 0 : "Sets how far the entity casts dynamic shadows, in units. 0 means default distance from the shadow_control entity." - disableshadows(boolean) : "Disable Shadows?" : 0 : "Prevent the entity from creating cheap render-to-texture/dynamic shadows." - disablereceiveshadows(boolean) : "Disable Receiving Shadows?" : 0 : "Prevents dynamic shadows (e.g. player and prop shadows) from appearing on this entity." - disableshadowdepth[since_L4D, +complete](boolean) : "Disable ShadowDepth" : 0 : "Used to disable rendering into shadow depth (for flashlight) for this entity." + + // Disambiguate from projected textures if present. + disableshadows[until_L4D](boolean) : "Disable Producing Shadows?" : 0 : "Prevent the entity from creating cheap render-to-texture/dynamic shadows." + disableshadows[since_L4D](boolean) : "Disable Producing Cheap Shadows?" : 0 : "Prevent the entity from creating cheap render-to-texture/dynamic shadows." + + disablereceiveshadows(boolean) : "Disable Receiving Shadows?" : 0 : "Prevents shadows (cheap & projected texture) from appearing on this entity." + disableshadowdepth[since_L4D](boolean) : "No Affecting Proj Texs" : 0 : "Prevent this entity from affecting projected texture shadows." shadowdepthnocache[since_L4D, +complete](choices) : "Projected Texture Cache" : "0" : "Used to hint projected texture system whether it is sufficient to cache shadow volume of this entity or to force render it every frame instead." = [ 0: "Default" @@ -73,7 +77,7 @@ 2: "Cache it = render only once" ] shadowdepthnocache[engine](integer): "Projected Texture Cache": 0 - disableflashlight[since_L4D, MBase](boolean) : "Disable flashlight" : 0 : "Used to disable flashlight (env_projectedtexture) lighting and shadows on this entity." + disableflashlight[since_L4D, MBase](boolean) : "No Recieving Proj Texs" : 0 : "Used to disable flashlight (env_projectedtexture) lighting and shadows on this entity." linedivider_anim[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" diff --git a/fgd/point/prop/prop_static.fgd b/fgd/point/prop/prop_static.fgd index d1f7f5e52..8663c96f4 100644 --- a/fgd/point/prop/prop_static.fgd +++ b/fgd/point/prop/prop_static.fgd @@ -36,7 +36,6 @@ uniformscale[PROP_SCALING](float) : "Uniform Scale Override" : 1 : "Resize the static prop." - solid[engine](integer) : "Collisions" : 6 solid(choices) : "Collisions" : 6 = [ @@ -119,7 +118,11 @@ linedivider_light[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" - disableshadows(boolean) : "Disable Shadows" : 0 + disableshadows(boolean) : "Disable Shadows" : 0 : "Prevent this prop from obstructing light to cast shadows." + // Doesn't appear to work? + disableshadowdepth[since_L4D, +complete](boolean) : "No Affecting Proj Texs" : 0 : "Prevent this entity from affecting projected texture shadows." + disableflashlight[since_L4D](boolean) : "No Recieving Proj Texs" : 0 : "Prevent this prop from recieving projected texture shadows." + disablevertexlighting(boolean) : "Disable Vertex lighting" : 0 : "Disable per-vertex lighting on this prop." disableselfshadowing(boolean) : "Disable Self-Shadowing" : 0 : "When vertex lighting is enabled, prevent the geometry from self-shadowing -- casting shadows onto itself." ignorenormals(boolean) : "Ignore Surface Normal" : 0 : "When vertex lighting is enabled, ignore the surface normal of faces when calculating the vertex lighting. Useful for thin, translucent objects such as leaves on foliage props." From 20c403a8744fdbc9a789316c85cf7f20e9a0d02c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 10 Jun 2023 10:34:53 +1000 Subject: [PATCH 112/243] Remove duplicate key --- .github/workflows/build-postcompiler.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build-postcompiler.yml b/.github/workflows/build-postcompiler.yml index cc12afc8b..a09ce3e90 100644 --- a/.github/workflows/build-postcompiler.yml +++ b/.github/workflows/build-postcompiler.yml @@ -17,10 +17,6 @@ permissions: contents: read pull-requests: read -permissions: - contents: read - pull-requests: read - jobs: freeze: strategy: From cef77d32dc4dde38389d272c8872d4e71a2eb102 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 13 Jun 2023 15:10:18 +1000 Subject: [PATCH 113/243] Automatically set info_target transmit to client flags. --- CHANGELOG.md | 1 + transforms/client_target.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 transforms/client_target.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5283d2739..9c8510c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Propcombine will no longer merge props found in different areaportal areas. This allows props on the outside of a building to be culled when inside, or vice versa. * Restore missing projected texture shadow keyvalues. +* Automatically set the "transmit to client" flag for `info_target`s used as particle system destinations. * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. -------------------- diff --git a/transforms/client_target.py b/transforms/client_target.py new file mode 100644 index 000000000..84df4628d --- /dev/null +++ b/transforms/client_target.py @@ -0,0 +1,28 @@ +"""Automatically enable the clientside flag on info_target entities.""" +from hammeraddons.bsp_transform import Context, trans +from srctools import Entity, conv_int +from srctools.logger import get_logger + +LOGGER = get_logger(__name__) + +KEYVALUES = [ + ('info_particle_system', [f'cpoint{x}' for x in range(1, 64)]), + ('env_instructor_hint', ['hint_target']), +] + + +@trans('Enable info_target clientside') +def clientside_info_target(ctx: Context) -> None: + """Automatically enable the clientside flag on info_target entities. + """ + for clsname, keys in KEYVALUES: + ent: Entity + for ent in ctx.vmf.by_class[clsname]: + for key in keys: + value = ent[key] + if not value: + continue + for target in ctx.vmf.search(value): + if target['classname'].casefold() == 'info_target': + spawnflags = conv_int(target['spawnflags']) + target['spawnflags'] = spawnflags | 1 # Transmit to client (respect PVS) From b45a4ca6eb1840e74ec3c8adc9c7bd2b687621a4 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 16 Jun 2023 14:38:53 +1000 Subject: [PATCH 114/243] PyInstaller needs to be updated for latest Py3.11 versions --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b86a114bb..0205b3c22 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,5 @@ typing_extensions >= 4.2.0 srctools >= 2.3.12 trio >= 0.20.0 trio-typing >= 0.7.0 -pyinstaller >= 5.7.0 +pyinstaller >= 5.12.0 versioningit >= 2.1.0 From 4da8cff48661da8d6598e765bd046298ec902a50 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 16 Jun 2023 14:43:42 +1000 Subject: [PATCH 115/243] Fix #192: Use both specified model and cube type field to find matching cubes --- CHANGELOG.md | 1 + transforms/vactubes/__init__.py | 4 +- transforms/vactubes/objects.py | 83 +++++++++++++++++++++++++++++---- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8510c3d..7eae59fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ * Restore missing projected texture shadow keyvalues. * Automatically set the "transmit to client" flag for `info_target`s used as particle system destinations. * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. +* Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. -------------------- diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index 244236c92..2530a5d95 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -349,8 +349,8 @@ async def vactube_transform(ctx: Context) -> None: cube_model = target.cube['model'].replace('\\', '/') cube_skin = conv_int(target.cube['skin']) try: - cube_name = vac_objects[start_node.group, cube_model, cube_skin].id - except KeyError: + cube_name = objects.find_for_cube(vac_objects, start_node.group, target.cube).id + except LookupError: LOGGER.warning( 'Cube model "{}", skin {} is not a type of cube travelling ' 'in this vactube!\n\n' diff --git a/transforms/vactubes/objects.py b/transforms/vactubes/objects.py index bbc81528d..7ba91b3b9 100644 --- a/transforms/vactubes/objects.py +++ b/transforms/vactubes/objects.py @@ -1,10 +1,12 @@ -from collections import defaultdict +"""Handles configuration for the objects appearing inside vactubes.""" from typing import Optional, Tuple, List, Dict +from typing_extensions import TypeAlias +from collections import defaultdict import os.path import math from srctools.packlist import PackList, FileType -from srctools import Vec, VMF +from srctools import Entity, Vec, VMF, conv_bool, conv_int import srctools.logger from hammeraddons.bsp_transform.packing import make_precache_prop @@ -12,6 +14,23 @@ LOGGER = srctools.logger.get_logger(__name__) +# For prop_weighted_cube, cube type -> model, clean skin, dirty skin +_CUBE_STANDARD = ('models/props/metal_box.mdl', 0, 3) +_CUBE_COMPANION = ('models/props/metal_box.mdl', 1, 1) +_CUBE_REFLECT = ('models/props/reflection_cube.mdl', 0, 1) +_CUBE_SPHERE = ('models/props_gameplay/mp_ball.mdl', 0, 0) +_CUBE_ANTIQUE = ('models/props_underground/underground_weighted_cube.mdl', 0, 0) +CUBE_MODELS_FOR_TYPE = [ + _CUBE_STANDARD, _CUBE_COMPANION, + _CUBE_REFLECT, _CUBE_SPHERE, _CUBE_ANTIQUE, +] +# The same, but using the old skin property. +CUBE_MODELS_FOR_SKIN = [ + _CUBE_STANDARD, _CUBE_COMPANION, + _CUBE_STANDARD, # Standard Activated -> just regular. + _CUBE_REFLECT, _CUBE_SPHERE, _CUBE_ANTIQUE, +] + class VacObject: """An object that can appear in vactubes.""" @@ -38,7 +57,7 @@ def __init__( self.skin_drop = skin_drop def __repr__(self) -> str: - return ''.format(os.path.basename(self.model_vac)) + return f'' def make_code(self) -> str: """Generate the code to construct this object in VScript.""" @@ -52,11 +71,10 @@ def make_code(self) -> str: ) -def parse(vmf: VMF, pack: PackList) -> Tuple[ - int, - Dict[Tuple[str, str, int], VacObject], - Dict[str, str], -]: +VacObjectDict: TypeAlias = Dict[Tuple[str, str, int], VacObject] + + +def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]]: """Parse out the cube objects from the map. The return value is the number of objects, a dict of objects, and the @@ -112,3 +130,52 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[ codes[group] = pack.inject_vscript('\n'.join(code)) return sum(map(len, vac_objects.values())), cube_objects, codes + + +def find_for_cube(vac_objects: VacObjectDict, group: str, cube: Entity) -> VacObject: + """Find an object that matches the specified cube entity.""" + potentials: List[Tuple[str, int]] = [] + # Try what's set in the keyvalues first. But if it's a default value, skip so that we use + # the cube type first. + model = cube['model'].replace('\\', '/') + if model not in ('', 'models/props/metal_box.mdl'): + potentials.append((model, conv_int(cube['skin']))) + + if cube['classname'] == 'prop_weighted_cube': + model = '' + clean = rusty = 0 + if conv_bool(cube['newskins']): + cube_type = conv_int(cube['cubetype']) + if cube_type != 6: # Used for custom cubes, no error. + try: + model, clean, rusty = CUBE_MODELS_FOR_TYPE[cube_type] + except KeyError: + LOGGER.warning( + 'Cube "{}" at ({}) has unknown cube type {}!', + cube['targetname'], cube['origin'], cube_type, + ) + else: + # Old skin-based lookup + cube_skin = conv_int(cube['skin']) + try: + model, clean, rusty = CUBE_MODELS_FOR_SKIN[cube_skin] + except KeyError: + LOGGER.warning( + 'Cube "{}" at ({}) has unknown old-style cube skin {}!', + cube['targetname'], cube['origin'], cube_skin, + ) + if model: + potentials.append((model, rusty if conv_bool(cube['skintype']) else clean)) + elif cube['classname'] == 'prop_monster_box': + # Hardcoded model. Prefer box form. + potentials += [ + ('models/npcs/monsters/monster_a_box.mdl', 0), + ('models/npcs/monsters/monster_a.mdl', 0), + ] + + for model, skin in potentials: + try: + return vac_objects[group, model, skin] + except KeyError: + pass + raise LookupError('No matching object!') From 8a0da1b6b2f9162274e8473823293ae09eb11341 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 16 Jun 2023 14:52:17 +1000 Subject: [PATCH 116/243] Close #211: Re-enable info_lighting_relative --- fgd/point/info/info_lighting_relative.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/info/info_lighting_relative.fgd b/fgd/point/info/info_lighting_relative.fgd index 637145a83..23dfeca8e 100644 --- a/fgd/point/info/info_lighting_relative.fgd +++ b/fgd/point/info/info_lighting_relative.fgd @@ -1,6 +1,6 @@ @PointClass base(BaseEntityPoint) - appliesto(until_L4D, +complete) + appliesto(until_L4D) line(255 255 255, targetname, lightinglandmark) iconsprite("editor/ficool2/info_lighting_relative.vmt") halfgridsnap From cb33f5965011c8fef0f8790a80487719b811f9c4 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 20 Jun 2023 13:28:55 +1000 Subject: [PATCH 117/243] Fix #177: Pass Cancel input through to all comp_choreo_sceneset scenes --- transforms/sceneset.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/transforms/sceneset.py b/transforms/sceneset.py index 2e5fc5309..829c2aa8b 100644 --- a/transforms/sceneset.py +++ b/transforms/sceneset.py @@ -1,5 +1,5 @@ """Implement comp_choreo_sceneset.""" -from srctools import Output, conv_bool, conv_float +from srctools import Entity, Output, conv_bool, conv_float from srctools.logger import get_logger from hammeraddons.bsp_transform import trans, Context @@ -10,6 +10,7 @@ @trans('comp_choreo_sceneset') def sceneset(ctx: Context): """Chains a set of choreographed scenes together.""" + ent: Entity for ent in ctx.vmf.by_class['comp_choreo_sceneset']: scenes = [ ent['scene{:02}'.format(i)] @@ -32,43 +33,45 @@ def sceneset(ctx: Context): ent.remove() - start_ent = None + scene_ents: list[Entity] = [] - name = ent['targetname'] or '_choreo_{}'.format(ent.id) + name = ent.make_unique('_choreo')['targetname'] for i, scene in enumerate(scenes): part = ctx.vmf.create_ent( classname='logic_choreographed_scene', targetname=( - '{}_{}'.format(name, i) + f'{name}_{i}' if i > 0 else name ), origin=ent['origin'], scenefile=scene, ) + scene_ents.append(part) if i + 1 < len(scenes): part.add_out(Output( 'OnCompletion', - '{}_{}'.format(name, i+1), + f'{name}_{i + 1}', 'Start', delay=delay, )) if only_once: - # When started blank the name so it can't be triggered, - # then clean up after finished + # When started blank the name so that it can't be triggered, then clean up after + # it's finished part.add_out( Output('OnStart', '!self', 'AddOutput', 'targetname '), Output('OnCompletion', '!self', 'Kill'), ) - if start_ent is None: - start_ent = part - - assert start_ent is not None, "Has scenes but none made?" for out in ent.outputs: if out.output.casefold() == 'onstart': - start_ent.add_out(out) + scene_ents[0].add_out(out) elif out.output.casefold() == 'onfinish': - # Part is the last in the loop. out.output = 'OnCompletion' - part.add_out(out) + scene_ents[-1].add_out(out) + + # Make firing cancel at the first scene also cancel the others. + ctx.add_io_remap(name, *[ + Output('Cancel', ent, 'Cancel') + for ent in scene_ents[1:] + ], remove=False) From 4ce39172c7f012e634238c10a8708fd2e1304340 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 21 Jun 2023 17:09:28 +1000 Subject: [PATCH 118/243] Move IO remap logic into a method --- src/hammeraddons/bsp_transform/__init__.py | 96 ++++++++++++---------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index e467d7379..6d1fde1eb 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -118,6 +118,58 @@ def add_code(self, ent: Entity, code: str) -> None: else: self._ent_code[ent] = '{}\n{}'.format(existing, code) + def _apply_remaps(self) -> None: + """Apply all the IO remaps.""" + # Always disallow remaps now. + self._allow_remaps = False + + if not self._io_remaps: + return + + LOGGER.info('Remapping outputs...') + for (name, inp_name), outs in self._io_remaps.items(): + LOGGER.debug('Remap {}.{} = {}', name, inp_name, outs) + + for ent in self.vmf.entities: + todo = ent.outputs[:] + # Recursively convert only up to 500 times. + # Arbitrary limit, should be sufficient. + for _ in range(500): + if not todo: + break + deferred = [] + for out in todo: + try: + remaps, should_remove = self._io_remaps[ + out.target.casefold(), + out.input.casefold(), + ] + except KeyError: + continue + if should_remove: + ent.outputs.remove(out) + for rep_out in remaps: + new_out = Output( + out.output, + rep_out.target, + rep_out.input, + rep_out.params or out.params, + out.delay + rep_out.delay, + times=out.times if rep_out.times == -1 + else rep_out.times if out.times == -1 + else min(out.times, rep_out.times), + ) + ent.outputs.append(new_out) + deferred.append(new_out) + todo = deferred + else: + LOGGER.error( + 'Entity "{}" ({}) @ {} has infinite loop when expanding ' + ' compiler outputs to real ones! Final output list: \n{}', + ent['targetname'], ent['classname'], ent['origin'], + '\n'.join(['* {}\n'.format(out) for out in ent.outputs]) + ) + TransFunc = Callable[[Context], Awaitable[None]] TransFuncOrSync = Callable[[Context], Optional[Awaitable[None]]] @@ -180,49 +232,7 @@ async def run_transformations( init_scripts.append(pack.inject_vscript(code.replace('`', '"'))) ent['vscripts'] = ' '.join(init_scripts) - if context._io_remaps: - LOGGER.info('Remapping outputs...') - for (name, inp_name), outs in context._io_remaps.items(): - LOGGER.debug('Remap {}.{} = {}', name, inp_name, outs) - for ent in vmf.entities: - todo = ent.outputs[:] - # Recursively convert only up to 500 times. - # Arbitrary limit, should be sufficient. - for _ in range(500): - if not todo: - break - deferred = [] - for out in todo: - try: - remaps, should_remove = context._io_remaps[ - out.target.casefold(), - out.input.casefold(), - ] - except KeyError: - continue - if should_remove: - ent.outputs.remove(out) - for rep_out in remaps: - new_out = Output( - out.output, - rep_out.target, - rep_out.input, - rep_out.params or out.params, - out.delay + rep_out.delay, - times=out.times if rep_out.times == -1 - else rep_out.times if out.times == -1 - else min(out.times, rep_out.times), - ) - ent.outputs.append(new_out) - deferred.append(new_out) - todo = deferred - else: - LOGGER.error( - 'Entity "{}" ({}) @ {} has infinite loop when expanding ' - ' compiler outputs to real ones! Final output list: \n{}', - ent['targetname'], ent['classname'], ent['origin'], - '\n'.join(['* {}\n'.format(out) for out in ent.outputs]) - ) + context._apply_remaps() def _load() -> None: From 81cec590d174e2a86ae879b7e33d29b9a08ca958 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 21 Jun 2023 17:35:49 +1000 Subject: [PATCH 119/243] Allow defining io remaps using a function --- src/hammeraddons/bsp_transform/__init__.py | 64 +++++++++++++++++----- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index 6d1fde1eb..cb84449fa 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -1,9 +1,11 @@ """Transformations that can be applied to the BSP file.""" +from typing import Awaitable, Callable, Dict, FrozenSet, List, Mapping, Optional, Tuple, Union +from typing_extensions import TypeAlias import warnings -from typing import Awaitable, Callable, Dict, FrozenSet, List, Mapping, Optional, Tuple from pathlib import Path import inspect + from srctools import FGD, VMF, EmptyMapping, Entity, FileSystem, Keyvalues, Output, conv_bool from srctools.bsp import BSP from srctools.game import Game @@ -12,6 +14,7 @@ LOGGER = get_logger(__name__, 'bsp_trans') +RemapFunc: TypeAlias = Callable[[Entity, Output], List[Output]] __all__ = [ 'check_control_enabled', @@ -63,7 +66,8 @@ def __init__( self.studiomdl = studiomdl_loc self.config = Keyvalues.root() - self._io_remaps: Dict[Tuple[str, str], Tuple[List[Output], bool]] = {} + self._io_remaps: Dict[Tuple[str, str], Tuple[List[Union[Output, RemapFunc]], bool]] = {} + self._allow_remaps = True self._ent_code: Dict[Entity, str] = {} @property @@ -73,6 +77,24 @@ def fgd(self) -> FGD: self._fgd = FGD.engine_dbase() return self._fgd + def _add_io_remap( + self, name: str, inp_name: str, + value: Union[Output, RemapFunc], + remove: bool, + ) -> None: + if not self._allow_remaps: + raise RecursionError('Cannot add more remaps from a remap callback!') + key = (name, inp_name) + try: + out_list, old_remove = self._io_remaps[key] + except KeyError: + self._io_remaps[key] = ([value], remove) + else: + out_list.append(value) + # Only allow removing if all remaps have requested it. + if old_remove and not remove: + self._io_remaps[key] = (out_list, False) + def add_io_remap(self, name: str, *outputs: Output, remove: bool=True) -> None: """Register an output to be replaced. @@ -89,19 +111,23 @@ def add_io_remap(self, name: str, *outputs: Output, remove: bool=True) -> None: for out in outputs: inp_name = out.output.casefold() out.output = '' - key = (name, inp_name) - try: - out_list, old_remove = self._io_remaps[key] - except KeyError: - self._io_remaps[key] = ([out], remove) - else: - out_list.append(out) - # Only allow removing if all remaps have requested it. - if old_remove and not remove: - self._io_remaps[key] = (out_list, False) + self._add_io_remap(name, inp_name, out, remove) + + def add_io_remap_func(self, name: str, inp_name: str, func: RemapFunc, remove: bool = True) -> None: + """Register an output to be dynamically replaced, using a function. + + This allows varying the output for each input entity. The entity and the relevant output + are passed to the function for reference, but the output and entity should not be modified. + Instead, return new outputs from the function, which are merged with the original. + """ + if name and inp_name: + self._add_io_remap(name.casefold(), inp_name.casefold(), func, remove) def add_io_remap_removal(self, name: str, inp_name: str) -> None: """Special case of add_io_remap, request that this output should be removed.""" + if not self._allow_remaps: + raise RecursionError('Cannot add more remaps from a remap callback!') + key = (name.casefold(), inp_name.casefold()) if key not in self._io_remaps: self._io_remaps[key] = ([], True) @@ -131,12 +157,12 @@ def _apply_remaps(self) -> None: LOGGER.debug('Remap {}.{} = {}', name, inp_name, outs) for ent in self.vmf.entities: + if not ent.outputs: # Early out. + continue todo = ent.outputs[:] # Recursively convert only up to 500 times. # Arbitrary limit, should be sufficient. for _ in range(500): - if not todo: - break deferred = [] for out in todo: try: @@ -148,6 +174,14 @@ def _apply_remaps(self) -> None: continue if should_remove: ent.outputs.remove(out) + collapsed_remaps: list[Output] = [] + out_copy = out.copy() # Don't allow remapping functions to modify this. + for remap in remaps: + if isinstance(remap, Output): + collapsed_remaps.append(remap) + else: + collapsed_remaps.extend(remap(ent, out_copy)) + for rep_out in remaps: new_out = Output( out.output, @@ -161,6 +195,8 @@ def _apply_remaps(self) -> None: ) ent.outputs.append(new_out) deferred.append(new_out) + if not deferred: + break todo = deferred else: LOGGER.error( From 5e13843e7c5e2305cc7f81c0bd2ed4343a612345 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 21 Jun 2023 15:47:24 -0700 Subject: [PATCH 120/243] Fix #220: New Gmod stuff --- fgd/bases/BaseEntityAnimating.fgd | 4 ++-- fgd/bases/BaseEntityBrush.fgd | 4 ++-- fgd/bases/BaseEntityPoint.fgd | 4 ++-- fgd/bases/BaseEntityVisBrush.fgd | 4 ++-- fgd/bases/BaseTrain.fgd | 14 +++++++------- fgd/bases/RenderFields.fgd | 4 ++-- fgd/bases/ToggleDraw.fgd | 4 ++-- fgd/point/filter/filter_activator_context.fgd | 2 +- fgd/point/filter/filter_activator_model.fgd | 2 +- fgd/point/prop/prop_sphere.fgd | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/fgd/bases/BaseEntityAnimating.fgd b/fgd/bases/BaseEntityAnimating.fgd index db793a5e2..aec3785c5 100644 --- a/fgd/bases/BaseEntityAnimating.fgd +++ b/fgd/bases/BaseEntityAnimating.fgd @@ -97,8 +97,8 @@ input DisableShadow(void) : "Allows the entity to draw a render target (dynamic) shadow." input EnableShadow(void) : "Prevents the entity from drawing a render target (dynamic) shadow." - input DisableReceivingFlashlight[since_L4D, MBase](void) : "This object will not recieve light or shadows from projected textures (flashlights)." - input EnableReceivingFlashlight[since_L4D, MBase](void) : "This object may recieve light or shadows from projected textures (flashlights)." + input DisableReceivingFlashlight[since_L4D, MBase, GMod](void) : "This object will not recieve light or shadows from projected textures (flashlights)." + input EnableReceivingFlashlight[since_L4D, MBase, GMod](void) : "This object may recieve light or shadows from projected textures (flashlights)." input AlternativeSorting[SINCE_EP1](boolean) : "Used to attempt to fix sorting problems when rendering. True activates, false deactivates" input SetModelScale[EP1, EP2, MESA, !GMod](vector) : "Takes two values separated by a space. The first is the target model scale. " + diff --git a/fgd/bases/BaseEntityBrush.fgd b/fgd/bases/BaseEntityBrush.fgd index df036edfd..5836a610d 100644 --- a/fgd/bases/BaseEntityBrush.fgd +++ b/fgd/bases/BaseEntityBrush.fgd @@ -60,8 +60,8 @@ input SetParentAttachment(string) : "Change this entity to attach to a specific attachment point on its parent. Entities must be parented before being sent this input. The parameter passed in should be the name of the attachment." input SetParentAttachmentMaintainOffset(string) : "Change this entity to attach to a specific attachment point on it's parent. Entities must be parented before being sent this input. The parameter passed in should be the name of the attachment. The entity will maintain it's position relative to the parent at the time it is attached." input ClearParent(void) : "Removes this entity from the the movement hierarchy, leaving it free to move independently." - input SetLocalAngles[since_ASW, MBase](vector) : "Sets the rotation of the entity relative to the parent's rotation." - input SetLocalOrigin[since_ASW, MBase](vector) : "Sets the position of the entity relative to its parent if one exists. Otherwise relative to the world." + input SetLocalAngles[since_ASW, MBase, GMod](vector) : "Sets the rotation of the entity relative to the parent's rotation." + input SetLocalOrigin[since_ASW, MBase, GMod](vector) : "Sets the position of the entity relative to its parent if one exists. Otherwise relative to the world." input SetAbsAngles[MBase](vector) : "Set this entity's angles, always relative to the world origin." input FreeChildren[MBase](void) : "Unparents all direct children of this entity." diff --git a/fgd/bases/BaseEntityPoint.fgd b/fgd/bases/BaseEntityPoint.fgd index f877e1fd8..e0911b160 100644 --- a/fgd/bases/BaseEntityPoint.fgd +++ b/fgd/bases/BaseEntityPoint.fgd @@ -38,8 +38,8 @@ input SetParentAttachment(string) : "Change this entity to attach to a specific attachment point on its parent. Entities must be parented before being sent this input. The parameter passed in should be the name of the attachment." input SetParentAttachmentMaintainOffset(string) : "Change this entity to attach to a specific attachment point on it's parent. Entities must be parented before being sent this input. The parameter passed in should be the name of the attachment. The entity will maintain it's position relative to the parent at the time it is attached." input ClearParent(void) : "Removes this entity from the the movement hierarchy, leaving it free to move independently." - input SetLocalAngles[since_ASW, MBase](vector) : "Sets the rotation of the entity relative to the parent's rotation." - input SetLocalOrigin[since_ASW, MBase](vector) : "Sets the position of the entity relative to its parent if one exists. Otherwise relative to the world." + input SetLocalAngles[since_ASW, MBase, GMod](vector) : "Sets the rotation of the entity relative to the parent's rotation." + input SetLocalOrigin[since_ASW, MBase, GMod](vector) : "Sets the position of the entity relative to its parent if one exists. Otherwise relative to the world." input SetAbsAngles[MBase](vector) : "Set this entity's angles, always relative to the world origin." // Mapbase BaseEntity changes: diff --git a/fgd/bases/BaseEntityVisBrush.fgd b/fgd/bases/BaseEntityVisBrush.fgd index bc5fe31c9..55e7d731a 100644 --- a/fgd/bases/BaseEntityVisBrush.fgd +++ b/fgd/bases/BaseEntityVisBrush.fgd @@ -48,8 +48,8 @@ // Inputs input DisableShadow(void) : "Allows the entity to draw a render target (dynamic) shadow." input EnableShadow(void) : "Prevents the entity from drawing a render target (dynamic) shadow." - input DisableReceivingFlashlight[since_L4D](void) : "This object will not recieve light or shadows from projected textures (flashlights)." - input EnableReceivingFlashlight[since_L4D](void) : "This object may recieve light or shadows from projected textures (flashlights)." + input DisableReceivingFlashlight[since_L4D, GMod](void) : "This object will not recieve light or shadows from projected textures (flashlights)." + input EnableReceivingFlashlight[since_L4D, GMod](void) : "This object may recieve light or shadows from projected textures (flashlights)." input EnableDamageForces(void) : "Damaging the entity applies physics forces to it." input DisableDamageForces(void) : "Damaging the entity does not apply physics forces to it." diff --git a/fgd/bases/BaseTrain.fgd b/fgd/bases/BaseTrain.fgd index c44e9e9d2..0d8f92f1f 100644 --- a/fgd/bases/BaseTrain.fgd +++ b/fgd/bases/BaseTrain.fgd @@ -77,12 +77,12 @@ // This specific input did get ported in L4D2 input SetSpeedDirAccel[!L4D](float) : "Accel/Decel to the specified speed, as a ratio of max speed. Negative values reverse the direction [-1, 1]" - // Portal 2 inputs, also in CSGO - input TeleportToPathNode[since_P2](string) : "Teleport to a destination and stop there. This can be in a new path." - input MoveToPathNode[since_P2](string) : "Start moving to a destination and stop when you get there. This must be in the same path." - input LockOrientation[since_P2](void) : "Lock the current orientation of the train." - input UnlockOrientation[since_P2](void) : "Unlock the current orientation of the train." - input SetMaxSpeed[since_P2](float) : "Set a new max speed for the train." + // Added in Portal 2, Gmod backported + input TeleportToPathNode[since_P2, GMod](string) : "Teleport to a destination and stop there. This can be in a new path." + input MoveToPathNode[since_P2, GMod](string) : "Start moving to a destination and stop when you get there. This must be in the same path." + input LockOrientation[since_P2, GMod](void) : "Lock the current orientation of the train." + input UnlockOrientation[since_P2, GMod](void) : "Unlock the current orientation of the train." + input SetMaxSpeed[since_P2, GMod](float) : "Set a new max speed for the train." input SetVelocityType[MESA](integer) : "" input EnableControls[MESA](void) : "" @@ -92,5 +92,5 @@ output OnStart(void) : "Fired when the train starts moving in either direction." // The default FGD mislabels this as OnNext output OnNextPoint(string) : "Fires continuously every frame when the train is moving to its next destination." - output OnArrivedAtDestinationNode[since_P2](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." + output OnArrivedAtDestinationNode[since_P2, GMod](void) : "Fired when this train arrives at a destination that was specified by the MoveToPathNode Input." ] diff --git a/fgd/bases/RenderFields.fgd b/fgd/bases/RenderFields.fgd index 444eea39e..c33339c82 100644 --- a/fgd/bases/RenderFields.fgd +++ b/fgd/bases/RenderFields.fgd @@ -117,8 +117,8 @@ input SetViewHideFlags[MBase](integer) : "Sets this entity's view ID nodraw flags (takes raw flag combination)." input AddEffects[MBase](integer) : "Adds an entity effect." input RemoveEffects[MBase](integer) : "Removes an entity effect." - input EnableDraw[!MBase, since_P2](void) : "Draws an entity if it is not drawn." - input DisableDraw[!MBase, since_P2](void) : "Undraws an entity if it is drawn." + input EnableDraw[!MBase, since_P2, GMod](void) : "Draws an entity if it is not drawn." + input DisableDraw[!MBase, since_P2, GMod](void) : "Undraws an entity if it is drawn." input EnableDraw[MBase](void) : "Draws an entity if it is not drawn. Equivalent to RemoveEffects > 32." input DisableDraw[MBase](void) : "Undraws an entity if it is drawn. Equivalent to AddEffects > 32." input AddEFlags[MBase](integer) : "Adds an entity flag. NOTE: Entity flags are not the spawn flags you see in Hammer. Use AddSpawnFlags to add spawnflags." diff --git a/fgd/bases/ToggleDraw.fgd b/fgd/bases/ToggleDraw.fgd index b8561b7f1..a2f3c855e 100644 --- a/fgd/bases/ToggleDraw.fgd +++ b/fgd/bases/ToggleDraw.fgd @@ -2,6 +2,6 @@ @BaseClass = ToggleDraw [ // Inputs - input DisableDraw[since_P2](void) : "Add the EF_NODRAW flag to this entity. Some entities manage this on their own so be aware you can override that value." - input EnableDraw[since_P2](void) : "Remove the EF_NODRAW flag to this entity. Some entities manage this on their own so be aware you can override that value." + input DisableDraw[since_P2, GMod](void) : "Add the EF_NODRAW flag to this entity. Some entities manage this on their own so be aware you can override that value." + input EnableDraw[since_P2, GMod](void) : "Remove the EF_NODRAW flag to this entity. Some entities manage this on their own so be aware you can override that value." ] diff --git a/fgd/point/filter/filter_activator_context.fgd b/fgd/point/filter/filter_activator_context.fgd index 198f14d6b..a276af3d7 100644 --- a/fgd/point/filter/filter_activator_context.fgd +++ b/fgd/point/filter/filter_activator_context.fgd @@ -1,6 +1,6 @@ @FilterClass base(filter_base) autovis(Logic, Filters) - appliesto(since_L4D, MBase) + appliesto(since_L4D, MBase, GMod) iconsprite("editor/filter_context.vmt") = filter_activator_context: "A filter that filters by a context on the activator." [ diff --git a/fgd/point/filter/filter_activator_model.fgd b/fgd/point/filter/filter_activator_model.fgd index 22a5f5344..0055c4ccb 100644 --- a/fgd/point/filter/filter_activator_model.fgd +++ b/fgd/point/filter/filter_activator_model.fgd @@ -1,6 +1,6 @@ @FilterClass base(filter_base) autovis(Logic, Filters) - appliesto(since_L4D2, MBase, Mesa) + appliesto(since_L4D2, MBase, Mesa, GMod) iconsprite("editor/filter_model.vmt") = filter_activator_model: "A filter that filters by the model of the activator." [ diff --git a/fgd/point/prop/prop_sphere.fgd b/fgd/point/prop/prop_sphere.fgd index 9ae393b07..4082f6a47 100644 --- a/fgd/point/prop/prop_sphere.fgd +++ b/fgd/point/prop/prop_sphere.fgd @@ -4,5 +4,5 @@ sphere(radius) = prop_sphere : "A variant of prop_physics which has a perfect sphere shape. It is normally restricted to a radius of 12 units." [ - radius[MBase](float) : "Radius" : 12 : "The size of the sphere collision." + radius[MBase, GMod](float) : "Radius" : 12 : "The size of the sphere collision." ] From 849276f61ab79263b1578d5b18727e4d017cad61 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 21 Jun 2023 19:15:48 -0700 Subject: [PATCH 121/243] Add postcompiler transform for custom cube models (closes #96) --- fgd/point/prop/prop_weighted_cube.fgd | 13 ++++++++-- transforms/custom_cube_models.py | 35 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 transforms/custom_cube_models.py diff --git a/fgd/point/prop/prop_weighted_cube.fgd b/fgd/point/prop/prop_weighted_cube.fgd index 7de432b44..db23c10ea 100644 --- a/fgd/point/prop/prop_weighted_cube.fgd +++ b/fgd/point/prop/prop_weighted_cube.fgd @@ -11,7 +11,7 @@ 2: "[2] Discouragement Redirection Cube" 3: "[3] Edgeless Safety Cube" 4: "[4] Antique Storage Cube" - 6: "[6] Custom model" + 6: "Custom model" ] skintype[engine](boolean) : "Skin Type" : 0 @@ -22,7 +22,7 @@ ] model[engine](studio) : "Model" : "" - model(choices) : "Model" : "models/props/metal_box.mdl" : "The model to show in Hammer, or a custom model for cube type 6." = + model(choices) : "Model" : "models/props/metal_box.mdl" : "The model to show in Hammer. Custom model names can also be typed/pasted in here, and will be shown in-game if the entity is set to use a custom model." = [ "models/props/metal_box.mdl": "Weighted Storage/Companion Cube" "models/props/reflection_cube.mdl": "Discouragement Redirection" @@ -30,6 +30,15 @@ "models/props_underground/underground_weighted_cube.mdl": "Antique" ] + comp_custom_model_type[srctools, -engine](choices) : "[COMP] Custom Model Type" : 0 : "Automatically handles various methods of setting custom cube models. " + + "Cube Type sets the behavior and which skin numbers are used for gel, and the Model keyvalue sets the actual model. " + + "Script Override mode requires the collisions to be the same as the base cube type, but tends to produce more correct physics than Cube Type 6." = + [ + 0 : "None" + 1 : "Script Override" + 2 : "Cube Type 6" + ] + skin(integer) : "Skin" : 0 : "The old skin property, mainly to show in Hammer. " newskins(integer) readonly : "Use new skins" : 1 : "Use the values in the Cube Type and Skin Type fields instead of the Skin(OLD) field. You shouldn't need to touch this." diff --git a/transforms/custom_cube_models.py b/transforms/custom_cube_models.py new file mode 100644 index 000000000..505c14892 --- /dev/null +++ b/transforms/custom_cube_models.py @@ -0,0 +1,35 @@ +"""Add a keyvalue to prop_weighted_cube to automatically handle custom models.""" + +from srctools import conv_int + +from hammeraddons.bsp_transform import trans, Context + +@trans('Easy Custom Cube Models') +def custom_cube_models(ctx: Context) -> None: + """Add a keyvalue to prop_weighted_cube to automatically handle custom models.""" + for ent in ctx.vmf.by_class['prop_weighted_cube']: + model_type = conv_int(ent['comp_custom_model_type']) + + if model_type == 0: # none + continue + elif model_type == 1: # script override + cube_model = ent['model'] + # Make a prop_dynamic_override to precache the model + ctx.vmf.create_ent( + classname = 'prop_dynamic_override', + model = cube_model, + rendermode = '10', + solid = '0', + shadowdepthnocache = '2', + spawnflags = '256', # disable collision + SuppressAnimSounds = '1', + DisableBoneFollowers = '1', + origin = '-15872 -15872 -15872' # stick it out of bounds + ) + ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cube_model + '") }') + elif model_type == 2: # cube type 6 + orig_cube_type = ent['CubeType'] + ent['CubeType'] = '6' + # Revert to the original type on spawn + ctx.add_code(ent, 'function OnPostSpawn() { EntFireByHandle(self, "AddOutput", "CubeType ' + orig_cube_type + '", 0, self, self) }') + \ No newline at end of file From 73784af4f4f87e6ac0b932372b466aa9b4301edb Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 21 Jun 2023 19:30:14 -0700 Subject: [PATCH 122/243] logic_auto also should have this removed --- fgd/point/logic/logic_auto.fgd | 1 - 1 file changed, 1 deletion(-) diff --git a/fgd/point/logic/logic_auto.fgd b/fgd/point/logic/logic_auto.fgd index 96a2378a2..d4da29a19 100644 --- a/fgd/point/logic/logic_auto.fgd +++ b/fgd/point/logic/logic_auto.fgd @@ -42,7 +42,6 @@ "is_console": "Game is running on a console" "is_pc": "Game is running on a PC" - "portalgun_nospawn": "Spawn without Portalgun" "no_pinging_blue": "Prevent Pinging ATLAS" "no_pinging_orange": "Prevent Pinging P-Body" "no_taunting_blue": "Prevent Taunting ATLAS" From f9203bb23842f5e6f8462be19106c9ab489cf680 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 22 Jun 2023 13:51:32 +1000 Subject: [PATCH 123/243] Use the correct list, and simplify using this method --- src/hammeraddons/bsp_transform/__init__.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index cb84449fa..8f2208fdd 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -174,7 +174,7 @@ def _apply_remaps(self) -> None: continue if should_remove: ent.outputs.remove(out) - collapsed_remaps: list[Output] = [] + collapsed_remaps: List[Output] = [] out_copy = out.copy() # Don't allow remapping functions to modify this. for remap in remaps: if isinstance(remap, Output): @@ -182,17 +182,8 @@ def _apply_remaps(self) -> None: else: collapsed_remaps.extend(remap(ent, out_copy)) - for rep_out in remaps: - new_out = Output( - out.output, - rep_out.target, - rep_out.input, - rep_out.params or out.params, - out.delay + rep_out.delay, - times=out.times if rep_out.times == -1 - else rep_out.times if out.times == -1 - else min(out.times, rep_out.times), - ) + for rep_out in collapsed_remaps: + new_out = Output.combine(out, rep_out) ent.outputs.append(new_out) deferred.append(new_out) if not deferred: From 3cecccb3ccc7e5634a3035683811489be83b5e11 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 23 Jun 2023 11:05:21 +1000 Subject: [PATCH 124/243] Move IO remap logic back to a function --- src/hammeraddons/bsp_transform/__init__.py | 108 +++++++++++---------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index 8f2208fdd..c4ac293a4 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -144,59 +144,6 @@ def add_code(self, ent: Entity, code: str) -> None: else: self._ent_code[ent] = '{}\n{}'.format(existing, code) - def _apply_remaps(self) -> None: - """Apply all the IO remaps.""" - # Always disallow remaps now. - self._allow_remaps = False - - if not self._io_remaps: - return - - LOGGER.info('Remapping outputs...') - for (name, inp_name), outs in self._io_remaps.items(): - LOGGER.debug('Remap {}.{} = {}', name, inp_name, outs) - - for ent in self.vmf.entities: - if not ent.outputs: # Early out. - continue - todo = ent.outputs[:] - # Recursively convert only up to 500 times. - # Arbitrary limit, should be sufficient. - for _ in range(500): - deferred = [] - for out in todo: - try: - remaps, should_remove = self._io_remaps[ - out.target.casefold(), - out.input.casefold(), - ] - except KeyError: - continue - if should_remove: - ent.outputs.remove(out) - collapsed_remaps: List[Output] = [] - out_copy = out.copy() # Don't allow remapping functions to modify this. - for remap in remaps: - if isinstance(remap, Output): - collapsed_remaps.append(remap) - else: - collapsed_remaps.extend(remap(ent, out_copy)) - - for rep_out in collapsed_remaps: - new_out = Output.combine(out, rep_out) - ent.outputs.append(new_out) - deferred.append(new_out) - if not deferred: - break - todo = deferred - else: - LOGGER.error( - 'Entity "{}" ({}) @ {} has infinite loop when expanding ' - ' compiler outputs to real ones! Final output list: \n{}', - ent['targetname'], ent['classname'], ent['origin'], - '\n'.join(['* {}\n'.format(out) for out in ent.outputs]) - ) - TransFunc = Callable[[Context], Awaitable[None]] TransFuncOrSync = Callable[[Context], Optional[Awaitable[None]]] @@ -262,6 +209,61 @@ async def run_transformations( context._apply_remaps() +# noinspection PyProtectedMember +def apply_io_remaps(context: Context) -> None: + """Apply all the IO remaps.""" + # Always disallow remaps now. + context._allow_remaps = False + + if not context._io_remaps: + return + + LOGGER.info('Remapping outputs...') + for (name, inp_name), outs in context._io_remaps.items(): + LOGGER.debug('Remap {}.{} = {}', name, inp_name, outs) + + for ent in context.vmf.entities: + if not ent.outputs: # Early out. + continue + todo = ent.outputs[:] + # Recursively convert only up to 500 times. + # Arbitrary limit, should be sufficient. + for _ in range(500): + deferred = [] + for out in todo: + try: + remaps, should_remove = context._io_remaps[ + out.target.casefold(), + out.input.casefold(), + ] + except KeyError: + continue + if should_remove: + ent.outputs.remove(out) + collapsed_remaps: List[Output] = [] + out_copy = out.copy() # Don't allow remapping functions to modify this. + for remap in remaps: + if isinstance(remap, Output): + collapsed_remaps.append(remap) + else: + collapsed_remaps.extend(remap(ent, out_copy)) + + for rep_out in collapsed_remaps: + new_out = Output.combine(out, rep_out) + ent.outputs.append(new_out) + deferred.append(new_out) + if not deferred: + break + todo = deferred + else: + LOGGER.error( + 'Entity "{}" ({}) @ {} has infinite loop when expanding ' + ' compiler outputs to real ones! Final output list: \n{}', + ent['targetname'], ent['classname'], ent['origin'], + '\n'.join(['* {}\n'.format(out) for out in ent.outputs]) + ) + + def _load() -> None: """Import all submodules. From 2e5c9093b9706efc4516d3091d9a8eab895c88be Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 23 Jun 2023 11:35:45 +1000 Subject: [PATCH 125/243] Add a regression test for io remaps --- tests/test_transforms/test_core_io_remap.py | 38 ++++++ .../test_core_io_remap/test_simple_remap.vmf | 116 ++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 tests/test_transforms/test_core_io_remap.py create mode 100644 tests/test_transforms/test_core_io_remap/test_simple_remap.vmf diff --git a/tests/test_transforms/test_core_io_remap.py b/tests/test_transforms/test_core_io_remap.py new file mode 100644 index 000000000..79ab688c8 --- /dev/null +++ b/tests/test_transforms/test_core_io_remap.py @@ -0,0 +1,38 @@ +"""Test the IO remapping behaviour.""" +from srctools import Output +from . import blank_ctx, Context + +from hammeraddons.bsp_transform import apply_io_remaps # noqa + + +def test_simple_remap(blank_ctx: Context, file_regression) -> None: + """Test some basic remaps.""" + rl_source = blank_ctx.vmf.create_ent('some_ent', targetname='rl_source') + rl_source.add_out( + Output('OnMulti', 'some_dest', 'Trigger', delay=0.5), + Output('OnSingle', 'some_dest', 'Trigger', only_once=True), + Output('OnTrigger', 'some_dest', 'WillBeRemoved'), + Output('OnTrigger', 'regular_output', 'SetAnimation', 'dance', 0.125), + Output('OnSpawn', 'basic_dest', 'NeedsParam', '1'), + Output('OnSpawn', 'overrides', 'Skin', '5'), + ) + + blank_ctx.add_io_remap( + 'some_dest', + Output('Trigger', 'model_1', 'Skin', '1'), + Output('Trigger', 'model_1', 'Skin', '0', delay=1.0, times=3), + ) + blank_ctx.add_io_remap( + 'some_dest', + Output('Trigger', 'pfx', 'Start'), + Output('Trigger', 'pfx', 'Stop', delay=0.5), + ) + blank_ctx.add_io_remap( + 'overrides', + Output('Skin', 'another_model', 'Bodygroup', '256'), + ) + blank_ctx.add_io_remap_removal('some_dest', 'WillBeRemoved') + + apply_io_remaps(blank_ctx) + + file_regression.check(blank_ctx.vmf.export(), encoding='utf8', extension='.vmf') diff --git a/tests/test_transforms/test_core_io_remap/test_simple_remap.vmf b/tests/test_transforms/test_core_io_remap/test_simple_remap.vmf new file mode 100644 index 000000000..f682205e4 --- /dev/null +++ b/tests/test_transforms/test_core_io_remap/test_simple_remap.vmf @@ -0,0 +1,116 @@ +versioninfo +{ + "editorversion" "400" + "editorbuild" "5304" + "mapversion" "6" + "formatversion" "100" + "prefab" "0" +} +visgroups +{ +} +viewsettings +{ + "bSnapToGrid" "1" + "bShowGrid" "1" + "bShowLogicalGrid" "0" + "nGridSpacing" "64" + "bShow3DGrid" "0" +} +world +{ + "id" "1" + "classname" "worldspawn" + "detailmaterial" "detail/detailsprites" + "detailvbsp" "detail.vbsp" + "hammerid" "1" + "mapversion" "6" + "maxpropscreenwidth" "-1" + "skyname" "sky_day01_01" + "world_maxs" "128 128 128" + "world_mins" "0 0 0" + editor + { + "color" "255 255 255" + } +} +entity +{ + "id" "2" + "angles" "0 0 0" + "classname" "info_player_start" + "hammerid" "30" + "origin" "64 64 0" + editor + { + "color" "255 255 255" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2]" + } +} +entity +{ + "id" "3" + "_cone" "80" + "_constant_attn" "0" + "_distance" "0" + "_exponent" "1" + "_fifty_percent_distance" "0" + "_hardfalloff" "0" + "_inner_cone" "40" + "_light" "187 240 255 200" + "_lighthdr" "-1 -1 -1 1" + "_lightscalehdr" "1" + "_linear_attn" "0" + "_quadratic_attn" "1" + "_zero_percent_distance" "0" + "angles" "-90 0 0" + "classname" "light_spot" + "hammerid" "36" + "origin" "64 64 120" + "pitch" "-90" + "style" "0" + editor + { + "color" "255 255 255" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3]" + } +} +entity +{ + "id" "4" + "classname" "some_ent" + "targetname" "rl_source" + connections + { + "OnTrigger" "regular_outputSetAnimationdance0.125-1" + "OnSpawn" "basic_destNeedsParam10-1" + "OnMulti" "model_1Skin10.5-1" + "OnMulti" "model_1Skin01.53" + "OnMulti" "pfxStart0.5-1" + "OnMulti" "pfxStop1-1" + "OnSingle" "model_1Skin101" + "OnSingle" "model_1Skin011" + "OnSingle" "pfxStart01" + "OnSingle" "pfxStop0.51" + "OnSpawn" "another_modelBodygroup2560-1" + } + editor + { + "color" "255 255 255" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 4]" + } +} +cameras +{ + "activecamera" "-1" +} +cordons +{ + "active" "0" +} From de35e807bd34cc8cd6bac6b576b2a9275407fd8e Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 23 Jun 2023 13:54:23 +1000 Subject: [PATCH 126/243] Add definition for comp_case --- fgd/point/comp/comp_case.fgd | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 fgd/point/comp/comp_case.fgd diff --git a/fgd/point/comp/comp_case.fgd b/fgd/point/comp/comp_case.fgd new file mode 100644 index 000000000..e66481997 --- /dev/null +++ b/fgd/point/comp/comp_case.fgd @@ -0,0 +1,70 @@ +@PointClass base(StaticTargetName, ControlEnables) + iconsprite("editor/comp_case") + appliesto(srctools) += comp_case: + "Simplified version of logic_case which is able to be optimised away by the compiler."+ + "" + + "This is primarly intended to be used in instances - depending on a fixup value, it will produce different results." + + "It can be used alternatively to pick a random output, though this is fixed at compile time for each inputting entity." + [ + MultipleCasesAllowed(boolean) : "Multiple case hits allowed" : 0 : "If an input value matches a given case, " + + "are we allowed to test the rest of the cases or should we stop there? " + + "Don't worry about this if you're only using this entity for PickRandom." + + value(string) : "Input Value" : "If the InValue parameter is blank or Trigger is used, this value will be used instead." + + mode[engine](string) : "Mode" : "text" + mode(choices) : "Mode" : "string_casefold" : "Specifies how comparisons are performed. Text mode simply checks for a case that matches the input text. " + + "Numeric treats values as numbers, allowing cases to additionally specify a comparison like '< 3.14'. In all modes, each case is compared in order." = + [ + "string" : "Text - Case Sensitive" + "casefold" : "Text - Case Insensitive" + "numeric" : "Numeric" + ] + + seed(string) : "Random Seed" : : "For the PickRandom input, the position and name of the input entity and the case are used to seed a random number generator. This can be set to further randomise the chosen case." + + case01(string) : "Case 01" : : "Fires OnCase01 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case02(string) : "Case 02" : : "Fires OnCase02 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case03(string) : "Case 03" : : "Fires OnCase03 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case04(string) : "Case 04" : : "Fires OnCase04 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case05(string) : "Case 05" : : "Fires OnCase05 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case06(string) : "Case 06" : : "Fires OnCase06 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case07(string) : "Case 07" : : "Fires OnCase07 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case08(string) : "Case 08" : : "Fires OnCase08 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case09(string) : "Case 09" : : "Fires OnCase09 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case10(string) : "Case 10" : : "Fires OnCase10 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case11(string) : "Case 11" : : "Fires OnCase11 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case12(string) : "Case 12" : : "Fires OnCase12 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case13(string) : "Case 13" : : "Fires OnCase13 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case14(string) : "Case 14" : : "Fires OnCase14 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case15(string) : "Case 15" : : "Fires OnCase15 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case16(string) : "Case 16" : : "Fires OnCase16 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + + // Inputs + input InValue(string) : "Replaced by whichever case matches the parameter." + input Trigger(void) : "Replaced by whichever case matches the input keyvalue." + input PickRandom(void) : "Replaced by a random case with outputs defined." + + // Outputs + output OnCase01(void) : "Fired when the input value equals the Case01 value." + output OnCase02(void) : "Fired when the input value equals the Case02 value." + output OnCase03(void) : "Fired when the input value equals the Case03 value." + output OnCase04(void) : "Fired when the input value equals the Case04 value." + output OnCase05(void) : "Fired when the input value equals the Case05 value." + output OnCase06(void) : "Fired when the input value equals the Case06 value." + output OnCase07(void) : "Fired when the input value equals the Case07 value." + output OnCase08(void) : "Fired when the input value equals the Case08 value." + output OnCase09(void) : "Fired when the input value equals the Case09 value." + output OnCase10(void) : "Fired when the input value equals the Case10 value." + output OnCase11(void) : "Fired when the input value equals the Case11 value." + output OnCase12(void) : "Fired when the input value equals the Case12 value." + output OnCase13(void) : "Fired when the input value equals the Case13 value." + output OnCase14(void) : "Fired when the input value equals the Case14 value." + output OnCase15(void) : "Fired when the input value equals the Case15 value." + output OnCase16(void) : "Fired when the input value equals the Case16 value." + output OnDefault(void) : "Fired when the input value does not equal any of the Case values." + output OnUsed(string) : "Fired when an input value is received, regardless of whether it matches a case." + + @resources [] + ] From 990f91c5d68450d65608b4c2c308f5fc673f962c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 23 Jun 2023 13:54:44 +1000 Subject: [PATCH 127/243] Implement comp_case logic --- transforms/comp_case.py | 193 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 transforms/comp_case.py diff --git a/transforms/comp_case.py b/transforms/comp_case.py new file mode 100644 index 000000000..16db6d054 --- /dev/null +++ b/transforms/comp_case.py @@ -0,0 +1,193 @@ +"""comp_case is a compile-time collapsible version of logic_case.""" +from typing import Callable, Iterator, List, Tuple +from typing_extensions import TypeAlias + +import hashlib +import operator +import re +import struct +import random +from decimal import Decimal +from collections import defaultdict + +from srctools import Entity, Output, conv_bool +from srctools.math import parse_vec_str +from srctools.logger import get_logger + +from hammeraddons.bsp_transform import trans, Context, check_control_enabled + + +LOGGER = get_logger(__name__) + + +CASES = [f'case{x:02}' for x in range(1, 17)] +NumericOp: TypeAlias = Callable[[Decimal, Decimal], bool] + +OPERATIONS: dict[str, NumericOp] = { + '<': operator.lt, + '>': operator.gt, + '>=': operator.ge, + '<=': operator.le, + '=': operator.eq, + '==': operator.eq, + '!=': operator.ne, + '=!=': operator.ne, + '~=': operator.ne, + '=/=': operator.ne, +} +# Matches characters present in OPERATIONS +OPERATION_RE = re.compile('({0})+'.format('|'.join(map(re.escape, { + char for key in OPERATIONS for char in key +})))) + + +def parse_numeric_specifier(case_num: int, text: str, desc: str) -> Tuple[int, NumericOp, Decimal]: + """Parse case values like "> 5" into the operation and number.""" + operation: NumericOp + if (match := OPERATION_RE.match(text)) is not None: + try: + operation = OPERATIONS[match.group()] + except KeyError: + LOGGER.warning('Invalid numeric operator "{}" for case #{} in {}', match.group(), case_num, desc) + operation = operator.eq + num_str = text[match.end():] + else: + operation = operator.eq + num_str = text + try: + num = Decimal(num_str) + except ValueError: + LOGGER.warning('Invalid number "{}" for case #{} in {}', num_str, case_num, desc) + # Force this to always fail. + return (case_num, lambda a, b: False, Decimal()) + else: + return (case_num, operation, num) + + +def collapse_case(ctx: Context, case: Entity) -> None: + """Collapse a single case.""" + multi_cases = conv_bool(case['multiplecasesallowed']) + case_name = case['targetname'] + mode = case['mode'].casefold() + default_value = case['value'] + + desc = f'for comp_case "{case_name}" @ ({case["origin"]})' + + hasher_template = hashlib.sha512() + hasher_template.update(f"{case['seed']};{case_name}".encode('utf-8')) + hasher_template.update(struct.pack(' Iterator[int]: + """Find string-based matches.""" + for case_num in key_params: + if param == case_params[case_num]: + yield case_num + elif mode == 'casefold': + def find_matches(param: str) -> Iterator[int]: + """Find string-based matches, ignoring cases.""" + for case_num in key_params: + if param.casefold() == case_params[case_num].casefold(): + yield case_num + elif mode == 'numeric': + # Pre-parse "< 5" style values. + numeric_cases: List[Tuple[int, NumericOp, Decimal]] = [ + parse_numeric_specifier(case_num, case_params[case_num], desc) + for case_num in key_params + ] + + def find_matches(param: str) -> Iterator[int]: + """Find numeric matches, using a configurable operator.""" + try: + num_a = Decimal(param) + except ValueError: + return # Matches nothing. + for case_num, operation, num_b in numeric_cases: + if operation(num_a, num_b): + yield case_num + else: + LOGGER.error( + 'Invalid mode "{}" for comp_case "{}" @ ({})', + case['mode'], case_name, case['origin'] + ) + return + + def compute_outputs(param: str) -> Iterator[Output]: + """Compute the matching cases, then yield the outputs.""" + matching = find_matches(param) + yield from out_used # Always used. + try: + first_match = next(matching) + except StopIteration: + # No match, use defaults. + yield from out_default + return + yield from out_cases[first_match] + if multi_cases: # Include all matches. + for match in matching: + yield from out_cases[match] + + def handle_pick_random(source: Entity, out: Output) -> List[Output]: + """Handle the PickRandom input.""" + hasher = hasher_template.copy() + hasher.update((source['targetname'] or source['classname']).encode('utf8')) + hasher.update(struct.pack(' None: + """A version of logic_case which is collapsed at compile time.""" + for ent in ctx.vmf.by_class['comp_case']: + if check_control_enabled(ent): + collapse_case(ctx, ent) + else: + # If any entities exist with the same name that aren't comp_case, we need to + # keep the inputs. + case_name = ent['targetname'] + if not any(ent['classname'].casefold() != 'comp_case' for ent in ctx.vmf.by_target[case_name]): + ctx.add_io_remap_removal(case_name, 'InValue') + ctx.add_io_remap_removal(case_name, 'Trigger') + ctx.add_io_remap_removal(case_name, 'PickRandom') From 5126af419088bf89bcb0d0998c97da4a9aae3c49 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 23 Jun 2023 20:58:23 -0700 Subject: [PATCH 128/243] Clean up solid keyvalues on various entities --- fgd/bases/BasePedButton.fgd | 1 - fgd/bases/WeaponSpawnSingle.fgd | 15 ++++++++------- fgd/point/prop/prop_dynamic_ornament.fgd | 2 +- fgd/point/prop/prop_static.fgd | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/fgd/bases/BasePedButton.fgd b/fgd/bases/BasePedButton.fgd index cdbc0fee8..25ab0876e 100644 --- a/fgd/bases/BasePedButton.fgd +++ b/fgd/bases/BasePedButton.fgd @@ -4,7 +4,6 @@ delay(float) : "Delay Before Reset" : 1 : "Amount of time, in seconds, after the button has been pressed before it returns to the starting position. Once it has returned, it can be used again." istimer(boolean) : "Play timer sound?" : 0 : "If set, this button will play timer sounds while button is depressed. This allows fast reset by default - use Prevent fast reset to stop this." preventfastreset(boolean) : "Prevent fast reset?" : 0 : "Buttons that are timer's allow you to reset early - use this to make the button stick for the entire duration." - solid(boolean) : "Is Solid" : 1 : "Makes button able to be passed through." // Inputs input Press(void) : "Cause the button to be pressed." diff --git a/fgd/bases/WeaponSpawnSingle.fgd b/fgd/bases/WeaponSpawnSingle.fgd index a394316d5..860ce9ed9 100644 --- a/fgd/bases/WeaponSpawnSingle.fgd +++ b/fgd/bases/WeaponSpawnSingle.fgd @@ -6,13 +6,14 @@ weaponskin[L4D2](integer) : "Weapon Skin" : -1 : "Some weapons have multiple versions of their textures, called skins. Set this to a number other than -1 to make the given weapon use that skin instead of the default." glowrange[L4D2](float) : "Glow Range" : 0 : "Set a custom glow range for this spawner. 0 means use the default range." - solid[engine](integer) : "Collisions" : 6 - solid(choices) : "Collisions" : 6 = - [ - 0: "Not Solid" - 2: "Use Bounding Box" - 6: "Use VPhysics" - ] + // Should be inherited from base physics + // solid[engine](integer) : "Collisions" : 6 + // solid(choices) : "Collisions" : 6 = + // [ + // 0: "Not Solid" + // 2: "Use Bounding Box" + // 6: "Use VPhysics" + // ] spawnflags(flags) = [ diff --git a/fgd/point/prop/prop_dynamic_ornament.fgd b/fgd/point/prop/prop_dynamic_ornament.fgd index 2365b9c6c..6609508cf 100644 --- a/fgd/point/prop/prop_dynamic_ornament.fgd +++ b/fgd/point/prop/prop_dynamic_ornament.fgd @@ -3,7 +3,7 @@ autovis(Props, Dynamic) = prop_dynamic_ornament: "A way to attach one studio model to another as an ornament. It will render in the way that player/NPC weapons render." [ - solid(choices) : "Collisions" : "0" = + solid(choices) readonly : "Collisions" : "0" : "Collisions don't follow the target entity properly, so this should always be non-solid." = [ 0: "Not Solid" ] diff --git a/fgd/point/prop/prop_static.fgd b/fgd/point/prop/prop_static.fgd index 8663c96f4..55cd3a4dc 100644 --- a/fgd/point/prop/prop_static.fgd +++ b/fgd/point/prop/prop_static.fgd @@ -36,15 +36,15 @@ uniformscale[PROP_SCALING](float) : "Uniform Scale Override" : 1 : "Resize the static prop." - solid[engine](integer) : "Collisions" : 6 - solid(choices) : "Collisions" : 6 = + solid[engine](integer) : "Collisions": 6 + solid(choices) : "Collisions" : 6 : "Method of collision for this prop." = [ - 0: "Not Solid" - 1: "Use BSP (QPhysics)" [complete] - 2: "Use Bounding Box" - 3: "Use Oriented Bounding Box" [complete] - 4: "Use Oriented Bounding Box, constrained to Yaw only" [complete] - 6: "Use VPhysics" + 0: "None" + 1: "BSP (QPhysics)" [complete] + 2: "Bounding Box" + 3: "Oriented Bounding Box" + 4: "Oriented Bounding Box, constrained to Yaw only" + 6: "VPhysics" ] preventpropcombine[PROPCOMBINE](boolean) : "Disable Prop Combine" : 0 : "Prevent this static prop from combining with any other static props in vbsp." From 60d9451c276b7318431c62bfa0cbd6a0c7431c55 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 23 Jun 2023 21:10:39 -0700 Subject: [PATCH 129/243] Swap all .spr to .vmt --- examples/hadd_sequential_call.vmf | 260 ++++++++++++------------ examples/instances/unique_state_btn.vmf | 2 +- fgd/bases/BaseBeam.fgd | 2 +- fgd/point/env/env_explosion.fgd | 2 +- fgd/point/env/env_smoketrail.fgd | 4 +- 5 files changed, 135 insertions(+), 135 deletions(-) diff --git a/examples/hadd_sequential_call.vmf b/examples/hadd_sequential_call.vmf index 130310f3b..e70e14b96 100644 --- a/examples/hadd_sequential_call.vmf +++ b/examples/hadd_sequential_call.vmf @@ -2338,7 +2338,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -2535,7 +2535,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -2732,7 +2732,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -2929,7 +2929,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -3126,7 +3126,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -3323,7 +3323,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -3520,7 +3520,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -3717,7 +3717,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -3914,7 +3914,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -4111,7 +4111,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -4308,7 +4308,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -4505,7 +4505,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -4702,7 +4702,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -4899,7 +4899,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -5096,7 +5096,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -5293,7 +5293,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -5490,7 +5490,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -5687,7 +5687,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -5884,7 +5884,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -6081,7 +6081,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -6278,7 +6278,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -6475,7 +6475,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -6672,7 +6672,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -6869,7 +6869,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -7066,7 +7066,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -7263,7 +7263,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -7460,7 +7460,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -7657,7 +7657,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -7854,7 +7854,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -8051,7 +8051,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -8248,7 +8248,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -8445,7 +8445,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -8642,7 +8642,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -9331,7 +9331,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -9530,7 +9530,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -9729,7 +9729,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -9928,7 +9928,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -10127,7 +10127,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -10326,7 +10326,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -10525,7 +10525,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -10724,7 +10724,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -10923,7 +10923,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -11122,7 +11122,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -11321,7 +11321,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -11520,7 +11520,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -11719,7 +11719,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -11918,7 +11918,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -12117,7 +12117,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -12316,7 +12316,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -12515,7 +12515,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -12714,7 +12714,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -12913,7 +12913,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -13112,7 +13112,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -13311,7 +13311,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -13510,7 +13510,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -13709,7 +13709,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -13908,7 +13908,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -14107,7 +14107,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -14306,7 +14306,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -14505,7 +14505,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -14704,7 +14704,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -14903,7 +14903,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -15102,7 +15102,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -15301,7 +15301,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -15500,7 +15500,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -15699,7 +15699,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -15898,7 +15898,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -16097,7 +16097,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -16296,7 +16296,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -16495,7 +16495,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -16694,7 +16694,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -16893,7 +16893,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -17092,7 +17092,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -17291,7 +17291,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -17490,7 +17490,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -17689,7 +17689,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -17888,7 +17888,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -18087,7 +18087,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -18286,7 +18286,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -18485,7 +18485,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -18684,7 +18684,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -18883,7 +18883,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -19082,7 +19082,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -19281,7 +19281,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -19497,7 +19497,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -19696,7 +19696,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -19895,7 +19895,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -20094,7 +20094,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -20293,7 +20293,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -20492,7 +20492,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -20855,7 +20855,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21054,7 +21054,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21253,7 +21253,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21452,7 +21452,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21651,7 +21651,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21850,7 +21850,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -21973,7 +21973,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22096,7 +22096,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22219,7 +22219,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22342,7 +22342,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22541,7 +22541,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22740,7 +22740,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -22939,7 +22939,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -23138,7 +23138,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -23337,7 +23337,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -23536,7 +23536,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -23735,7 +23735,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -23934,7 +23934,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -24133,7 +24133,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -24332,7 +24332,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -24531,7 +24531,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -24730,7 +24730,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -24929,7 +24929,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -25128,7 +25128,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -25327,7 +25327,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -25526,7 +25526,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -25725,7 +25725,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -25924,7 +25924,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26123,7 +26123,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26246,7 +26246,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26369,7 +26369,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26568,7 +26568,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26767,7 +26767,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -26890,7 +26890,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -27013,7 +27013,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -27212,7 +27212,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -27411,7 +27411,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -27610,7 +27610,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -27809,7 +27809,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" @@ -28008,7 +28008,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 0 0" "renderfx" "0" diff --git a/examples/instances/unique_state_btn.vmf b/examples/instances/unique_state_btn.vmf index 660fa314e..85eed1391 100644 --- a/examples/instances/unique_state_btn.vmf +++ b/examples/instances/unique_state_btn.vmf @@ -326,7 +326,7 @@ entity "mincpulevel" "0" "mindxlevel" "0" "mingpulevel" "0" - "model" "sprites/glow01.spr" + "model" "sprites/glow01.vmt" "renderamt" "255" "rendercolor" "255 50 50" "renderfx" "0" diff --git a/fgd/bases/BaseBeam.fgd b/fgd/bases/BaseBeam.fgd index e19210d3c..9266706af 100644 --- a/fgd/bases/BaseBeam.fgd +++ b/fgd/bases/BaseBeam.fgd @@ -14,7 +14,7 @@ framerate(integer) : "Frames per 10 seconds" : 0 : "Framerate at which the beam texture should animate, if it has multiple frames." framestart(integer) : "Starting Frame" : 0 : "The frame to start the beam texture on." - texture(sprite) : "Sprite Name" : "sprites/laserbeam.spr" : "The material used to draw the beam." + texture(sprite) : "Sprite Name" : "sprites/laserbeam.vmt" : "The material used to draw the beam." texturescroll(integer) : "Texture Scroll Rate (0-100)" : 35 : "Rate at which the beam texture should scroll along the beam." damage(string) : "Damage / second" : "0" : "How much damage this beam does per second to things while active. For continuous damage, the value should be greater than 10 or it may not work." diff --git a/fgd/point/env/env_explosion.fgd b/fgd/point/env/env_explosion.fgd index ff8e22c75..e8bb7b17a 100644 --- a/fgd/point/env/env_explosion.fgd +++ b/fgd/point/env/env_explosion.fgd @@ -7,7 +7,7 @@ [ imagnitude(integer) : "Magnitude" : 100 : "The amount of damage done by the explosion." iradiusoverride(integer) : "Radius Override" : 0 : "If specified, the radius in which the explosion damages entities. If unspecified, the radius will be based on the magnitude." - fireballsprite(sprite) : "Fireball Sprite" : "sprites/zerogxplode.spr" + fireballsprite(sprite) : "Fireball Sprite" : "sprites/zerogxplode.vmt" DamageForce(float) : "Damage Force" : 0 : "The force to apply the damage with. If unspecified, the explosion won't push entities." rendermode[engine](integer) : "Render Mode" : 5 diff --git a/fgd/point/env/env_smoketrail.fgd b/fgd/point/env/env_smoketrail.fgd index 745ab35f1..da9a0466e 100644 --- a/fgd/point/env/env_smoketrail.fgd +++ b/fgd/point/env/env_smoketrail.fgd @@ -17,8 +17,8 @@ startsize(float) : "Starting particle size" : 15 : "Starting particle size." endsize(float) : "Ending particle size" : 50 : "Ending particle size." spawnradius(float) : "Spawn radius" : 15 : "Distance from env_smoketrail at which particles are emitted." - firesprite(sprite) : "Fire Sprite" : "sprites/firetrail.spr" - smokesprite(sprite) : "Smoke Puff" : "sprites/whitepuff.spr" + firesprite(sprite) : "Fire Sprite" : "sprites/firetrail.vmt" + smokesprite(sprite) : "Smoke Puff" : "sprites/whitepuff.vmt" @resources [ From dcc9fadfabe36f444b694ecb66a40cb44e3eb90e Mon Sep 17 00:00:00 2001 From: TeamSpen210 Date: Sun, 25 Jun 2023 10:52:00 +1000 Subject: [PATCH 130/243] Revert 4af2e1a: dispenser_touch_trigger is not internal --- fgd/brush/dispenser_touch_trigger.fgd | 5 +++++ fgd/engine/dispenser_touch_trigger.fgd | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 fgd/brush/dispenser_touch_trigger.fgd delete mode 100644 fgd/engine/dispenser_touch_trigger.fgd diff --git a/fgd/brush/dispenser_touch_trigger.fgd b/fgd/brush/dispenser_touch_trigger.fgd new file mode 100644 index 000000000..449a10cef --- /dev/null +++ b/fgd/brush/dispenser_touch_trigger.fgd @@ -0,0 +1,5 @@ + +@SolidClass base(Trigger) + appliesto(TF2) = dispenser_touch_trigger: "Trigger for dispenser healing bounds" + [ + ] diff --git a/fgd/engine/dispenser_touch_trigger.fgd b/fgd/engine/dispenser_touch_trigger.fgd deleted file mode 100644 index 9f4275f16..000000000 --- a/fgd/engine/dispenser_touch_trigger.fgd +++ /dev/null @@ -1,4 +0,0 @@ -@SolidClass base(Trigger) - appliesto(+engine, TF2) = dispenser_touch_trigger: "Trigger for dispenser healing bounds" - [ - ] From 56f942fce406f3248f540fd7a021941ae3b6ed85 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 24 Jun 2023 23:19:06 -0700 Subject: [PATCH 131/243] This can just create a comp precache model --- transforms/custom_cube_models.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/transforms/custom_cube_models.py b/transforms/custom_cube_models.py index 505c14892..7bd823599 100644 --- a/transforms/custom_cube_models.py +++ b/transforms/custom_cube_models.py @@ -14,17 +14,10 @@ def custom_cube_models(ctx: Context) -> None: continue elif model_type == 1: # script override cube_model = ent['model'] - # Make a prop_dynamic_override to precache the model + # Make a comp_precache_model ctx.vmf.create_ent( - classname = 'prop_dynamic_override', + classname = 'comp_precache_model', model = cube_model, - rendermode = '10', - solid = '0', - shadowdepthnocache = '2', - spawnflags = '256', # disable collision - SuppressAnimSounds = '1', - DisableBoneFollowers = '1', - origin = '-15872 -15872 -15872' # stick it out of bounds ) ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cube_model + '") }') elif model_type == 2: # cube type 6 From 8a9b5286049b993c138c58edc9f283d1be2e1a40 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 25 Jun 2023 14:11:45 -0700 Subject: [PATCH 132/243] Set env_beverage and item_sodacan as complete fgd only --- fgd/point/env/env_beverage.fgd | 1 + fgd/point/item/item_sodacan.fgd | 1 + 2 files changed, 2 insertions(+) diff --git a/fgd/point/env/env_beverage.fgd b/fgd/point/env/env_beverage.fgd index 6988c2ae2..baf6ebdd8 100644 --- a/fgd/point/env/env_beverage.fgd +++ b/fgd/point/env/env_beverage.fgd @@ -1,5 +1,6 @@ @PointClass base(BaseEntityPoint) iconsprite("editor/ficool2/env_beverage") + appliesto(+complete) = env_beverage: "HL1 Legacy: Beverage Dispenser." [ health(integer) : "Capacity" : 10 : "Number of cans in the dispenser." diff --git a/fgd/point/item/item_sodacan.fgd b/fgd/point/item/item_sodacan.fgd index 8a30db5ca..7c6b49e3e 100644 --- a/fgd/point/item/item_sodacan.fgd +++ b/fgd/point/item/item_sodacan.fgd @@ -1,4 +1,5 @@ @PointClass base(BaseEntityAnimating) + appliesto(+complete) studio("models/can.mdl") = item_sodacan : "HL1 soda can." [ @resources From c919281e0abe2b4a66613a55bc87e6415b86114f Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 10:57:26 +1000 Subject: [PATCH 133/243] Move parse_numeric_specifier to hammeraddons module --- src/hammeraddons/bsp_transform/__init__.py | 7 ++- src/hammeraddons/bsp_transform/common.py | 54 ++++++++++++++++++++ transforms/comp_case.py | 58 ++++------------------ 3 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 src/hammeraddons/bsp_transform/common.py diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index c4ac293a4..1cbb3afbb 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -12,14 +12,19 @@ from srctools.logger import get_logger from srctools.packlist import PackList +from hammeraddons.bsp_transform.common import ( + parse_numeric_specifier, NumericSpecifier, NumericOp +) LOGGER = get_logger(__name__, 'bsp_trans') RemapFunc: TypeAlias = Callable[[Entity, Output], List[Output]] __all__ = [ - 'check_control_enabled', 'Context', 'trans', 'run_transformations', 'TransFunc', 'TRANSFORMS', + # Utils: + 'check_control_enabled', + 'parse_numeric_specifier', 'NumericOp', 'NumericSpecifier', ] diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py new file mode 100644 index 000000000..c6ee4ffdb --- /dev/null +++ b/src/hammeraddons/bsp_transform/common.py @@ -0,0 +1,54 @@ +"""Operations that can be reused across different transforms.""" +import operator +import re +from typing import Callable, Tuple +from decimal import Decimal + +from typing_extensions import TypeAlias + +from srctools.logger import get_logger + + +LOGGER = get_logger(__name__) +NumericOp: TypeAlias = Callable[[Decimal, Decimal], bool] +NumericSpecifier: TypeAlias = Tuple[NumericOp, Decimal] + +OPERATIONS: dict[str, NumericOp] = { + '<': operator.lt, + '>': operator.gt, + '>=': operator.ge, + '<=': operator.le, + '=': operator.eq, + '==': operator.eq, + '!=': operator.ne, + '=!=': operator.ne, + '~=': operator.ne, + '=/=': operator.ne, +} +# Matches characters present in OPERATIONS +OPERATION_RE = re.compile('({0})+'.format('|'.join(map(re.escape, { + char for key in OPERATIONS for char in key +})))) + + +def parse_numeric_specifier(text: str, desc: str) -> NumericSpecifier: + """Parse case values like "> 5" into the operation and number.""" + operation: NumericOp + if (match := OPERATION_RE.match(text)) is not None: + try: + operation = OPERATIONS[match.group()] + except KeyError: + LOGGER.warning('Invalid numeric operator "{}" {}', match.group(), desc) + operation = operator.eq + num_str = text[match.end():] + else: + operation = operator.eq + num_str = text + try: + num = Decimal(num_str) + except ValueError: + LOGGER.warning('Invalid number "{}" {}', num_str, desc) + # Force this to always fail. + return (lambda a, b: False, Decimal()) + else: + return (operation, num) diff --git a/transforms/comp_case.py b/transforms/comp_case.py index 16db6d054..255e9e168 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -14,54 +14,15 @@ from srctools.math import parse_vec_str from srctools.logger import get_logger -from hammeraddons.bsp_transform import trans, Context, check_control_enabled +from hammeraddons.bsp_transform import ( + trans, Context, + check_control_enabled, + NumericSpecifier, parse_numeric_specifier, +) LOGGER = get_logger(__name__) - - CASES = [f'case{x:02}' for x in range(1, 17)] -NumericOp: TypeAlias = Callable[[Decimal, Decimal], bool] - -OPERATIONS: dict[str, NumericOp] = { - '<': operator.lt, - '>': operator.gt, - '>=': operator.ge, - '<=': operator.le, - '=': operator.eq, - '==': operator.eq, - '!=': operator.ne, - '=!=': operator.ne, - '~=': operator.ne, - '=/=': operator.ne, -} -# Matches characters present in OPERATIONS -OPERATION_RE = re.compile('({0})+'.format('|'.join(map(re.escape, { - char for key in OPERATIONS for char in key -})))) - - -def parse_numeric_specifier(case_num: int, text: str, desc: str) -> Tuple[int, NumericOp, Decimal]: - """Parse case values like "> 5" into the operation and number.""" - operation: NumericOp - if (match := OPERATION_RE.match(text)) is not None: - try: - operation = OPERATIONS[match.group()] - except KeyError: - LOGGER.warning('Invalid numeric operator "{}" for case #{} in {}', match.group(), case_num, desc) - operation = operator.eq - num_str = text[match.end():] - else: - operation = operator.eq - num_str = text - try: - num = Decimal(num_str) - except ValueError: - LOGGER.warning('Invalid number "{}" for case #{} in {}', num_str, case_num, desc) - # Force this to always fail. - return (case_num, lambda a, b: False, Decimal()) - else: - return (case_num, operation, num) def collapse_case(ctx: Context, case: Entity) -> None: @@ -122,8 +83,11 @@ def find_matches(param: str) -> Iterator[int]: yield case_num elif mode == 'numeric': # Pre-parse "< 5" style values. - numeric_cases: List[Tuple[int, NumericOp, Decimal]] = [ - parse_numeric_specifier(case_num, case_params[case_num], desc) + numeric_cases: List[Tuple[NumericSpecifier, int]] = [ + (parse_numeric_specifier( + case_params[case_num], + f'for case #{case_num} in {desc}' + ), case_num) for case_num in key_params ] @@ -133,7 +97,7 @@ def find_matches(param: str) -> Iterator[int]: num_a = Decimal(param) except ValueError: return # Matches nothing. - for case_num, operation, num_b in numeric_cases: + for (operation, num_b), case_num in numeric_cases: if operation(num_a, num_b): yield case_num else: From f5bd575dc9e97bd5500df146b2546a8921ab859d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:03:44 +1000 Subject: [PATCH 134/243] Move check_control_enabled to common model --- src/hammeraddons/bsp_transform/__init__.py | 17 ++--------------- src/hammeraddons/bsp_transform/common.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index 1cbb3afbb..84f08469c 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -6,13 +6,14 @@ import inspect -from srctools import FGD, VMF, EmptyMapping, Entity, FileSystem, Keyvalues, Output, conv_bool +from srctools import FGD, VMF, EmptyMapping, Entity, FileSystem, Keyvalues, Output from srctools.bsp import BSP from srctools.game import Game from srctools.logger import get_logger from srctools.packlist import PackList from hammeraddons.bsp_transform.common import ( + check_control_enabled, parse_numeric_specifier, NumericSpecifier, NumericOp ) @@ -28,20 +29,6 @@ ] -def check_control_enabled(ent: Entity) -> bool: - """Implement the bahaviour of ControlEnables - control_type and control_value. - - This allows providing a fixup value, and optionally inverting it. - """ - # If ctrl_type is 0, ctrl_value needs to be 1 to be enabled. - # If ctrl_type is 1, ctrl_value needs to be 0 to be enabled. - if 'ctrl_type' in ent: - return conv_bool(ent['ctrl_type'], False) != conv_bool(ent['ctrl_value'], True) - else: - # Missing, assume true if ctrl_value also isn't present. - return conv_bool(ent['ctrl_value'], True) - - class Context: """Bundles information useful for each transformation. diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index c6ee4ffdb..9b0f667c6 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -6,6 +6,7 @@ from typing_extensions import TypeAlias +from srctools import Entity, conv_bool from srctools.logger import get_logger @@ -52,3 +53,17 @@ def parse_numeric_specifier(text: str, desc: str) -> NumericSpecifier: return (lambda a, b: False, Decimal()) else: return (operation, num) + + +def check_control_enabled(ent: Entity) -> bool: + """Implement the bahaviour of ControlEnables - control_type and control_value. + + This allows providing a fixup value, and optionally inverting it. + """ + # If ctrl_type is 0, ctrl_value needs to be 1 to be enabled. + # If ctrl_type is 1, ctrl_value needs to be 0 to be enabled. + if 'ctrl_type' in ent: + return conv_bool(ent['ctrl_type'], False) != conv_bool(ent['ctrl_value'], True) + else: + # Missing, assume true if ctrl_value also isn't present. + return conv_bool(ent['ctrl_value'], True) From bd8df8eb00bd40992129aabc8cc84df2359d380d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:10:13 +1000 Subject: [PATCH 135/243] Add tests for check_control_enabled --- tests/test_transforms/test_utils.py | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_transforms/test_utils.py diff --git a/tests/test_transforms/test_utils.py b/tests/test_transforms/test_utils.py new file mode 100644 index 000000000..e7795e19d --- /dev/null +++ b/tests/test_transforms/test_utils.py @@ -0,0 +1,42 @@ +"""Test utilty transform logic.""" +from srctools import VMF, Entity + +from hammeraddons.bsp_transform import check_control_enabled + + +def test_check_control_start_enabled() -> None: + """Test the control-enabled options functions, for the start-enabled mode.""" + vmf = VMF() + assert not check_control_enabled(Entity(vmf, { + 'ctrl_type': '0', # Start enabled. + 'ctrl_value': '0', + })) + assert check_control_enabled(Entity(vmf, { + 'ctrl_type': '0', + 'ctrl_value': '1', + })) + + +def test_check_control_start_disabled() -> None: + """Test the control-enabled options functions, for the start-disabled mode.""" + vmf = VMF() + assert check_control_enabled(Entity(vmf, { + 'ctrl_type': '1', # Start disabled + 'ctrl_value': '0', + })) + assert not check_control_enabled(Entity(vmf, { + 'ctrl_type': '1', + 'ctrl_value': '1', + })) + + +def test_check_control_backward_compatiblity() -> None: + """Test the control-enabled options functions, when the mode is missing.""" + vmf = VMF() + # Backwards compatibility, if ctrl_type is missing assume Start Enabled. + assert not check_control_enabled(Entity(vmf, { + 'ctrl_value': '0', + })) + assert check_control_enabled(Entity(vmf, { + 'ctrl_value': '1', + })) From 61b52e5c20921f242f4c60161ae6e3d017a0b9be Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:19:10 +1000 Subject: [PATCH 136/243] Make desc parameter optional --- src/hammeraddons/bsp_transform/common.py | 6 +++--- transforms/comp_case.py | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index 9b0f667c6..3f8daadcd 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -32,14 +32,14 @@ })))) -def parse_numeric_specifier(text: str, desc: str) -> NumericSpecifier: +def parse_numeric_specifier(text: str, desc: str='') -> NumericSpecifier: """Parse case values like "> 5" into the operation and number.""" operation: NumericOp if (match := OPERATION_RE.match(text)) is not None: try: operation = OPERATIONS[match.group()] except KeyError: - LOGGER.warning('Invalid numeric operator "{}" {}', match.group(), desc) + LOGGER.warning('Invalid numeric operator "{}"{}', match.group(), desc) operation = operator.eq num_str = text[match.end():] else: @@ -48,7 +48,7 @@ def parse_numeric_specifier(text: str, desc: str) -> NumericSpecifier: try: num = Decimal(num_str) except ValueError: - LOGGER.warning('Invalid number "{}" {}', num_str, desc) + LOGGER.warning('Invalid number "{}"{}', num_str, desc) # Force this to always fail. return (lambda a, b: False, Decimal()) else: diff --git a/transforms/comp_case.py b/transforms/comp_case.py index 255e9e168..0afa4f505 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -1,9 +1,7 @@ """comp_case is a compile-time collapsible version of logic_case.""" -from typing import Callable, Iterator, List, Tuple -from typing_extensions import TypeAlias +from typing import Iterator, List, Tuple import hashlib -import operator import re import struct import random @@ -86,7 +84,7 @@ def find_matches(param: str) -> Iterator[int]: numeric_cases: List[Tuple[NumericSpecifier, int]] = [ (parse_numeric_specifier( case_params[case_num], - f'for case #{case_num} in {desc}' + f' for case #{case_num} in {desc}' ), case_num) for case_num in key_params ] From 45d478ef6255feb6e0ff9cfd25afca8dd6f8f7ff Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:20:11 +1000 Subject: [PATCH 137/243] Give a name for this lambda so we can check whether it was returned. --- src/hammeraddons/bsp_transform/common.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index 3f8daadcd..2838a5f10 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -4,7 +4,7 @@ from typing import Callable, Tuple from decimal import Decimal -from typing_extensions import TypeAlias +from typing_extensions import Literal, TypeAlias from srctools import Entity, conv_bool from srctools.logger import get_logger @@ -32,6 +32,12 @@ })))) +# noinspection PyUnusedLocal +def op_always_fail(a: Decimal, b: Decimal, /) -> Literal[False]: + """NumericOp implementation which always fails.""" + return False + + def parse_numeric_specifier(text: str, desc: str='') -> NumericSpecifier: """Parse case values like "> 5" into the operation and number.""" operation: NumericOp @@ -50,7 +56,7 @@ def parse_numeric_specifier(text: str, desc: str='') -> NumericSpecifier: except ValueError: LOGGER.warning('Invalid number "{}"{}', num_str, desc) # Force this to always fail. - return (lambda a, b: False, Decimal()) + return (op_always_fail, Decimal()) else: return (operation, num) From 507ae838018d5c25606e7e654d3cb9adbb106d37 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:45:12 +1000 Subject: [PATCH 138/243] Add tests for parse_numeric_specifier, fix behaviour --- src/hammeraddons/bsp_transform/common.py | 13 ++++---- tests/test_transforms/test_utils.py | 41 +++++++++++++++++++++++- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index 2838a5f10..2f42defce 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -2,7 +2,7 @@ import operator import re from typing import Callable, Tuple -from decimal import Decimal +from decimal import Decimal, InvalidOperation from typing_extensions import Literal, TypeAlias @@ -26,8 +26,9 @@ '~=': operator.ne, '=/=': operator.ne, } -# Matches characters present in OPERATIONS -OPERATION_RE = re.compile('({0})+'.format('|'.join(map(re.escape, { +# Matches multiple characters present in OPERATIONS. +# \s skips whitespace beforehand, so we have a capturing group to just grap the actual operation. +OPERATION_RE = re.compile(r'\s*((?:{0})+)'.format('|'.join(map(re.escape, { char for key in OPERATIONS for char in key })))) @@ -43,9 +44,9 @@ def parse_numeric_specifier(text: str, desc: str='') -> NumericSpecifier: operation: NumericOp if (match := OPERATION_RE.match(text)) is not None: try: - operation = OPERATIONS[match.group()] + operation = OPERATIONS[match.group(1)] except KeyError: - LOGGER.warning('Invalid numeric operator "{}"{}', match.group(), desc) + LOGGER.warning('Invalid numeric operator "{}"{}', match.group(1), desc) operation = operator.eq num_str = text[match.end():] else: @@ -53,7 +54,7 @@ def parse_numeric_specifier(text: str, desc: str='') -> NumericSpecifier: num_str = text try: num = Decimal(num_str) - except ValueError: + except InvalidOperation: LOGGER.warning('Invalid number "{}"{}', num_str, desc) # Force this to always fail. return (op_always_fail, Decimal()) diff --git a/tests/test_transforms/test_utils.py b/tests/test_transforms/test_utils.py index e7795e19d..463008def 100644 --- a/tests/test_transforms/test_utils.py +++ b/tests/test_transforms/test_utils.py @@ -1,7 +1,11 @@ """Test utilty transform logic.""" +import logging +from decimal import Decimal +import operator + from srctools import VMF, Entity -from hammeraddons.bsp_transform import check_control_enabled +from hammeraddons.bsp_transform.common import check_control_enabled, parse_numeric_specifier, op_always_fail def test_check_control_start_enabled() -> None: @@ -40,3 +44,38 @@ def test_check_control_backward_compatiblity() -> None: assert check_control_enabled(Entity(vmf, { 'ctrl_value': '1', })) + + +def test_numeric_parsing_good(caplog) -> None: + """Test the numeric parsing function, with good values.""" + caplog.set_level(logging.WARNING) + assert parse_numeric_specifier(' 42 ') == (operator.eq, Decimal('42')) + assert parse_numeric_specifier('<3') == (operator.lt, Decimal('3')) + assert parse_numeric_specifier(' > 45.872_67') == (operator.gt, Decimal('45.87267')) + assert parse_numeric_specifier('>= -0892.930') == (operator.ge, Decimal('-892.93')) + assert parse_numeric_specifier('=4') == (operator.eq, Decimal(4)) + assert parse_numeric_specifier('== -17') == (operator.eq, Decimal(-17)) + assert parse_numeric_specifier(' != 909') == (operator.ne, Decimal(909)) + assert parse_numeric_specifier(' =!= 87') == (operator.ne, Decimal(87)) + assert parse_numeric_specifier('~= 38') == (operator.ne, Decimal(38)) + assert parse_numeric_specifier(' =/= 24.5') == (operator.ne, Decimal('24.5')) + # No warnings caught. + assert caplog.record_tuples == [] + + +def test_numeric_parsing_bad_op(caplog) -> None: + """Test the numeric parsing function, when given an illegal operator.""" + assert parse_numeric_specifier(' <> 34.83') == (operator.eq, Decimal('34.83')) + assert parse_numeric_specifier(' >!> 34.83') == (operator.eq, Decimal('34.83')) + assert caplog.record_tuples == [ + ('srctools.hammeraddons.bsp_transform.common', logging.WARNING, 'Invalid numeric operator "<>"'), + ('srctools.hammeraddons.bsp_transform.common', logging.WARNING, 'Invalid numeric operator ">!>"'), + ] + + +def test_numeric_parsing_bad_number(caplog) -> None: + """Test the numeric parsing function, when given a totally invalid number.""" + assert parse_numeric_specifier('hello') == (op_always_fail, Decimal()) + assert caplog.record_tuples == [ + ('srctools.hammeraddons.bsp_transform.common', logging.WARNING, 'Invalid number "hello"') + ] From 5dd815af1e1817008b960bfb92f5ef8f4015c2bb Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:48:36 +1000 Subject: [PATCH 139/243] Simplify the regex --- src/hammeraddons/bsp_transform/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index 2f42defce..424a09a30 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -28,7 +28,7 @@ } # Matches multiple characters present in OPERATIONS. # \s skips whitespace beforehand, so we have a capturing group to just grap the actual operation. -OPERATION_RE = re.compile(r'\s*((?:{0})+)'.format('|'.join(map(re.escape, { +OPERATION_RE = re.compile(r'\s*([{0}]+)'.format(''.join(map(re.escape, { char for key in OPERATIONS for char in key })))) From 046cbc6b5a0feea9958749c50a4fd3c032952884 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:48:47 +1000 Subject: [PATCH 140/243] Fix these default names --- fgd/point/comp/comp_case.fgd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/point/comp/comp_case.fgd b/fgd/point/comp/comp_case.fgd index e66481997..064d1245a 100644 --- a/fgd/point/comp/comp_case.fgd +++ b/fgd/point/comp/comp_case.fgd @@ -11,10 +11,10 @@ "are we allowed to test the rest of the cases or should we stop there? " + "Don't worry about this if you're only using this entity for PickRandom." - value(string) : "Input Value" : "If the InValue parameter is blank or Trigger is used, this value will be used instead." + value(string) : "Input Value" : : "If the InValue parameter is blank or Trigger is used, this value will be used instead." - mode[engine](string) : "Mode" : "text" - mode(choices) : "Mode" : "string_casefold" : "Specifies how comparisons are performed. Text mode simply checks for a case that matches the input text. " + + mode[engine](string) : "Mode" : "string" + mode(choices) : "Mode" : "casefold" : "Specifies how comparisons are performed. Text mode simply checks for a case that matches the input text. " + "Numeric treats values as numbers, allowing cases to additionally specify a comparison like '< 3.14'. In all modes, each case is compared in order." = [ "string" : "Text - Case Sensitive" From 809ccaea7be5a96d99b38da3a2ac288f2373bf92 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 11:57:38 +1000 Subject: [PATCH 141/243] Fix postcompiler not actually working --- CHANGELOG.md | 2 +- src/hammeraddons/bsp_transform/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eae59fdc..b01f1ed69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,7 +82,7 @@ * Particle systems will now be detected and packed along with their dependencies. This needs configuration in the config file, since different games use different filenames. * Optionally, the postcompiler can collapse and remove `func_instance_io_proxy` from maps entirely to save ents. -* Add comp_sequential_call: finds a sequence of entities (by distance or numeric suffix), then fires inputs delayed in order. +* Add `comp_sequential_call`: finds a sequence of entities (by distance or numeric suffix), then fires inputs delayed in order. * Add `comp_flicker`: fires on/off and skin inputs repeatedly to simulate a flicker-on effect. * `comp_scriptvar_setter` can now set global variables also. * `prop_paint_bomb` will now show its collision mesh (futbols). diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index c4ac293a4..64ee6e8c5 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -206,7 +206,7 @@ async def run_transformations( init_scripts.append(pack.inject_vscript(code.replace('`', '"'))) ent['vscripts'] = ' '.join(init_scripts) - context._apply_remaps() + apply_io_remaps(context) # noinspection PyProtectedMember From 779e971c54aea78f27700800cd38e49b680b9e5d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 12:00:49 +1000 Subject: [PATCH 142/243] Actually remove comp_case from the map --- transforms/comp_case.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transforms/comp_case.py b/transforms/comp_case.py index 0afa4f505..4191924db 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -142,7 +142,9 @@ def handle_pick_random(source: Entity, out: Output) -> List[Output]: @trans('comp_case', priority=10) def comp_case(ctx: Context) -> None: """A version of logic_case which is collapsed at compile time.""" + ent: Entity for ent in ctx.vmf.by_class['comp_case']: + ent.remove() if check_control_enabled(ent): collapse_case(ctx, ent) else: From df68bf38b6317a907f26b146c81ce9acaee8fdf2 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 12:52:01 +1000 Subject: [PATCH 143/243] Add an example map for comp_case --- examples/hadd_comp_case.vmf | 6645 +++++++++++++++++++++++++++++++++++ 1 file changed, 6645 insertions(+) create mode 100644 examples/hadd_comp_case.vmf diff --git a/examples/hadd_comp_case.vmf b/examples/hadd_comp_case.vmf new file mode 100644 index 000000000..1f047975b --- /dev/null +++ b/examples/hadd_comp_case.vmf @@ -0,0 +1,6645 @@ +versioninfo +{ + "editorversion" "400" + "editorbuild" "9520" + "mapversion" "51" + "formatversion" "100" + "prefab" "0" +} +visgroups +{ +} +viewsettings +{ + "bSnapToGrid" "1" + "bShowGrid" "1" + "bShowLogicalGrid" "0" + "nGridSpacing" "16" + "bShow3DGrid" "1" +} +world +{ + "id" "1" + "mapversion" "51" + "classname" "worldspawn" + "detailmaterial" "detail/detailsprites" + "detailvbsp" "detail.vbsp" + "maxpropscreenwidth" "-1" + "skyname" "sky_day01_01" + solid + { + "id" "4" + side + { + "id" "18" + "plane" "(-64 320 128) (0 320 128) (0 0 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "17" + "plane" "(-64 0 0) (0 0 0) (0 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "16" + "plane" "(-64 320 128) (-64 0 128) (-64 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "15" + "plane" "(0 320 0) (0 0 0) (0 0 128)" + "material" "DEV/DEV_MEASUREGENERIC01" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "14" + "plane" "(0 320 128) (-64 320 128) (-64 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "13" + "plane" "(0 0 0) (-64 0 0) (-64 0 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "7" + side + { + "id" "30" + "plane" "(448 320 128) (448 0 128) (384 0 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "29" + "plane" "(448 0 0) (448 320 0) (384 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "28" + "plane" "(384 320 128) (384 0 128) (384 0 0)" + "material" "DEV/DEV_MEASUREGENERIC01" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "27" + "plane" "(448 0 128) (448 320 128) (448 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "26" + "plane" "(448 320 128) (384 320 128) (384 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "25" + "plane" "(384 0 128) (448 0 128) (448 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "24" + side + { + "id" "42" + "plane" "(0 320 128) (0 384 128) (384 384 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "41" + "plane" "(0 384 0) (0 320 0) (384 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "40" + "plane" "(0 320 0) (0 384 0) (0 384 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "39" + "plane" "(384 384 0) (384 320 0) (384 320 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "38" + "plane" "(0 384 0) (384 384 0) (384 384 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "37" + "plane" "(384 320 0) (0 320 0) (0 320 128)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "27" + side + { + "id" "54" + "plane" "(384 0 128) (384 -64 128) (0 -64 128)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "53" + "plane" "(384 -64 0) (384 0 0) (0 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "52" + "plane" "(0 0 128) (0 -64 128) (0 -64 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "51" + "plane" "(384 -64 128) (384 0 128) (384 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "50" + "plane" "(384 0 128) (0 0 128) (0 0 0)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "49" + "plane" "(0 -64 128) (384 -64 128) (384 -64 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "33" + side + { + "id" "84" + "plane" "(384 384 256) (384 320 256) (0 320 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "83" + "plane" "(384 320 128) (384 384 128) (0 384 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "82" + "plane" "(0 384 256) (0 320 256) (0 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "81" + "plane" "(384 320 256) (384 384 256) (384 384 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "80" + "plane" "(384 384 256) (0 384 256) (0 384 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "79" + "plane" "(0 320 256) (384 320 256) (384 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "34" + side + { + "id" "90" + "plane" "(-64 320 256) (0 320 256) (0 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "89" + "plane" "(-64 0 128) (0 0 128) (0 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "88" + "plane" "(-64 320 256) (-64 0 256) (-64 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "87" + "plane" "(0 320 128) (0 0 128) (0 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "86" + "plane" "(0 320 256) (-64 320 256) (-64 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "85" + "plane" "(0 0 128) (-64 0 128) (-64 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "35" + side + { + "id" "96" + "plane" "(384 0 256) (384 -64 256) (0 -64 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "95" + "plane" "(384 -64 128) (384 0 128) (0 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "94" + "plane" "(0 0 256) (0 -64 256) (0 -64 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "93" + "plane" "(384 -64 256) (384 0 256) (384 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "92" + "plane" "(384 0 256) (0 0 256) (0 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "91" + "plane" "(0 -64 256) (384 -64 256) (384 -64 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "36" + side + { + "id" "102" + "plane" "(448 320 256) (448 0 256) (384 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "101" + "plane" "(448 0 128) (448 320 128) (384 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "100" + "plane" "(384 320 256) (384 0 256) (384 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "99" + "plane" "(448 0 256) (448 320 256) (448 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "98" + "plane" "(448 320 256) (384 320 256) (384 320 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "97" + "plane" "(384 0 256) (448 0 256) (448 0 128)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "53" + side + { + "id" "114" + "plane" "(384 320 320) (384 0 320) (0 0 320)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "113" + "plane" "(384 0 256) (384 320 256) (0 320 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "112" + "plane" "(0 320 320) (0 0 320) (0 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "111" + "plane" "(384 0 320) (384 320 320) (384 320 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "110" + "plane" "(384 320 320) (0 320 320) (0 320 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "109" + "plane" "(0 0 320) (384 0 320) (384 0 256)" + "material" "TOOLS/TOOLSSKYBOX" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "5279" + side + { + "id" "1191" + "plane" "(0 0 0) (0 96 0) (384 96 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1190" + "plane" "(0 96 -64) (0 0 -64) (384 0 -64)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1189" + "plane" "(0 0 -64) (0 96 -64) (0 96 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1188" + "plane" "(384 96 -64) (384 0 -64) (384 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1187" + "plane" "(384 0 -64) (0 0 -64) (0 0 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1186" + "plane" "(0 96 -64) (384 96 -64) (384 96 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "5280" + side + { + "id" "1197" + "plane" "(0 96 0) (0 208 0) (384 208 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1196" + "plane" "(0 208 -64) (0 96 -64) (384 96 -64)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1195" + "plane" "(0 96 -64) (0 208 -64) (0 208 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1194" + "plane" "(384 208 -64) (384 96 -64) (384 96 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1193" + "plane" "(384 96 -64) (0 96 -64) (0 96 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1192" + "plane" "(0 208 -64) (384 208 -64) (384 208 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "5281" + side + { + "id" "1203" + "plane" "(0 208 0) (0 320 0) (384 320 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1202" + "plane" "(0 320 -64) (0 208 -64) (384 208 -64)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1201" + "plane" "(0 208 -64) (0 320 -64) (0 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1200" + "plane" "(384 320 -64) (384 208 -64) (384 208 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1199" + "plane" "(0 320 -64) (384 320 -64) (384 320 0)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1198" + "plane" "(384 208 -64) (0 208 -64) (0 208 0)" + "material" "DEV/DEV_MEASUREGENERIC01B" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 148 189" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + group + { + "id" "1339" + editor + { + "color" "220 220 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + group + { + "id" "1348" + editor + { + "color" "220 220 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } +} +entity +{ + "id" "109" + "classname" "info_player_start" + "angles" "0 45 0" + "origin" "32 32 0" + editor + { + "color" "0 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 5000]" + } +} +entity +{ + "id" "117" + "classname" "light_environment" + "_ambient" "255 255 255 20" + "_ambienthdr" "-1 -1 -1 1" + "_ambientscalehdr" "1" + "_light" "254 252 216 200" + "_lighthdr" "-1 -1 -1 1" + "_lightscalehdr" "1" + "angles" "-75 18.5 0" + "pitch" "-65" + "style" "0" + "sunspreadangle" "2" + "origin" "32 32 96" + editor + { + "color" "255 255 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 5500]" + } +} +entity +{ + "id" "157" + "classname" "func_detail" + solid + { + "id" "145" + side + { + "id" "150" + "plane" "(192 304 64) (192 320 64) (224 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 -256] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "149" + "plane" "(192 320 0) (192 304 0) (224 304 0)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 -256] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "148" + "plane" "(192 304 0) (192 320 0) (192 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "147" + "plane" "(224 320 0) (224 304 0) (224 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "146" + "plane" "(192 320 0) (224 320 0) (224 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 -256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "145" + "plane" "(224 304 0) (192 304 0) (192 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 -256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "164" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 0 0" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_eq_5" + "origin" "208 312 72" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "360" + "classname" "func_detail" + solid + { + "id" "352" + side + { + "id" "351" + "plane" "(202 302 26) (202 302 20) (196 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 -96] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "350" + "plane" "(196 304 26) (196 304 20) (202 304 20)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -32] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "349" + "plane" "(196 302 26) (196 302 20) (196 304 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "348" + "plane" "(202 304 26) (202 304 20) (202 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "347" + "plane" "(202 302 26) (196 302 26) (196 304 26)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + side + { + "id" "346" + "plane" "(202 304 20) (196 304 20) (196 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "313" + side + { + "id" "357" + "plane" "(202 304 26) (202 304 20) (212 304 24)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "356" + "plane" "(202 302 20) (202 302 26) (208 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "355" + "plane" "(202 302 26) (202 302 20) (202 304 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "354" + "plane" "(208 304 28) (212 304 24) (212 302 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "353" + "plane" "(212 304 24) (202 304 20) (202 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + side + { + "id" "352" + "plane" "(208 302 28) (202 302 26) (202 304 26)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "314" + side + { + "id" "363" + "plane" "(208 304 28) (212 304 24) (216 304 34)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "362" + "plane" "(212 302 24) (208 302 28) (210 302 34)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "361" + "plane" "(208 302 28) (212 302 24) (212 304 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "360" + "plane" "(216 302 34) (210 302 34) (210 304 34)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "359" + "plane" "(216 304 34) (212 304 24) (212 302 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + side + { + "id" "358" + "plane" "(210 302 34) (208 302 28) (208 304 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "307" + side + { + "id" "369" + "plane" "(210 304 34) (216 304 34) (212 304 44)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "368" + "plane" "(216 302 34) (210 302 34) (208 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "367" + "plane" "(216 304 34) (210 304 34) (210 302 34)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "366" + "plane" "(212 302 44) (208 302 40) (208 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "365" + "plane" "(212 304 44) (216 304 34) (216 302 34)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + side + { + "id" "364" + "plane" "(208 302 40) (210 302 34) (210 304 34)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "308" + side + { + "id" "375" + "plane" "(202 304 48) (202 304 42) (208 304 40)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "374" + "plane" "(212 302 44) (208 302 40) (202 302 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "373" + "plane" "(212 304 44) (208 304 40) (208 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "372" + "plane" "(202 302 48) (202 302 42) (202 304 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "371" + "plane" "(212 302 44) (202 302 48) (202 304 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + side + { + "id" "370" + "plane" "(208 304 40) (202 304 42) (202 302 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "350" + side + { + "id" "381" + "plane" "(202 302 54) (202 302 42) (196 302 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "380" + "plane" "(196 304 54) (196 304 42) (202 304 42)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "379" + "plane" "(196 302 54) (196 302 42) (196 304 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "378" + "plane" "(202 304 54) (202 304 42) (202 302 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "377" + "plane" "(202 302 54) (196 302 54) (196 304 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "376" + "plane" "(202 304 42) (196 304 42) (196 302 42)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "351" + side + { + "id" "387" + "plane" "(216 302 60) (216 302 54) (196 302 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 0 -1 40] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "386" + "plane" "(196 304 60) (196 304 54) (216 304 54)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 40] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "385" + "plane" "(196 302 60) (196 302 54) (196 304 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -40] 0.25" + "vaxis" "[0 1 0 -352] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "384" + "plane" "(216 304 60) (216 304 54) (216 302 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -40] 0.25" + "vaxis" "[0 1 0 -352] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "383" + "plane" "(216 302 60) (196 302 60) (196 304 60)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -352] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "382" + "plane" "(216 304 54) (196 304 54) (196 302 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -128] 0.25" + "vaxis" "[0 1 0 -352] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 6000]" + } +} +entity +{ + "id" "523" + "classname" "func_detail" + solid + { + "id" "524" + side + { + "id" "413" + "plane" "(96 304 64) (96 320 64) (160 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 128] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "412" + "plane" "(96 320 0) (96 304 0) (160 304 0)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 128] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "411" + "plane" "(96 304 0) (96 320 0) (96 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "410" + "plane" "(160 320 0) (160 304 0) (160 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "409" + "plane" "(96 320 0) (160 320 0) (160 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "408" + "plane" "(160 304 0) (96 304 0) (96 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 -384] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "547" + "classname" "func_detail" + solid + { + "id" "548" + side + { + "id" "434" + "plane" "(132 302 44) (132 302 40) (108 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -48] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "433" + "plane" "(108 304 36) (108 304 28) (132 304 40)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -48] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "432" + "plane" "(132 304 44) (132 304 40) (132 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + side + { + "id" "431" + "plane" "(108 302 36) (108 302 28) (108 304 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "430" + "plane" "(124 302 44) (108 302 36) (108 304 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "429" + "plane" "(132 304 40) (108 304 28) (108 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "428" + "plane" "(132 302 44) (124 302 44) (124 304 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 -1 0 -4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "549" + side + { + "id" "441" + "plane" "(132 302 48) (132 302 44) (124 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 1 -400] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "440" + "plane" "(108 304 60) (108 304 52) (124 304 44)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 1 -16] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "439" + "plane" "(132 304 48) (132 304 44) (132 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 400] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + side + { + "id" "438" + "plane" "(108 302 60) (108 302 52) (108 304 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 400] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "437" + "plane" "(124 304 44) (108 304 52) (108 302 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "436" + "plane" "(132 302 48) (108 302 60) (108 304 60)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + side + { + "id" "435" + "plane" "(132 304 44) (124 304 44) (124 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 -1 0 -4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 6500]" + } +} +entity +{ + "id" "852" + "classname" "func_detail" + solid + { + "id" "821" + side + { + "id" "591" + "plane" "(150.402 304 42.8594) (146.803 304 45.7152) (141.398 304 44.2853)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "590" + "plane" "(141.398 302 40) (141.398 302 44.2853) (146.802 302 45.7149)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 1 -436] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "589" + "plane" "(150.402 302 42.8594) (146.803 302 45.7152) (146.802 304 45.7147)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "588" + "plane" "(141.4 302 44.2857) (141.4 302 40) (141.4 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "587" + "plane" "(150.402 304 42.8574) (141.4 304 40) (141.398 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + side + { + "id" "586" + "plane" "(146.801 302 45.7148) (141.4 302 44.286) (141.398 304 44.2852)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "822" + side + { + "id" "597" + "plane" "(154 304 50) (148.6 304 50) (146.8 304 45.7133)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "596" + "plane" "(150.398 302 42.8555) (146.799 302 45.7126) (148.6 302 50)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 1 -436] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "595" + "plane" "(154 302 50) (148.6 302 50) (148.6 304 50)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "594" + "plane" "(150.398 304 42.8594) (146.8 304 45.7153) (146.801 302 45.7156)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "593" + "plane" "(154 304 50) (150.4 304 42.8571) (150.4 302 42.8594)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "1" + } + side + { + "id" "592" + "plane" "(148.6 302 50) (146.8 302 45.7145) (146.801 304 45.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "823" + side + { + "id" "603" + "plane" "(141.398 304 60) (141.398 304 55.7147) (146.802 304 54.2851)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "602" + "plane" "(150.398 302 57.1445) (146.798 302 54.2863) (141.398 302 55.7147)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 1 -436] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "601" + "plane" "(141.4 302 60) (141.4 302 55.7143) (141.4 304 55.7143)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "600" + "plane" "(150.398 304 57.1406) (146.8 304 54.2857) (146.799 302 54.286)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "599" + "plane" "(150.398 302 57.1426) (141.398 302 60) (141.398 304 60)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "1" + } + side + { + "id" "598" + "plane" "(146.801 304 54.2871) (141.398 304 55.7158) (141.395 302 55.7158)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "824" + side + { + "id" "609" + "plane" "(141.399 302 60) (141.399 302 55.7148) (136 302 55.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 1 -480] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "608" + "plane" "(136 304 60) (136 304 55.7148) (141.399 304 55.7148)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 1 -32] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "607" + "plane" "(136 302 60) (136 302 55.7148) (136 304 55.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 480] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "606" + "plane" "(141.4 304 60) (141.4 304 55.7148) (141.4 302 55.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 480] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "605" + "plane" "(141.398 304 55.7143) (136 304 55.7143) (136 302 55.7143)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "604" + "plane" "(141.398 302 60) (136 302 60) (136 304 60)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "825" + side + { + "id" "615" + "plane" "(150.402 304 57.1445) (146.8 304 54.2851) (148.6 304 50)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "614" + "plane" "(154 302 50) (148.598 302 50) (146.8 302 54.285)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 1 -436] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "613" + "plane" "(150.398 302 57.1445) (146.799 302 54.2874) (146.8 304 54.2867)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "612" + "plane" "(154 304 50) (148.6 304 50) (148.6 302 50)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "611" + "plane" "(150.402 304 57.1406) (154 304 50) (154 302 50)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "1" + } + side + { + "id" "610" + "plane" "(146.799 302 54.2891) (148.6 302 50) (148.604 304 50)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 436] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "598" + side + { + "id" "621" + "plane" "(141.399 302 44.2852) (141.399 302 35.7143) (136 302 35.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "620" + "plane" "(136 304 44.2852) (136 304 35.7143) (141.399 304 35.7148)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "619" + "plane" "(136 302 44.2852) (136 302 35.7143) (136 304 35.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "618" + "plane" "(141.4 304 44.2852) (141.4 304 35.7143) (141.4 302 35.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "617" + "plane" "(141.398 302 44.2857) (136 302 44.2857) (136 304 44.2857)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "616" + "plane" "(141.398 304 35.7143) (136 304 35.7143) (136 302 35.7143)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -316] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "597" + side + { + "id" "627" + "plane" "(141.398 304 40) (141.398 304 35.7147) (146.799 304 34.286)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "626" + "plane" "(150.402 302 37.1406) (146.803 302 34.2848) (141.398 302 35.7147)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "625" + "plane" "(150.398 304 37.1445) (146.798 304 34.2863) (146.802 302 34.2851)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "624" + "plane" "(141.4 302 40) (141.4 302 35.7143) (141.4 304 35.7143)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "623" + "plane" "(150.406 302 37.1426) (141.4 302 40) (141.398 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "2" + } + side + { + "id" "622" + "plane" "(146.797 304 34.2861) (141.4 304 35.7139) (141.398 302 35.7148)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "595" + side + { + "id" "633" + "plane" "(146.8 304 25.7134) (150.4 304 22.8563) (154 304 30)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "632" + "plane" "(150.406 302 22.8555) (146.801 302 25.7173) (148.6 302 30)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "631" + "plane" "(150.398 304 22.8555) (146.799 304 25.7126) (146.799 302 25.711)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "630" + "plane" "(154 302 30) (148.6 302 30) (148.6 304 30)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "629" + "plane" "(154 304 30) (150.398 304 22.8555) (150.4 302 22.8594)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + side + { + "id" "628" + "plane" "(148.598 302 30) (146.799 302 25.7148) (146.797 304 25.7109)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "594" + side + { + "id" "639" + "plane" "(141.398 304 24.2853) (141.398 304 20) (150.398 304 22.8555)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "638" + "plane" "(141.398 302 20) (141.398 302 24.2853) (146.799 302 25.714)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "637" + "plane" "(141.4 302 24.2857) (141.4 302 20) (141.4 304 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "636" + "plane" "(150.398 302 22.8555) (146.798 302 25.7137) (146.795 304 25.713)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "635" + "plane" "(150.398 304 22.8574) (141.398 304 20) (141.398 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + side + { + "id" "634" + "plane" "(146.797 302 25.7139) (141.398 302 24.2852) (141.398 304 24.2852)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "596" + side + { + "id" "645" + "plane" "(148.6 304 30) (154 304 30) (150.398 304 37.1445)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "644" + "plane" "(154 302 30) (148.6 302 30) (146.8 302 34.2867)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 -52] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "643" + "plane" "(154 304 30) (148.6 304 30) (148.6 302 30)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "642" + "plane" "(150.398 302 37.1445) (146.799 302 34.2874) (146.801 304 34.2844)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "641" + "plane" "(150.4 304 37.1406) (154 304 30) (154 302 30)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "2" + } + side + { + "id" "640" + "plane" "(146.799 302 34.2852) (148.6 302 30) (148.6 304 30)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 52] 0.25" + "vaxis" "[0 1 0 -276] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "593" + side + { + "id" "651" + "plane" "(141.399 302 24.2852) (141.399 302 20) (136 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 0 -1 -96] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "650" + "plane" "(136 304 24.2852) (136 304 20) (141.399 304 20)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -32] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "649" + "plane" "(136 302 24.2852) (136 302 20) (136 304 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "648" + "plane" "(141.4 304 24.2852) (141.4 304 20) (141.4 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "647" + "plane" "(141.398 302 24.2857) (136 302 24.2857) (136 304 24.2857)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "646" + "plane" "(141.398 304 20) (136 304 20) (136 302 20)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 112] 0.25" + "vaxis" "[0 1 0 -216] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 9500]" + } +} +entity +{ + "id" "913" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 0 0" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_gt_3" + "origin" "128 312 72" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "1017" + "classname" "comp_case" + "case01" ">= 3" + "case02" "5" + "case03" "< 8" + "ctrl_type" "0" + "ctrl_value" "1" + "mode" "numeric" + "multiplecasesallowed" "1" + "targetname" "case_numbers" + connections + { + "OnUsed" "spr_*,Color,255 0 0,1,-1" + "OnCase01" "spr_gt_3,Color,0 255 0,0,-1" + "OnCase02" "spr_eq_5,Color,0 255 0,0,-1" + "OnCase03" "spr_lt_8,Color,0 255 0,0,-1" + } + "origin" "208 280 40" + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "comments" "Demonstrates the numeric mode, allowing doing numeric comparisons in addition to just equality." + "logicalpos" "[0 10000]" + } +} +entity +{ + "id" "1163" + "classname" "func_detail" + solid + { + "id" "1164" + side + { + "id" "678" + "plane" "(284 302 32) (284 302 24) (260 302 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 0 -1 -64] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "677" + "plane" "(260 304 40) (260 304 36) (284 304 24)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "676" + "plane" "(260 302 40) (260 302 36) (260 304 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 64] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "675" + "plane" "(284 304 32) (284 304 24) (284 302 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 64] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "674" + "plane" "(284 302 32) (268 302 40) (268 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "673" + "plane" "(284 304 24) (260 304 36) (260 302 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "672" + "plane" "(268 302 40) (260 302 40) (260 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 -1 0 -4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1165" + side + { + "id" "685" + "plane" "(284 302 56) (284 302 48) (268 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 0 1 -384] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "684" + "plane" "(260 304 44) (260 304 40) (268 304 40)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 0 1 0] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "683" + "plane" "(260 302 44) (260 302 40) (260 304 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 384] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "682" + "plane" "(284 304 56) (284 304 48) (284 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 -1 384] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "681" + "plane" "(284 304 48) (268 304 40) (268 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "680" + "plane" "(284 302 56) (260 302 44) (260 304 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "679" + "plane" "(268 304 40) (260 304 40) (260 302 40)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 16] 0.25" + "vaxis" "[0 -1 0 -4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 6500]" + } +} +entity +{ + "id" "1166" + "classname" "func_detail" + solid + { + "id" "1167" + side + { + "id" "691" + "plane" "(256 304 64) (256 320 64) (320 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "690" + "plane" "(256 320 0) (256 304 0) (320 304 0)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 128] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "689" + "plane" "(256 304 0) (256 320 0) (256 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "688" + "plane" "(320 320 0) (320 304 0) (320 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[0 1 0 -128] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "687" + "plane" "(256 320 0) (320 320 0) (320 320 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "686" + "plane" "(320 304 0) (256 304 0) (256 304 64)" + "material" "DEV/REFLECTIVITY_20" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 500]" + } +} +entity +{ + "id" "1465" + "classname" "func_detail" + solid + { + "id" "1340" + side + { + "id" "841" + "plane" "(312 304 48) (309 304 55) (306 304 52)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "840" + "plane" "(308 302 48) (306 302 52) (309 302 55)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "839" + "plane" "(312 302 48) (312 304 48) (308 304 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "838" + "plane" "(309 304 55) (309 302 55) (306 302 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "837" + "plane" "(309 304 55) (312 304 48) (312 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "836" + "plane" "(308 304 48) (306 304 52) (306 302 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1341" + side + { + "id" "847" + "plane" "(309 304 55) (302 304 58) (302 304 54)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "846" + "plane" "(306 302 52) (302 302 54) (302 302 58)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "845" + "plane" "(309 302 55) (309 304 55) (306 304 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "844" + "plane" "(302 304 58) (302 302 58) (302 302 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "843" + "plane" "(302 304 58) (309 304 55) (309 302 55)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "842" + "plane" "(306 304 52) (302 304 54) (302 302 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1342" + side + { + "id" "853" + "plane" "(302 304 58) (295 304 55) (298 304 52)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "852" + "plane" "(302 302 54) (298 302 52) (295 302 55)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "851" + "plane" "(302 302 58) (302 304 58) (302 304 54)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "850" + "plane" "(295 304 55) (295 302 55) (298 302 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "849" + "plane" "(295 304 55) (302 304 58) (302 302 58)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "848" + "plane" "(302 304 54) (298 304 52) (298 302 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1343" + side + { + "id" "859" + "plane" "(295 304 55) (292 304 48) (296 304 48)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "858" + "plane" "(298 302 52) (296 302 48) (292 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "857" + "plane" "(295 302 55) (295 304 55) (298 304 52)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "856" + "plane" "(292 304 48) (292 302 48) (296 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "855" + "plane" "(292 304 48) (295 304 55) (295 302 55)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "854" + "plane" "(298 304 52) (296 304 48) (296 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1344" + side + { + "id" "865" + "plane" "(292 304 48) (295 304 41) (298 304 44)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "864" + "plane" "(296 302 48) (298 302 44) (295 302 41)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "863" + "plane" "(292 302 48) (292 304 48) (296 304 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "862" + "plane" "(295 304 41) (295 302 41) (298 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "861" + "plane" "(295 304 41) (292 304 48) (292 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "860" + "plane" "(296 304 48) (298 304 44) (298 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1347" + side + { + "id" "883" + "plane" "(309 304 41) (312 304 48) (308 304 48)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "882" + "plane" "(306 302 44) (308 302 48) (312 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "881" + "plane" "(309 302 41) (309 304 41) (306 304 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "880" + "plane" "(312 304 48) (312 302 48) (308 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "879" + "plane" "(312 304 48) (309 304 41) (309 302 41)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "8" + } + side + { + "id" "878" + "plane" "(306 304 44) (308 304 48) (308 302 48)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "1" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "5001" + side + { + "id" "1152" + "plane" "(298 304 44) (295 304 41) (306 304 36)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1151" + "plane" "(309 302 39) (306 302 36) (295 302 41)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 4] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1150" + "plane" "(295 304 41) (298 304 44) (298 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1149" + "plane" "(306 302 36) (309 302 39) (309 304 39)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 -4] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1148" + "plane" "(295 302 41) (306 302 36) (306 304 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "10" + } + side + { + "id" "1147" + "plane" "(298 304 44) (309 304 39) (309 302 39)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "5" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1349" + side + { + "id" "889" + "plane" "(312 304 32) (309 304 39) (306 304 36)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "888" + "plane" "(308 302 32) (306 302 36) (309 302 39)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "887" + "plane" "(312 302 32) (312 304 32) (308 304 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "886" + "plane" "(309 304 39) (309 302 39) (306 302 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "885" + "plane" "(309 304 39) (312 304 32) (312 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "884" + "plane" "(308 304 32) (306 304 36) (306 302 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1352" + side + { + "id" "907" + "plane" "(295 304 39) (292 304 32) (296 304 32)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "906" + "plane" "(298 302 36) (296 302 32) (292 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "905" + "plane" "(295 302 39) (295 304 39) (298 304 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "904" + "plane" "(292 304 32) (292 302 32) (296 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "903" + "plane" "(292 304 32) (295 304 39) (295 302 39)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "902" + "plane" "(298 304 36) (296 304 32) (296 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1353" + side + { + "id" "913" + "plane" "(292 304 32) (295 304 25) (298 304 28)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "912" + "plane" "(296 302 32) (298 302 28) (295 302 25)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "911" + "plane" "(292 302 32) (292 304 32) (296 304 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "910" + "plane" "(295 304 25) (295 302 25) (298 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "909" + "plane" "(295 304 25) (292 304 32) (292 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "908" + "plane" "(296 304 32) (298 304 28) (298 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1354" + side + { + "id" "919" + "plane" "(295 304 25) (302 304 22) (302 304 26)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "918" + "plane" "(298 302 28) (302 302 26) (302 302 22)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "917" + "plane" "(295 302 25) (295 304 25) (298 304 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "916" + "plane" "(302 304 22) (302 302 22) (302 302 26)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "915" + "plane" "(302 304 22) (295 304 25) (295 302 25)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "914" + "plane" "(298 304 28) (302 304 26) (302 302 26)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1355" + side + { + "id" "925" + "plane" "(302 304 22) (309 304 25) (306 304 28)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "924" + "plane" "(302 302 26) (306 302 28) (309 302 25)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "923" + "plane" "(302 302 22) (302 304 22) (302 304 26)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "922" + "plane" "(309 304 25) (309 302 25) (306 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "921" + "plane" "(309 304 25) (302 304 22) (302 302 22)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "920" + "plane" "(302 304 26) (306 304 28) (306 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "1356" + side + { + "id" "931" + "plane" "(309 304 25) (312 304 32) (308 304 32)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "930" + "plane" "(306 302 28) (308 302 32) (312 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "929" + "plane" "(309 302 25) (309 304 25) (306 304 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "928" + "plane" "(312 304 32) (312 302 32) (308 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "927" + "plane" "(312 304 32) (309 304 25) (309 302 25)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "4" + } + side + { + "id" "926" + "plane" "(306 304 28) (308 304 32) (308 302 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "2" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + solid + { + "id" "5031" + side + { + "id" "1173" + "plane" "(309 304 41) (306 304 44) (295 304 39)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1172" + "plane" "(298 302 36) (295 302 39) (306.003 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 -60] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1170" + "plane" "(298 304 36) (295 304 39) (295 302 39)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 60] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1169" + "plane" "(295 304 39) (306 304 44) (306.003 302 44)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "5" + } + side + { + "id" "1168" + "plane" "(298 302 36) (309 302 41) (309 304 41)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 1 0 -324] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "10" + } + side + { + "id" "1167" + "plane" "(306 304 44) (309 304 41) (309 302 41)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1000]" + } +} +entity +{ + "id" "1514" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 0 0" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_lt_8" + "origin" "289 312 72" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "1617" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "96 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,1,0,-1" + } + solid + { + "id" "1604" + side + { + "id" "943" + "plane" "(88 256 32) (104 256 32) (104 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 384] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "942" + "plane" "(88 240 -1.03315e-006) (104 240 -1.03315e-006) (104 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 384] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "941" + "plane" "(88 256 32) (88 240 32) (88 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "940" + "plane" "(104 256 -1.03315e-006) (104 240 -1.03315e-006) (104 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "939" + "plane" "(104 256 32) (88 256 32) (88 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 384] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "938" + "plane" "(104 240 -1.03315e-006) (88 240 -1.03315e-006) (88 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 384] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1648" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "1" + "radius" "128" + "origin" "96 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1678" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "2" + "radius" "128" + "origin" "120 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1682" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "120 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,2,0,-1" + } + solid + { + "id" "1683" + side + { + "id" "955" + "plane" "(112 256 32) (128 256 32) (128 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 288] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "954" + "plane" "(112 240 -1.03315e-006) (128 240 -1.03315e-006) (128 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 288] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "953" + "plane" "(112 256 32) (112 240 32) (112 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "952" + "plane" "(128 256 -1.03315e-006) (128 240 -1.03315e-006) (128 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "951" + "plane" "(128 256 32) (112 256 32) (112 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 288] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "950" + "plane" "(128 240 -1.03315e-006) (112 240 -1.03315e-006) (112 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 288] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1750" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "3" + "radius" "128" + "origin" "144 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1754" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "144 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,3,0,-1" + } + solid + { + "id" "1755" + side + { + "id" "967" + "plane" "(136 256 32) (152 256 32) (152 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 192] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "966" + "plane" "(136 240 -1.03315e-006) (152 240 -1.03315e-006) (152 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 192] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "965" + "plane" "(136 256 32) (136 240 32) (136 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "964" + "plane" "(152 256 -1.03315e-006) (152 240 -1.03315e-006) (152 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "963" + "plane" "(152 256 32) (136 256 32) (136 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 192] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "962" + "plane" "(152 240 -1.03315e-006) (136 240 -1.03315e-006) (136 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 192] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1813" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "168 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,4,0,-1" + } + solid + { + "id" "1814" + side + { + "id" "979" + "plane" "(160 256 32) (176 256 32) (176 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "978" + "plane" "(160 240 -1.03315e-006) (176 240 -1.03315e-006) (176 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "977" + "plane" "(160 256 32) (160 240 32) (160 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "976" + "plane" "(176 256 -1.03315e-006) (176 240 -1.03315e-006) (176 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "975" + "plane" "(176 256 32) (160 256 32) (160 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "974" + "plane" "(176 240 -1.03315e-006) (160 240 -1.03315e-006) (160 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1818" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "4" + "radius" "128" + "origin" "168 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1840" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "192 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,5,0,-1" + } + solid + { + "id" "1841" + side + { + "id" "991" + "plane" "(184 256 32) (200 256 32) (200 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "990" + "plane" "(184 240 -1.03315e-006) (200 240 -1.03315e-006) (200 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "989" + "plane" "(184 256 32) (184 240 32) (184 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "988" + "plane" "(200 256 -1.03315e-006) (200 240 -1.03315e-006) (200 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "987" + "plane" "(200 256 32) (184 256 32) (184 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "986" + "plane" "(200 240 -1.03315e-006) (184 240 -1.03315e-006) (184 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1845" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "5" + "radius" "128" + "origin" "192 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1867" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "216 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,6,0,-1" + } + solid + { + "id" "1868" + side + { + "id" "1003" + "plane" "(208 256 32) (224 256 32) (224 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -96] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1002" + "plane" "(208 240 -1.03315e-006) (224 240 -1.03315e-006) (224 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -96] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1001" + "plane" "(208 256 32) (208 240 32) (208 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1000" + "plane" "(224 256 -1.03315e-006) (224 240 -1.03315e-006) (224 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "999" + "plane" "(224 256 32) (208 256 32) (208 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "998" + "plane" "(224 240 -1.03315e-006) (208 240 -1.03315e-006) (208 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1872" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "6" + "radius" "128" + "origin" "216 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1894" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "240 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,7,0,-1" + } + solid + { + "id" "1895" + side + { + "id" "1015" + "plane" "(232 256 32) (248 256 32) (248 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -192] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1014" + "plane" "(232 240 -1.03315e-006) (248 240 -1.03315e-006) (248 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -192] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1013" + "plane" "(232 256 32) (232 240 32) (232 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1012" + "plane" "(248 256 -1.03315e-006) (248 240 -1.03315e-006) (248 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1011" + "plane" "(248 256 32) (232 256 32) (232 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -192] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1010" + "plane" "(248 240 -1.03315e-006) (232 240 -1.03315e-006) (232 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -192] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1899" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "7" + "radius" "128" + "origin" "240 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1921" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "264 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,8,0,-1" + } + solid + { + "id" "1922" + side + { + "id" "1027" + "plane" "(256 256 32) (272 256 32) (272 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -288] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1026" + "plane" "(256 240 -1.03315e-006) (272 240 -1.03315e-006) (272 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -288] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1025" + "plane" "(256 256 32) (256 240 32) (256 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1024" + "plane" "(272 256 -1.03315e-006) (272 240 -1.03315e-006) (272 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1023" + "plane" "(272 256 32) (256 256 32) (256 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -288] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1022" + "plane" "(272 240 -1.03315e-006) (256 240 -1.03315e-006) (256 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -288] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1926" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "8" + "radius" "128" + "origin" "264 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1948" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "288 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,9,0,-1" + } + solid + { + "id" "1949" + side + { + "id" "1039" + "plane" "(280 256 32) (296 256 32) (296 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -384] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1038" + "plane" "(280 240 -1.03315e-006) (296 240 -1.03315e-006) (296 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -384] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1037" + "plane" "(280 256 32) (280 240 32) (280 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1036" + "plane" "(296 256 -1.03315e-006) (296 240 -1.03315e-006) (296 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1035" + "plane" "(296 256 32) (280 256 32) (280 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -384] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1034" + "plane" "(296 240 -1.03315e-006) (280 240 -1.03315e-006) (280 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -384] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1953" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "9" + "radius" "128" + "origin" "288 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "1975" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "312 248 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "case_numbers,InValue,10,0,-1" + } + solid + { + "id" "1976" + side + { + "id" "1051" + "plane" "(304 256 32) (320 256 32) (320 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 32] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1050" + "plane" "(304 240 -1.03315e-006) (320 240 -1.03315e-006) (320 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 32] 0.25" + "vaxis" "[0 -1 0 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1049" + "plane" "(304 256 32) (304 240 32) (304 240 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1048" + "plane" "(320 256 -1.03315e-006) (320 240 -1.03315e-006) (320 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 0] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1047" + "plane" "(320 256 32) (304 256 32) (304 256 -1.03315e-006)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 32] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1046" + "plane" "(320 240 -1.03315e-006) (304 240 -1.03315e-006) (304 240 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 32] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "1980" + "classname" "point_message" + "angles" "0 0 0" + "developeronly" "0" + "message" "10" + "radius" "128" + "origin" "312 248 36" + editor + { + "color" "200 200 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 3000]" + } +} +entity +{ + "id" "2996" + "classname" "func_detail" + solid + { + "id" "2980" + side + { + "id" "1083" + "plane" "(132 302 28) (108 302 16) (108 302 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -96] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1082" + "plane" "(108 304 16) (132 304 28) (132 304 36)" + "material" "TOOLS/TOOLSNODRAW" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 0 -1 -32] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1081" + "plane" "(132 304 28) (132 302 28) (132 302 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "2" + "smoothing_groups" "0" + } + side + { + "id" "1080" + "plane" "(108 302 16) (108 304 16) (108 304 24)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 0 1 96] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1079" + "plane" "(108 302 24) (108 304 24) (132 304 36)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + side + { + "id" "1078" + "plane" "(108 304 16) (108 302 16) (132 302 28)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[-1 0 0 48] 0.25" + "vaxis" "[0 1 0 -300] 0.25" + "rotation" "0" + "lightmapscale" "4" + "smoothing_groups" "0" + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "0 180 0" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 4500]" + } +} +entity +{ + "id" "3623" + "classname" "comp_case" + "ctrl_type" "0" + "ctrl_value" "1" + "mode" "string" + "multiplecasesallowed" "0" + "targetname" "case_random" + connections + { + "OnCase01" "!self,Color,255 0 0,0,-1" + "OnCase02" "!self,Color,0 255 0,0,-1" + "OnCase03" "!self,Color,0 0 255,0,-1" + "OnCase06" "!self,Color,255 255 0,0,-1" + "OnCase05" "!self,Color,255 0 255,0,-1" + "OnCase04" "!self,Color,0 255 255,0,-1" + "OnCase07" "!self,Color,255 255 255,0,-1" + "OnCase08" "!self,Color,16 16 16,0,-1" + "OnCase10" "!self,Color,255 128 64,0,-1" + "OnCase09" "!self,Color,64 128 255,0,-1" + "OnCase11" "!self,Color,128 255 64,0,-1" + "OnCase12" "!self,Color,64 255 128,0,-1" + } + "origin" "207.635 86.2613 8" + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "comments" "Each sprite fires PickRandom, so they get random colours added for the OnUserX outputs. The choice is consistent across compiles though." + "logicalpos" "[0 10000]" + } +} +entity +{ + "id" "3625" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_1" + connections + { + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "96 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3696" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_2" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "128 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3701" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_3" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "160 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3706" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_4" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "192 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3711" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_5" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "224 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3716" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_6" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "256 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3721" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_7" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "288 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3726" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_8" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "320 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3731" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_9" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "352 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3736" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_10" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "96 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3741" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_11" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "128 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3746" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_12" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "160 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3751" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_13" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "192 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3756" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_14" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "224 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3761" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_15" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "256 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3766" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_16" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "288 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3771" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_17" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "320 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "3781" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "168 56 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "spr_rand_*,FireUser1,,0,-1" + } + solid + { + "id" "3782" + side + { + "id" "1095" + "plane" "(160 64 32) (176 64 32) (176 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1094" + "plane" "(160 48 -7.94729e-008) (176 48 -7.94729e-008) (176 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1093" + "plane" "(160 64 32) (160 48 32) (160 48 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1092" + "plane" "(176 64 -7.94729e-008) (176 48 -7.94729e-008) (176 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1091" + "plane" "(176 64 32) (160 64 32) (160 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1090" + "plane" "(176 48 -7.94729e-008) (160 48 -7.94729e-008) (160 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 96] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "3791" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "200 56 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "spr_rand_*,FireUser2,,0,-1" + } + solid + { + "id" "3792" + side + { + "id" "1107" + "plane" "(192 64 32) (208 64 32) (208 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -32] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1106" + "plane" "(192 48 -7.94729e-008) (208 48 -7.94729e-008) (208 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -32] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1105" + "plane" "(192 64 32) (192 48 32) (192 48 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1104" + "plane" "(208 64 -7.94729e-008) (208 48 -7.94729e-008) (208 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1103" + "plane" "(208 64 32) (192 64 32) (192 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -32] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1102" + "plane" "(208 48 -7.94729e-008) (192 48 -7.94729e-008) (192 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 -32] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "3806" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "232 56 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "spr_rand_*,FireUser3,,0,-1" + } + solid + { + "id" "3807" + side + { + "id" "1119" + "plane" "(224 64 32) (240 64 32) (240 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 352] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1118" + "plane" "(224 48 -7.94729e-008) (240 48 -7.94729e-008) (240 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 352] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1117" + "plane" "(224 64 32) (224 48 32) (224 48 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1116" + "plane" "(240 64 -7.94729e-008) (240 48 -7.94729e-008) (240 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1115" + "plane" "(240 64 32) (224 64 32) (224 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 352] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1114" + "plane" "(240 48 -7.94729e-008) (224 48 -7.94729e-008) (224 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 352] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "3841" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "264 56 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "spr_rand_*,FireUser4,,0,-1" + } + solid + { + "id" "3842" + side + { + "id" "1131" + "plane" "(256 64 32) (272 64 32) (272 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 224] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1130" + "plane" "(256 48 -7.94729e-008) (272 48 -7.94729e-008) (272 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 224] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1129" + "plane" "(256 64 32) (256 48 32) (256 48 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1128" + "plane" "(272 64 -7.94729e-008) (272 48 -7.94729e-008) (272 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1127" + "plane" "(272 64 32) (256 64 32) (256 64 -7.94729e-008)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 224] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1126" + "plane" "(272 48 -7.94729e-008) (256 48 -7.94729e-008) (256 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 224] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "4750" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_18" + connections + { + "OnUser1" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random,PickRandom,,0,-1" + "OnUser3" "case_random,PickRandom,,0,-1" + "OnUser4" "case_random,PickRandom,,0,-1" + } + "origin" "352 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +cameras +{ + "activecamera" "-1" +} +cordon +{ + "mins" "(-1024 -1024 -1024)" + "maxs" "(1024 1024 1024)" + "active" "0" +} From d5bccae10081b10a2364d0aeceb72270e1cc6ef7 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 12:55:28 +1000 Subject: [PATCH 144/243] Tweak to show how activating the same case produces the same result --- examples/hadd_comp_case.vmf | 302 +++++++++++------------------------- 1 file changed, 94 insertions(+), 208 deletions(-) diff --git a/examples/hadd_comp_case.vmf b/examples/hadd_comp_case.vmf index 1f047975b..ef88dd51d 100644 --- a/examples/hadd_comp_case.vmf +++ b/examples/hadd_comp_case.vmf @@ -2,7 +2,7 @@ versioninfo { "editorversion" "400" "editorbuild" "9520" - "mapversion" "51" + "mapversion" "52" "formatversion" "100" "prefab" "0" } @@ -20,7 +20,7 @@ viewsettings world { "id" "1" - "mapversion" "51" + "mapversion" "52" "classname" "worldspawn" "detailmaterial" "detail/detailsprites" "detailvbsp" "detail.vbsp" @@ -938,26 +938,6 @@ world "visgroupautoshown" "1" } } - group - { - "id" "1339" - editor - { - "color" "220 220 220" - "visgroupshown" "1" - "visgroupautoshown" "1" - } - } - group - { - "id" "1348" - editor - { - "color" "220 220 220" - "visgroupshown" "1" - "visgroupautoshown" "1" - } - } } entity { @@ -5573,7 +5553,7 @@ entity "ctrl_value" "1" "mode" "string" "multiplecasesallowed" "0" - "targetname" "case_random" + "targetname" "case_random_a" connections { "OnCase01" "!self,Color,255 0 0,0,-1" @@ -5618,10 +5598,9 @@ entity "targetname" "spr_rand_1" connections { - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "96 16 48" editor @@ -5651,10 +5630,9 @@ entity "targetname" "spr_rand_2" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "128 16 48" editor @@ -5684,10 +5662,9 @@ entity "targetname" "spr_rand_3" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "160 16 48" editor @@ -5717,10 +5694,9 @@ entity "targetname" "spr_rand_4" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "192 16 48" editor @@ -5750,10 +5726,9 @@ entity "targetname" "spr_rand_5" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "224 16 48" editor @@ -5783,10 +5758,9 @@ entity "targetname" "spr_rand_6" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "256 16 48" editor @@ -5816,10 +5790,9 @@ entity "targetname" "spr_rand_7" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "288 16 48" editor @@ -5849,10 +5822,9 @@ entity "targetname" "spr_rand_8" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "320 16 48" editor @@ -5882,10 +5854,9 @@ entity "targetname" "spr_rand_9" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "352 16 48" editor @@ -5915,10 +5886,9 @@ entity "targetname" "spr_rand_10" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "96 16 80" editor @@ -5948,10 +5918,9 @@ entity "targetname" "spr_rand_11" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "128 16 80" editor @@ -5981,10 +5950,9 @@ entity "targetname" "spr_rand_12" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "160 16 80" editor @@ -6014,10 +5982,9 @@ entity "targetname" "spr_rand_13" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "192 16 80" editor @@ -6047,10 +6014,9 @@ entity "targetname" "spr_rand_14" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "224 16 80" editor @@ -6080,10 +6046,9 @@ entity "targetname" "spr_rand_15" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "256 16 80" editor @@ -6113,10 +6078,9 @@ entity "targetname" "spr_rand_16" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "288 16 80" editor @@ -6146,10 +6110,9 @@ entity "targetname" "spr_rand_17" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "320 16 80" editor @@ -6161,116 +6124,6 @@ entity } } entity -{ - "id" "3781" - "classname" "func_button" - "_minlight" "0" - "angles" "0 0 0" - "disablereceiveshadows" "0" - "health" "0" - "lip" "20" - "locked_sound" "0" - "movedir" "90 0 0" - "origin" "168 56 16" - "renderamt" "255" - "rendercolor" "255 255 255" - "renderfx" "0" - "rendermode" "0" - "sounds" "0" - "spawnflags" "1024" - "speed" "20" - "unlocked_sound" "0" - "vrad_brush_cast_shadows" "1" - "wait" "0.5" - connections - { - "OnPressed" "spr_rand_*,FireUser1,,0,-1" - } - solid - { - "id" "3782" - side - { - "id" "1095" - "plane" "(160 64 32) (176 64 32) (176 48 32)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 96] 0.25" - "vaxis" "[0 -1 0 -256] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - side - { - "id" "1094" - "plane" "(160 48 -7.94729e-008) (176 48 -7.94729e-008) (176 64 -7.94729e-008)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 96] 0.25" - "vaxis" "[0 -1 0 -256] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - side - { - "id" "1093" - "plane" "(160 64 32) (160 48 32) (160 48 -7.94729e-008)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[0 1 0 256] 0.25" - "vaxis" "[0 0 -1 0] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - side - { - "id" "1092" - "plane" "(176 64 -7.94729e-008) (176 48 -7.94729e-008) (176 48 32)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[0 1 0 256] 0.25" - "vaxis" "[0 0 -1 0] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - side - { - "id" "1091" - "plane" "(176 64 32) (160 64 32) (160 64 -7.94729e-008)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 96] 0.25" - "vaxis" "[0 0 -1 0] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - side - { - "id" "1090" - "plane" "(176 48 -7.94729e-008) (160 48 -7.94729e-008) (160 48 32)" - "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 96] 0.25" - "vaxis" "[0 0 -1 0] 0.25" - "rotation" "0" - "lightmapscale" "16" - "smoothing_groups" "0" - } - editor - { - "color" "220 30 220" - "visgroupshown" "1" - "visgroupautoshown" "1" - } - } - editor - { - "color" "220 30 220" - "visgroupshown" "1" - "visgroupautoshown" "1" - "logicalpos" "[0 2500]" - } -} -entity { "id" "3791" "classname" "func_button" @@ -6294,7 +6147,7 @@ entity "wait" "0.5" connections { - "OnPressed" "spr_rand_*,FireUser2,,0,-1" + "OnPressed" "spr_rand_*,FireUser1,,0,-1" } solid { @@ -6404,7 +6257,7 @@ entity "wait" "0.5" connections { - "OnPressed" "spr_rand_*,FireUser3,,0,-1" + "OnPressed" "spr_rand_*,FireUser2,,0,-1" } solid { @@ -6514,7 +6367,7 @@ entity "wait" "0.5" connections { - "OnPressed" "spr_rand_*,FireUser4,,0,-1" + "OnPressed" "spr_rand_*,FireUser3,,0,-1" } solid { @@ -6619,10 +6472,9 @@ entity "targetname" "spr_rand_18" connections { - "OnUser1" "case_random,PickRandom,,0,-1" - "OnUser2" "case_random,PickRandom,,0,-1" - "OnUser3" "case_random,PickRandom,,0,-1" - "OnUser4" "case_random,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "352 16 80" editor @@ -6633,6 +6485,40 @@ entity "logicalpos" "[0 1500]" } } +entity +{ + "id" "5497" + "classname" "comp_case" + "ctrl_type" "0" + "ctrl_value" "1" + "mode" "string" + "multiplecasesallowed" "0" + "targetname" "case_random_b" + connections + { + "OnCase01" "!self,Color,255 0 0,0,-1" + "OnCase02" "!self,Color,0 255 0,0,-1" + "OnCase03" "!self,Color,0 0 255,0,-1" + "OnCase06" "!self,Color,255 255 0,0,-1" + "OnCase05" "!self,Color,255 0 255,0,-1" + "OnCase04" "!self,Color,0 255 255,0,-1" + "OnCase07" "!self,Color,255 255 255,0,-1" + "OnCase08" "!self,Color,16 16 16,0,-1" + "OnCase10" "!self,Color,255 128 64,0,-1" + "OnCase09" "!self,Color,64 128 255,0,-1" + "OnCase11" "!self,Color,128 255 64,0,-1" + "OnCase12" "!self,Color,64 255 128,0,-1" + } + "origin" "239.635 86.2613 8" + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "comments" "Each sprite fires PickRandom, so they get random colours added for the OnUserX outputs. The choice is consistent across compiles though." + "logicalpos" "[0 10000]" + } +} cameras { "activecamera" "-1" From 3a3e4ca078432ee17f92326f9f97759f5accd147 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 13:10:04 +1000 Subject: [PATCH 145/243] Add icon for comp_case --- hammer/materials/editor/comp_case.vmt | 8 ++++++++ hammer/materials/editor/comp_case.vtf | Bin 0 -> 22052 bytes materialsrc/editor/comp_case.pdn | Bin 0 -> 7958 bytes 3 files changed, 8 insertions(+) create mode 100644 hammer/materials/editor/comp_case.vmt create mode 100644 hammer/materials/editor/comp_case.vtf create mode 100644 materialsrc/editor/comp_case.pdn diff --git a/hammer/materials/editor/comp_case.vmt b/hammer/materials/editor/comp_case.vmt new file mode 100644 index 000000000..6e91679f5 --- /dev/null +++ b/hammer/materials/editor/comp_case.vmt @@ -0,0 +1,8 @@ +Sprite +{ + $baseTexture "editor/comp_case" + $spriteorientation "vp_parallel" + $spriteorigin "[ 0.50 0.50 ]" + $spriterendermode 2 + $no_fullbright 1 +} diff --git a/hammer/materials/editor/comp_case.vtf b/hammer/materials/editor/comp_case.vtf new file mode 100644 index 0000000000000000000000000000000000000000..92ab9b457d4f56106d3753d4668ae23dbf964c31 GIT binary patch literal 22052 zcmeHNU2Ig>5guqL+bSr2NTa3+kttD$zvo?%wsT zf8y*xvvbb;eKT{;y?fT8rg|OB1%TN|RY(s0D=P<>g>xDmJAb|M$CZ@&vLXYG4)j&{xlRR7_B|9S^{-M7;Po2K-9%0_ObElzU&#|lBJ?~#Wz8zMd+XF4__1`{r@~v;azN~85iiK+y z_QDIWq_eYg&C2!LmMz_U$a@=-s%H?hL`HwdV70o zrG5}chWOfSU|=9g@z=d`{Np7X_J2U}yWWNuPwarsOZ!1}H$&~=8rXj5XG{NbZcjPo z_VmGR@a*vhXgXR8doLY;w)T41b#^yw$Mq6{UH0;(mxo{>;2vZg4r+)hkmM*^RkJF! z@!-ZLcnxktIg)4NT)uocwRrL3y=!WAsjBAgPhMT$*$=OGjG(>>_at@X$dM+Tm&fX2 zB&&eup$T4v)XkeWo9^GgKl0$ggOO|3uHD|g?|lE>18slm>FH6Ms}HAIT3XcO$B#$Q zj`OX6%`gHlLJH%QApz==Iwmbh_QJL#JijpsWk00+Xqa`BqfGmf^7{4bsfhWLdD_6e z$tV9b|8e9WiZRpj-@N-=>Xo$}AHjFpKgUAl`3cp{i$o%!NNEu%JP~H{Lw#8?g@X89aRIEf~e;%&K2I&-C4Qm)7*S z->vcpzOP(OUQDvH_<_bIeAGK+h=L_sNqdE^#8lfB5jB+uhwgjBE9+O+9XF zYpb5qnhEB|wcNKOcpZmg3b3S+g98f?m+(bT2-o*hlnc(29OIda2KSkY`atj7C1KGq zl*N>9m*&U*$dhnH<0WA}aw36Eb^_nQr9zU!!S{r#X#t)iqL*qYMp76#I9FmFp3<4G zw_}r`zz_S4trSj8a!{7fP-yJf90b1Q9FP0_+n}s6AD{YJ%=2%>vIz;FsmSNw2A3w2 z{hxsIpN3}&1iphhW#|h(|3*M_Dez4duN6*Aa!^*3A@ED*zqs%JNc$uEUpoKRmMN5) zUQeBK}7{)48`2GvYGAhNyxS6sO=diLzu{-5;q{eyYL z`%kn4#;nrrF-5rlj785qbI?Q?W)uGYH}HzROb&Rz&s@pzH&@ykfgJ&^{e&I;meaoj znH%=Pb!_TmhH@a8%tfpfd$J#{wXLvm|Na}$gu1|<{Y=jJn_S{kJb_0smCc@Pq#W%W zq+G*61C@oiHvy)Zi{a~tHBJj24n|M~M=1sPqIH0N!+G=+a-uKK&~?{-SnJil(Xao@ z(Sva9dn+{pTReWe(YLWqaZt zMqiL4`+4#=(&W-t&LuX>y%^y$!8*x3yA6L0^R@n+gn4BmSG-($i7Dsa_j|YpE<~@L zV%h4K7`f&$_gwQ_vGUjR{f2vxU+sifX#M%;Tx<{S!IsEpUS?2e{kisB>LqTm=i|k( z)gLjI_YRzb`+qTeXRV)v2*PB+^J+Klz-*t+$L_wQGl$H>lVn$DmX@kKi1jfP2C^pHJ!i-^it)Wybn3YKGbgZ_4lg1r;rvt@WcmOYMQTXESy8 z{~(b9Cbs(X3CDek!t;qcS+<4NKUsNB6i%V_N7;9_nW!2HiEFE$^Cg~TlCy1^7h&h+;|E2qX9^Vr)_x?YO&m58RIn9Zb&uK27 zDUuTfGW`(xA3~1P2_F(V<2>8_|0sIJd!X3{(Q0T|Jmw`qT@3Xg-3~3vMF^SY6qg6xqNN1VdsiPzENzl zVMNtwoX`FUE)nBqTC+_qy&uPxLmD2(;@G)zV7)E)IAo!^DYndm=gjle+tLZer+AVW zhdJxyTFh?9Xe~6E|ci zD%uD=gBUgMHRmmTIbX@8K94z?rsYJk=`!S*;GDAckZooPm$lB6no4z)Ixu58@PBx$ B2#){& literal 0 HcmV?d00001 diff --git a/materialsrc/editor/comp_case.pdn b/materialsrc/editor/comp_case.pdn new file mode 100644 index 0000000000000000000000000000000000000000..ad3ac991419ee2882d3cd21aab371142037a5fb6 GIT binary patch literal 7958 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 Date: Wed, 28 Jun 2023 13:15:26 +1000 Subject: [PATCH 146/243] Add new entities to the docs --- CHANGELOG.md | 1 + README.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b01f1ed69..2eb0dc61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Version (dev) * #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. +* Added `comp_case`, a version of `logic_case` that is collapsed into callers like `comp_relay`. * Added `--verbose` parameter, for showing DEBUG messages. * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Added ability to specify alt skins when using `comp_prop_cable_dynamic`. diff --git a/README.md b/README.md index 48ed65ecc..57c0774ac 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,9 @@ Below are short explanations, see the "Help" display on the entity properties in | Entity | Description | |----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `comp_adv_output` | Adds a single output to another entity, while allowing instance renaming or `$fixups` to apply to only certain parts. | | `comp_flicker` | Fires on/off and skin inputs repeatedly to simulate a flicker-on effect. | +| `comp_case` | Version of `logic_case` which is optimised away by the compiler. | | `comp_choreo_sceneset` | Chains a set of choreographed scenes together. | | `comp_entity_finder` | Finds the closest entity of a given type, then applies various transformations. Outputs from this entity will be moved to the found entity. | | `comp_entity_mover` | Shift an entity by a given amount. This is useful to place entities into the void, for example. | From 768383aea6a4b4f01c06a9425202595c034812c9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 14:10:13 +1000 Subject: [PATCH 147/243] Tweak tag expansion rules to allow mods to include since/until, but exclude the game itself Useful for mods based on CSGO but not actually using its game code. --- src/hammeraddons/unify_fgd.py | 50 +++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index 4e282c719..5ff116303 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -82,10 +82,10 @@ 'EP2': {'HL2', 'EP1'}, 'MBASE': {'VSCRIPT'}, - 'MESA': {'INST_IO'}, + 'MESA': {'HL2', 'INST_IO'}, 'GMOD': {'HL2', 'EP1', 'EP2'}, - 'EZ1': {'MBASE', 'VSCRIPT'}, - 'EZ2': {'MBASE', 'VSCRIPT'}, + 'EZ1': {'HL2', 'EP1', 'EP2', 'MBASE', 'VSCRIPT'}, + 'EZ2': {'HL2', 'EP1', 'EP2', 'MBASE', 'VSCRIPT'}, 'KZ': {'HL2'}, 'L4D2': {'INST_IO', 'VSCRIPT'}, @@ -248,30 +248,40 @@ def expand_tags(tags: FrozenSet[str]) -> FrozenSet[str]: This adds since_/until_ tags, and values in FEATURES. """ exp_tags = set(tags) + + # Figure out the game branch, for adding since/until tags. + # For games, pick the most recent one. For mods, pick the associated branch, + # but don't add the branch itself - they can do that via FEATURES. + pos = -1 for tag in tags: - try: - exp_tags.add(MOD_TO_BRANCH[tag.upper()]) - except KeyError: - pass + tag = tag.upper() + if tag in ALL_GAMES: + pos = max(pos, GAME_ORDER.index(tag)) + break + else: + try: + pos = GAME_ORDER.index(MOD_TO_BRANCH[tag]) + except (KeyError, ValueError): + pass + else: + break + + if pos > 0: + exp_tags.update( + 'SINCE_' + tag + for tag in GAME_ORDER[:pos + 1] + ) + exp_tags.update( + 'UNTIL_' + tag + for tag in GAME_ORDER[pos + 1:] + ) for tag in list(exp_tags): try: exp_tags.update(FEATURES[tag.upper()]) except KeyError: pass - try: - pos = GAME_ORDER.index(tag.upper()) - except ValueError: - pass - else: - exp_tags.update( - 'SINCE_' + tag - for tag in GAME_ORDER[:pos+1] - ) - exp_tags.update( - 'UNTIL_' + tag - for tag in GAME_ORDER[pos+1:] - ) + return frozenset(exp_tags) From 548440403410f4b740465b271eeb4db059dec819 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 28 Jun 2023 16:11:32 +1000 Subject: [PATCH 148/243] Add ability to change the map grid size --- comp_adv_output.png | Bin 337 -> 0 bytes src/hammeraddons/unify_fgd.py | 27 ++++++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) delete mode 100644 comp_adv_output.png diff --git a/comp_adv_output.png b/comp_adv_output.png deleted file mode 100644 index 716a5b612db635be403719dd6f674a982db19a56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337 zcmV-X0j~auP)lXK_;(I-P~&wNs-k+tMX2 z9M2I~Q;RyUrS!CxGD?Eq!?>*Ymb=@Tg2H_;>_-36(^5mm+ zFByxI6|emaE>&^I8hLC^09>{u(=D6UHHOl?Br{(t{tjj>C+&A<-!yIlfc3MUMR$mG z7m}B=OhiV`yVhI str: return '{}/{}.fgd'.format(folder, ent.classname) -def load_database(dbase: Path, extra_loc: Optional[Path]=None, fgd_vis: bool=False) -> Tuple[FGD, EntityDef]: +def load_database( + dbase: Path, + extra_loc: Optional[Path]=None, + fgd_vis: bool=False, + map_size: int=MAP_SIZE_DEFAULT, +) -> Tuple[FGD, EntityDef]: """Load the entire database from disk. This returns the FGD, plus the CBaseEntity definition.""" print(f'Loading database {dbase}:') fgd = FGD() - fgd.map_size_min = -16384 - fgd.map_size_max = 16384 + fgd.map_size_min = -map_size + fgd.map_size_max = map_size # Classname -> filename ent_source: Dict[str, str] = {} @@ -818,6 +825,7 @@ def action_export( output_path: Path, as_binary: bool, engine_mode: bool, + map_size: int=MAP_SIZE_DEFAULT, ) -> None: """Create an FGD file using the given tags.""" @@ -828,7 +836,9 @@ def action_export( print('Tags expanded to: {}'.format(', '.join(tags))) - fgd, base_entity_def = load_database(dbase, extra_db) + fgd, base_entity_def = load_database(dbase, extra_loc=extra_db, map_size=map_size) + + print(f'Map size: ({fgd.map_size_min}, {fgd.map_size_max})') if engine_mode: # In engine mode, we don't care about specific games. @@ -1225,6 +1235,12 @@ def main(args: Optional[List[str]]=None): help="Tags to include in the output.", default=None, ) + parser_exp.add_argument( + "--map-size", + default=MAP_SIZE_DEFAULT, + dest="map_size", + type=int, + ) parser_imp = subparsers.add_parser( "import", @@ -1300,6 +1316,7 @@ def main(args: Optional[List[str]]=None): result.output, result.binary, result.engine, + result.map_size, ) elif result.mode in ("c", "count"): action_count(dbase, extra_db, factories_folder=Path(repo_dir, 'db', 'factories')) From 7472a4a7357f95c9ffc9e9473196444f28929db8 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 1 Jul 2023 17:03:31 -0700 Subject: [PATCH 149/243] Expand this transform to work on other entities too --- fgd/bases/SRCModel.fgd | 7 +++ fgd/point/npc/npc_personality_core.fgd | 10 ++-- fgd/point/npc/npc_rocket_turret.fgd | 8 ++-- fgd/point/prop/prop_button.fgd | 7 +-- fgd/point/prop/prop_exploding_futbol.fgd | 7 ++- fgd/point/prop/prop_floor_ball_button.fgd | 7 +-- fgd/point/prop/prop_floor_cube_button.fgd | 9 ++-- fgd/point/prop/prop_glados_core.fgd | 11 +++-- fgd/point/prop/prop_glass_futbol.fgd | 9 +++- fgd/point/prop/prop_under_button.fgd | 8 +++- fgd/point/prop/prop_under_floor_button.fgd | 7 ++- fgd/point/prop/prop_wall_projector.fgd | 7 ++- transforms/custom_cube_models.py | 28 ------------ transforms/p2_custom_models.py | 53 ++++++++++++++++++++++ 14 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 fgd/bases/SRCModel.fgd delete mode 100644 transforms/custom_cube_models.py create mode 100644 transforms/p2_custom_models.py diff --git a/fgd/bases/SRCModel.fgd b/fgd/bases/SRCModel.fgd new file mode 100644 index 000000000..9a845c1e5 --- /dev/null +++ b/fgd/bases/SRCModel.fgd @@ -0,0 +1,7 @@ +@BaseClass appliesto(srctools) += SRCModel: "Adds keyvalues to set a custom model." +[ + comp_custom_model_type[srctools, -engine](boolean) : "[COMP] Override Model" : 0 : "If enabled, automatically pack a VScript to override the entity's model on spawn."+ + " NOTE: If you run into problems with players getting stuck on the model, make sure it has the same collisions as the base model,"+ + " as this is known to sometimes cause issues." +] \ No newline at end of file diff --git a/fgd/point/npc/npc_personality_core.fgd b/fgd/point/npc/npc_personality_core.fgd index 7737cd967..440430e8b 100644 --- a/fgd/point/npc/npc_personality_core.fgd +++ b/fgd/point/npc/npc_personality_core.fgd @@ -1,4 +1,4 @@ -@PointClass base(TalkNPC) +@PointClass base(TalkNPC, SRCModel) appliesto(P2) autovis(Entities, NPCs, Aper. Personality Core) studioprop() @@ -20,8 +20,12 @@ 1: "Yes" ] - model[engine](string) : "Hammer Preview" : "models/npcs/personality_sphere/personality_sphere.mdl" - model(choices) : "[H] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in hammer. Set to the same as Use Alternate Skins." = + model[-srctools](choices) : "[H] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins." = + [ + "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" + "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" + ] + model[+srctools](choices) : "Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins. Alternatively, if Override Model is enabled you can type/paste in a custom model path here." = [ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" diff --git a/fgd/point/npc/npc_rocket_turret.fgd b/fgd/point/npc/npc_rocket_turret.fgd index 56ebfb18c..002641182 100644 --- a/fgd/point/npc/npc_rocket_turret.fgd +++ b/fgd/point/npc/npc_rocket_turret.fgd @@ -4,7 +4,7 @@ @BaseClass appliesto(P2) studioprop() line(0 0 255, targetname, TripwireAimTarget) = _npc_rocket_turret_p2 [] -@PointClass base(BaseNPC, ResponseContext, _npc_rocket_turret_p1, _npc_rocket_turret_p2) +@PointClass base(BaseNPC, ResponseContext, _npc_rocket_turret_p1, _npc_rocket_turret_p2, SRCModel) appliesto(P1, P2) autovis(Entities, NPCs, Portal Rocket Turret) sphere(_sphere_radius) @@ -17,13 +17,13 @@ 1: "Start Retracted" : 0 ] + model[P2, -srctools](studio) : "[H] Model" : "models/props_bts/rocket_sentry.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[P2, +srctools](studio) : "Custom Model" : "models/props_bts/rocket_sentry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + rocketspeed[P2](float) : "Rocket Speed" : 450 : "Speed the rocket will travel at." rocketlifetime[P2](float) : "Rocket Lifetime" : 20 : "The rocket will automatically detonate after this number of seconds." TripwireMode[P2](boolean) : "Tripwire Mode" : 0 : "Makes the turret aim in a specific direction instead of following the target. When the beam is crossed, a rocket instantly fires." TripwireAimTarget[P2](target_destination) : "Tripwire Aim Target" : : "In tripwire mode, the entity to aim at." - - model[engine](string) : "Hammer Preview" - model[P2](studio) : "[H] Model" : "models/props_bts/rocket_sentry.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." _sphere_radius[!engine](integer) readonly : "" : 8192 : "How far the turret will be able to see targets. Always 8192, but this keyvalue is needed to display the preview." diff --git a/fgd/point/prop/prop_button.fgd b/fgd/point/prop/prop_button.fgd index 72d817564..7fbba6fb0 100644 --- a/fgd/point/prop/prop_button.fgd +++ b/fgd/point/prop/prop_button.fgd @@ -1,18 +1,19 @@ @PointClass - base(BasePedButton) + base(BasePedButton, SRCModel) appliesto(P2) autovis(Test Elements, P2 Buttons, Pedestal) studioprop() = prop_button: "A button which is activated by player use or by game inputs. While pressed it can play a tick-tock sound to indicate limited time." [ + model[-srctools](studio) : "[H] Model" : "models/props/switch001.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props/switch001.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin[engine](integer) : "Skin" : 0 : "Should it appear dirty or clean?" skin(choices) : "Skin" : "0" : "Should it appear dirty or clean?" = [ 0: "Clean" 1: "Dirty" ] - model[engine](string) : "Hammer Preview" - model(studio) : "[H] Model" : "models/props/switch001.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." @resources [ diff --git a/fgd/point/prop/prop_exploding_futbol.fgd b/fgd/point/prop/prop_exploding_futbol.fgd index 7587d494b..f9b9e9fef 100644 --- a/fgd/point/prop/prop_exploding_futbol.fgd +++ b/fgd/point/prop/prop_exploding_futbol.fgd @@ -1,8 +1,11 @@ -@PointClass base(BaseEntityPhysics) +@PointClass base(BaseEntityPhysics, SRCModel) appliesto(P2) - studioprop("models/npcs/personality_sphere_angry.mdl") + studioprop() = prop_exploding_futbol: "The bombs used by Wheatley." [ + model[-srctools](studio) : "[H] Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + explodeontouch(boolean) : "Explode on touch" : 1 : "If the bomb should explode when it touches something." // Inputs diff --git a/fgd/point/prop/prop_floor_ball_button.fgd b/fgd/point/prop/prop_floor_ball_button.fgd index 63a872b1e..00b3e7193 100644 --- a/fgd/point/prop/prop_floor_ball_button.fgd +++ b/fgd/point/prop/prop_floor_ball_button.fgd @@ -1,10 +1,11 @@ -@PointClass base(BasePortButton) +@PointClass base(BasePortButton, SRCModel) appliesto(P2) autovis(Test Elements, P2 Buttons, Floor) studioprop() = prop_floor_ball_button: "A floor button which is only activated by a Sphere-type prop_weighted_cube." [ - model[engine](string) : "Hammer Preview" - model(studio) : "[H] Model" : "models/props/ball_button.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." + + model[-srctools](studio) : "[H] Model" : "models/props/ball_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props/ball_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." @resources [ model "models/props/ball_button.mdl" diff --git a/fgd/point/prop/prop_floor_cube_button.fgd b/fgd/point/prop/prop_floor_cube_button.fgd index 77c86ba9f..108a92e86 100644 --- a/fgd/point/prop/prop_floor_cube_button.fgd +++ b/fgd/point/prop/prop_floor_cube_button.fgd @@ -1,11 +1,12 @@ -@PointClass base(BasePortButton) +@PointClass base(BasePortButton, SRCModel) appliesto(P2) autovis(Test Elements, P2 Buttons, Floor) studioprop() = prop_floor_cube_button: "A floor button which is activated by a prop_weighted_cube." [ - model[engine](string) : "Hammer Preview" - model(studio) : "[H]] Model" : "models/props/box_socket.mdl" : "Model to display in Hammer. Use Init Code and a comp_precache_model to change the model in-game." - acceptsball(boolean) : "Accepts Balls" : 0 : "Do Edgeless Safety Cubes activate this? Should almost always be No unless no balls are in the map." + + model[-srctools](studio) : "[H] Model" : "models/props/box_socket.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props/box_socket.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + acceptsball(boolean) : "Accepts Balls" : 0 : "Do Edgeless Safety Cubes activate this? Should almost always be No." @resources [ diff --git a/fgd/point/prop/prop_glados_core.fgd b/fgd/point/prop/prop_glados_core.fgd index 6d10eff0b..88468c686 100644 --- a/fgd/point/prop/prop_glados_core.fgd +++ b/fgd/point/prop/prop_glados_core.fgd @@ -1,16 +1,19 @@ // Switch the model used depending on game. @BaseClass appliesto(P1) studioprop("models/props_bts/glados_ball_reference.mdl") = _prop_glados_core_p1_mdl [] -@BaseClass appliesto(P2) studioprop("models/npcs/personality_sphere/personality_sphere.mdl") +@BaseClass appliesto(P2) studioprop() = _prop_glados_core_p2_mdl [] -@PointClass base(BasePropPhysics, _prop_glados_core_p1_mdl, _prop_glados_core_p2_mdl) +@PointClass base(BasePropPhysics, _prop_glados_core_p1_mdl, _prop_glados_core_p2_mdl, SRCModel) appliesto(P1, P2) = prop_glados_core: "The P1 personality cores for GlaDOS. Resemble little eyeballs with handles. " + "These play lines and look around when near the player. " + "Portal 2 uses the wrong core model, so this will need to be swapped back with VScript." [ - coretype[engine](integer) : "Core Personality" : 1 + model[P2, -srctools](studio) : "[H] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[P2, +srctools](studio) : "Custom Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + + coretype[engine](integer) : "Core Personality" : 3 coretype(choices) : "Core Personality" : 3 : "Which personality the core is set to, determines the voice lines and skin." = [ // Ordered to match P1 boss fight @@ -20,7 +23,7 @@ 1: "Anger Core" ] - skin[engine](integer) : "Skin" : 0 + skin[engine](integer) : "Skin" : 3 skin(choices) : "[H] Skin" : 3 : "Skin to show in Hammer." = [ // Ordered to match P1 boss fight diff --git a/fgd/point/prop/prop_glass_futbol.fgd b/fgd/point/prop/prop_glass_futbol.fgd index 523d88bcd..10e70f095 100644 --- a/fgd/point/prop/prop_glass_futbol.fgd +++ b/fgd/point/prop/prop_glass_futbol.fgd @@ -1,10 +1,15 @@ -@PointClass base(BasePropPhysics) +@PointClass base(BasePropPhysics, SRCModel) appliesto(P2) - studioprop("models/props/futbol.mdl") + studioprop() line(255 255 0, targetname, spawnername) = prop_glass_futbol: "A fragile glass ball that the player can pick up and toss. On contact with surfaces it will shatter, and it can be put into holders to power them. It is affected by gel, but the shattering means this has little effect." [ + model[-srctools](studio) : "[H] Model" : "models/props/futbol.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + spawnername(target_destination) : "Spawner Name" : : "Name of prop_glass_futbol_spawner for this futbol to respawn in once broken." + + // Inputs input Dissolve(void) : "Dissolve" input RemovePaint(void) : "Remove Paint" diff --git a/fgd/point/prop/prop_under_button.fgd b/fgd/point/prop/prop_under_button.fgd index 81f39cce1..5e2aab489 100644 --- a/fgd/point/prop/prop_under_button.fgd +++ b/fgd/point/prop/prop_under_button.fgd @@ -1,11 +1,15 @@ -@PointClass base(BasePedButton) +@PointClass base(BasePedButton, SRCModel) appliesto(P2) autovis(Test Elements, P2 Buttons, Pedestal) - studioprop("models/props_underground/underground_testchamber_button.mdl") + studioprop() = prop_under_button: "A button which is activated by player use or by game inputs, for use in underground test chambers. " + "Uses different press/release sounds compared to the modern one. " + "The same tick-tock noise is used to indicate limited time." [ + + model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + @resources [ model "models/props_underground/underground_testchamber_button.mdl" diff --git a/fgd/point/prop/prop_under_floor_button.fgd b/fgd/point/prop/prop_under_floor_button.fgd index d86ef737b..f58a8f248 100644 --- a/fgd/point/prop/prop_under_floor_button.fgd +++ b/fgd/point/prop/prop_under_floor_button.fgd @@ -1,11 +1,14 @@ -@PointClass base(BasePortButton) +@PointClass base(BasePortButton, SRCModel) appliesto(P2) autovis(Test Elements, P2 Buttons, Floor) - studioprop("models/props_underground/underground_floor_button.mdl") + studioprop() = prop_under_floor_button: "A floor button which is activated by a player or objects, for use in the underground test chambers. " + "It plays different sounds, and has a larger trigger area." [ + model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + // Outputs output OnPressedBlue(void) : "Called in Coop when the button has been pressed by ATLAS." output OnPressedOrange(void) : "Called in Coop when the button has been pressed by P-Body." diff --git a/fgd/point/prop/prop_wall_projector.fgd b/fgd/point/prop/prop_wall_projector.fgd index d53ade335..b029cb69f 100644 --- a/fgd/point/prop/prop_wall_projector.fgd +++ b/fgd/point/prop/prop_wall_projector.fgd @@ -1,10 +1,13 @@ -@PointClass base(BaseProjector) +@PointClass base(BaseProjector, SRCModel) appliesto(P2) autovis(Test Elements, Light Bridge) - studioprop("models/props/wall_emitter.mdl") + studioprop() = prop_wall_projector: "Aperture Science Hard Light Bridge Projector. " + "Note: To avoid lag when it first turns on, place an info_particle_system to precache 'projected_wall_impact'." [ + model[-srctools](studio) : "[H] Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "Custom Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin[engine](integer) : "Skin" : 0 skin(choices) : "Skin" : 0 : "Which skin to use." = [ diff --git a/transforms/custom_cube_models.py b/transforms/custom_cube_models.py deleted file mode 100644 index 7bd823599..000000000 --- a/transforms/custom_cube_models.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Add a keyvalue to prop_weighted_cube to automatically handle custom models.""" - -from srctools import conv_int - -from hammeraddons.bsp_transform import trans, Context - -@trans('Easy Custom Cube Models') -def custom_cube_models(ctx: Context) -> None: - """Add a keyvalue to prop_weighted_cube to automatically handle custom models.""" - for ent in ctx.vmf.by_class['prop_weighted_cube']: - model_type = conv_int(ent['comp_custom_model_type']) - - if model_type == 0: # none - continue - elif model_type == 1: # script override - cube_model = ent['model'] - # Make a comp_precache_model - ctx.vmf.create_ent( - classname = 'comp_precache_model', - model = cube_model, - ) - ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cube_model + '") }') - elif model_type == 2: # cube type 6 - orig_cube_type = ent['CubeType'] - ent['CubeType'] = '6' - # Revert to the original type on spawn - ctx.add_code(ent, 'function OnPostSpawn() { EntFireByHandle(self, "AddOutput", "CubeType ' + orig_cube_type + '", 0, self, self) }') - \ No newline at end of file diff --git a/transforms/p2_custom_models.py b/transforms/p2_custom_models.py new file mode 100644 index 000000000..fdecb3d96 --- /dev/null +++ b/transforms/p2_custom_models.py @@ -0,0 +1,53 @@ +"""Add keyvalues to Portal 2 test element entities to automatically handle custom models.""" + +from srctools import conv_int + +from hammeraddons.bsp_transform import trans, Context + +SUPPORTED_ENTS = [ + "prop_weighted_cube", + "prop_button", + "prop_floor_cube_button", + "prop_floor_ball_button", + "prop_under_button", + "prop_under_floor_button", + "prop_wall_projector", + "prop_glados_core", + # TODO: needs extra VScript for the sprite, otherwise should work + # "npc_security_camera", + "npc_rocket_turret", + "npc_personality_core", + "prop_exploding_futbol", + "prop_glass_futbol", + + # Not supported: + # prop_testchamber_door - reverses animations + # prop_tractor_beam - breaks animations + # prop_linked_portal_door - breaks animations + # prop_monster_box - swaps models dynamically +] + +@trans('Portal 2 Custom Models') +def p2_custom_models(ctx: Context) -> None: + """Add keyvalues to Portal 2 test element entities to automatically handle custom models.""" + for ent in ctx.vmf.entities: + if ent['classname'] not in SUPPORTED_ENTS: + continue + model_type = conv_int(ent['comp_custom_model_type']) + + if model_type == 0: # none + continue + elif model_type == 1: # script override + cust_model = ent['model'] + # Make a comp_precache_model + ctx.vmf.create_ent( + classname = 'comp_precache_model', + model = cust_model, + ) + ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cust_model + '") }') + elif model_type == 2 and ent['classname'] == 'prop_weighted_cube': # cube type 6, for prop_weighted_cube only + orig_cube_type = ent['CubeType'] + ent['CubeType'] = '6' + # Revert to the original type on spawn + ctx.add_code(ent, 'function OnPostSpawn() { EntFireByHandle(self, "AddOutput", "CubeType ' + orig_cube_type + '", 0, self, self) }') + \ No newline at end of file From a65c4f12a88b3068c3d180973570650f3517f9c4 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 1 Jul 2023 17:59:34 -0700 Subject: [PATCH 150/243] Extend this keyvalue description for comp_adv_output --- fgd/point/comp/comp_adv_output.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/comp/comp_adv_output.fgd b/fgd/point/comp/comp_adv_output.fgd index 981886641..7f64d452f 100644 --- a/fgd/point/comp/comp_adv_output.fgd +++ b/fgd/point/comp/comp_adv_output.fgd @@ -16,7 +16,7 @@ inp_name(string) : "Input Name" : : "The input to fire." delay(float) : "Delay" : "0.0": "The delay to add to the output." - delay2(float) : "Extra Delay" : "0.0" : "A second delay to add to the first." + delay2(float) : "Extra Delay" : "0.0" : "A second delay to add to the first. Useful for instance parameters." times(integer) : "Times to Fire" : -1 : "The number of times this output can fire, or -1 for infinite. Hammer normally only allows setting this to 1 or -1." From 242d727871899aefa86038e71cce1d3c9ffdcfb1 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 1 Jul 2023 19:49:58 -0700 Subject: [PATCH 151/243] This is apparently more efficient --- transforms/p2_custom_models.py | 37 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/transforms/p2_custom_models.py b/transforms/p2_custom_models.py index fdecb3d96..1d88e803f 100644 --- a/transforms/p2_custom_models.py +++ b/transforms/p2_custom_models.py @@ -30,24 +30,23 @@ @trans('Portal 2 Custom Models') def p2_custom_models(ctx: Context) -> None: """Add keyvalues to Portal 2 test element entities to automatically handle custom models.""" - for ent in ctx.vmf.entities: - if ent['classname'] not in SUPPORTED_ENTS: - continue - model_type = conv_int(ent['comp_custom_model_type']) + for classname in SUPPORTED_ENTS: + for ent in ctx.vmf.by_class[classname]: + model_type = conv_int(ent['comp_custom_model_type']) - if model_type == 0: # none - continue - elif model_type == 1: # script override - cust_model = ent['model'] - # Make a comp_precache_model - ctx.vmf.create_ent( - classname = 'comp_precache_model', - model = cust_model, - ) - ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cust_model + '") }') - elif model_type == 2 and ent['classname'] == 'prop_weighted_cube': # cube type 6, for prop_weighted_cube only - orig_cube_type = ent['CubeType'] - ent['CubeType'] = '6' - # Revert to the original type on spawn - ctx.add_code(ent, 'function OnPostSpawn() { EntFireByHandle(self, "AddOutput", "CubeType ' + orig_cube_type + '", 0, self, self) }') + if model_type == 0: # none + continue + elif model_type == 1: # script override + cust_model = ent['model'] + # Make a comp_precache_model + ctx.vmf.create_ent( + classname = 'comp_precache_model', + model = cust_model, + ) + ctx.add_code(ent, 'function OnPostSpawn() { self.SetModel("' + cust_model + '") }') + elif model_type == 2 and ent['classname'] == 'prop_weighted_cube': # cube type 6, for prop_weighted_cube only + orig_cube_type = ent['CubeType'] + ent['CubeType'] = '6' + # Revert to the original type on spawn + ctx.add_code(ent, 'function OnPostSpawn() { EntFireByHandle(self, "AddOutput", "CubeType ' + orig_cube_type + '", 0, self, self) }') \ No newline at end of file From aad4877ebbac95edb309466302a967031c539677 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 4 Jul 2023 18:22:10 +1000 Subject: [PATCH 152/243] Add actual descriptions to material_modify_control parameters --- fgd/point/material_modify_control.fgd | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fgd/point/material_modify_control.fgd b/fgd/point/material_modify_control.fgd index eb5c1f71d..bf61ba4ab 100644 --- a/fgd/point/material_modify_control.fgd +++ b/fgd/point/material_modify_control.fgd @@ -4,11 +4,12 @@ iconsprite("editor/ficool2/material_modify_control.vmt") = material_modify_control: "An entity that can be used to directly control material vars. " + "To use it, you need to add the MaterialModify or MaterialModifyAnimated proxy to the material you intend to change. " + - "Parent this entity the entity who's material you want to control. " + - "If not parented, this applies to all materials in the map with the given name." + "Parent this entity the entity who's material you want to control." [ - materialname(string) : "Material to modify." - materialvar(string) : "Material variable to modify." + parentname(target_destination) : "Parent / Target" : : "Materials are modified only on the parent of the material_modify_control." + + materialname(material): "Material Name" : : "The single material to modify on the parent. 'materials/' and '.vmt' is not required. Maximum of 255 characters allowed." + materialvar(string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed." // Inputs input SetMaterialVar(string) : "Fire to modify a material variable. The argument is the value to set the variable to." From eb835850c5f280daa75ac2f8fc1d9efbf7c0d4c3 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 4 Jul 2023 18:38:08 +1000 Subject: [PATCH 153/243] Add transform to allow automatically adding $ to mat-mod-control variables --- fgd/point/material_modify_control.fgd | 6 ++++-- transforms/fgd_tweaks/mat_modify_control.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 transforms/fgd_tweaks/mat_modify_control.py diff --git a/fgd/point/material_modify_control.fgd b/fgd/point/material_modify_control.fgd index bf61ba4ab..2a5a1dd19 100644 --- a/fgd/point/material_modify_control.fgd +++ b/fgd/point/material_modify_control.fgd @@ -7,9 +7,11 @@ "Parent this entity the entity who's material you want to control." [ parentname(target_destination) : "Parent / Target" : : "Materials are modified only on the parent of the material_modify_control." - + materialname(material): "Material Name" : : "The single material to modify on the parent. 'materials/' and '.vmt' is not required. Maximum of 255 characters allowed." - materialvar(string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed." + materialvar[-srctools](string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed." + materialvar[+srctools](string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed. " + + "If the $ is omitted, the postcompiler will add it. This prevents the variable from being detected for instances." // Inputs input SetMaterialVar(string) : "Fire to modify a material variable. The argument is the value to set the variable to." diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py new file mode 100644 index 000000000..dbad6f325 --- /dev/null +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -0,0 +1,14 @@ +"""Tweak material_modify_control to avoid the variable showing up in instances.""" +from hammeraddons.bsp_transform import trans, Context + + +@trans('FGD - material_modify_control') +def material_modify_control(ctx: Context) -> None: + """Prepend $ to mat-mod-control variable keyvalues if required. + + This allows Hammer to not detect this as a fixup variable. + """ + for ent in ctx.vmf.by_class['material_modify_control']: + var_name = ent['materialvar'] + if var_name and not var_name.startswith(('$', '%')): + ent['materialvar'] = '$' + var_name From c9e4f8235ecf11afd9d4cd562d69cc6b926d6334 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 4 Jul 2023 19:02:31 +1000 Subject: [PATCH 154/243] New keyvalue for searching parents of material_modify_control --- fgd/point/material_modify_control.fgd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fgd/point/material_modify_control.fgd b/fgd/point/material_modify_control.fgd index 2a5a1dd19..93da48d7b 100644 --- a/fgd/point/material_modify_control.fgd +++ b/fgd/point/material_modify_control.fgd @@ -8,6 +8,9 @@ [ parentname(target_destination) : "Parent / Target" : : "Materials are modified only on the parent of the material_modify_control." + srctools_search_parent[+srctools](boolean) : "Search Parent" : : "If set, the postcompiler will duplicate this entity for each material on the parent that have the appropriate proxy. " + + "If Material Name is set, only materials containing that value will be considered." + materialname(material): "Material Name" : : "The single material to modify on the parent. 'materials/' and '.vmt' is not required. Maximum of 255 characters allowed." materialvar[-srctools](string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed." materialvar[+srctools](string): "Variable Name" : : "Material variable to modify, '$frame' for example. Maximum of 255 characters allowed. " + From 7872cc31fd12b4004861d83ec348bf834f46f5a7 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 4 Jul 2023 19:02:55 +1000 Subject: [PATCH 155/243] Function to check if a material has a proxy. --- transforms/fgd_tweaks/mat_modify_control.py | 44 +++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py index dbad6f325..91d625378 100644 --- a/transforms/fgd_tweaks/mat_modify_control.py +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -1,5 +1,11 @@ """Tweak material_modify_control to avoid the variable showing up in instances.""" +import srctools.logger from hammeraddons.bsp_transform import trans, Context +from srctools.tokenizer import TokenSyntaxError +from srctools.vmt import Material + + +LOGGER = srctools.logger.get_logger(__name__) @trans('FGD - material_modify_control') @@ -8,6 +14,44 @@ def material_modify_control(ctx: Context) -> None: This allows Hammer to not detect this as a fixup variable. """ + # Material name -> does it have either materialmodify or materialmodifyanimated proxies. + mat_has_proxy: dict[str, bool] = {} + # Model or "*xx" index -> list of relevant materials. + model_mats: dict[str, list[str]] = {} + filter_fsys = ctx.pack.fsys # Close over just filesystem. + + def filter_materials(mat_name: str) -> bool: + """Check if this material has the proxy.""" + lowered = mat_name.casefold() + if not lowered.startswith(('materials/', 'materials\\')): + mat_name = f'materials/{mat_name}' + lowered = f'materials/{lowered}' + if not lowered.endswith('.vmt'): + mat_name += '.vmt' + lowered += '.vmt' + try: + return mat_has_proxy[lowered] + except KeyError: + pass + try: + file = filter_fsys[mat_name] + except FileNotFoundError: + LOGGER.warning('Material "{}" does not exist!', mat_name) + mat_has_proxy[lowered] = False + return False + with file.open_str() as f: + try: + mat = Material.parse(f, mat_name) + mat.apply_patches(filter_fsys) + except (TokenSyntaxError, ValueError) as exc: + LOGGER.warning('Material "{}" is not valid: ', exc_info=exc) + for proxy in mat.proxies: + if proxy.name in ('materialmodify', 'materialmodifyanimated'): + mat_has_proxy[lowered] = True + return True + mat_has_proxy[lowered] = False + return False + for ent in ctx.vmf.by_class['material_modify_control']: var_name = ent['materialvar'] if var_name and not var_name.startswith(('$', '%')): From 24a22c7d49e993c0714e8a3f69cc4a962f3985a6 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 4 Jul 2023 19:06:04 +1000 Subject: [PATCH 156/243] Set Precache/OnPostSpawn to empty function, to prevent double-firing --- CHANGELOG.md | 1 + src/hammeraddons/bsp_transform/__init__.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eb0dc61e..a6fd77105 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Automatically set the "transmit to client" flag for `info_target`s used as particle system destinations. * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. * Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. +* Set Precache/OnPostSpawn in generated VScript, to prevent double-firing functions already in Entity Scripts. -------------------- diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index 6adc169e7..ac7f66ba7 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -195,6 +195,13 @@ async def run_transformations( LOGGER.info('Injecting VScript code...') for ent, code in context._ent_code.items(): init_scripts = ent['vscripts'].split() + if init_scripts: + # If both a regular entity script and injected script are present, + # the call chaining mechanism used for Precache & OnPostSpawn can malfunction. + # If the entity script defines either, running the second script will make the + # chainer pick it up again, calling the function twice. So edit the script to + # first blank out the functions. + code = 'OnPostSpawn<-Precache<-@()null\n' + code init_scripts.append(pack.inject_vscript(code.replace('`', '"'))) ent['vscripts'] = ' '.join(init_scripts) From e6f3922dc6df94ab1cb967320197987c5b3e26a5 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:04:10 +1000 Subject: [PATCH 157/243] Add ability to search parents of mat-mod-control for valid materials --- transforms/fgd_tweaks/mat_modify_control.py | 82 ++++++++++++++++++--- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py index 91d625378..277fe0571 100644 --- a/transforms/fgd_tweaks/mat_modify_control.py +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -1,6 +1,12 @@ """Tweak material_modify_control to avoid the variable showing up in instances.""" +from collections.abc import Iterable +from typing import Dict, Set + import srctools.logger from hammeraddons.bsp_transform import trans, Context +from srctools import Entity, conv_bool +from srctools.bsp import BModel +from srctools.mdl import Model from srctools.tokenizer import TokenSyntaxError from srctools.vmt import Material @@ -15,12 +21,12 @@ def material_modify_control(ctx: Context) -> None: This allows Hammer to not detect this as a fixup variable. """ # Material name -> does it have either materialmodify or materialmodifyanimated proxies. - mat_has_proxy: dict[str, bool] = {} + mat_has_proxy: Dict[str, bool] = {} # Model or "*xx" index -> list of relevant materials. - model_mats: dict[str, list[str]] = {} - filter_fsys = ctx.pack.fsys # Close over just filesystem. + model_mats: Dict[str, Iterable[str]] = {} + fsys = ctx.pack.fsys # Close over just filesystem. - def filter_materials(mat_name: str) -> bool: + def material_has_proxy(mat_name: str) -> bool: """Check if this material has the proxy.""" lowered = mat_name.casefold() if not lowered.startswith(('materials/', 'materials\\')): @@ -34,7 +40,7 @@ def filter_materials(mat_name: str) -> bool: except KeyError: pass try: - file = filter_fsys[mat_name] + file = fsys[mat_name] except FileNotFoundError: LOGGER.warning('Material "{}" does not exist!', mat_name) mat_has_proxy[lowered] = False @@ -42,7 +48,7 @@ def filter_materials(mat_name: str) -> bool: with file.open_str() as f: try: mat = Material.parse(f, mat_name) - mat.apply_patches(filter_fsys) + mat = mat.apply_patches(fsys) except (TokenSyntaxError, ValueError) as exc: LOGGER.warning('Material "{}" is not valid: ', exc_info=exc) for proxy in mat.proxies: @@ -52,7 +58,65 @@ def filter_materials(mat_name: str) -> bool: mat_has_proxy[lowered] = False return False - for ent in ctx.vmf.by_class['material_modify_control']: - var_name = ent['materialvar'] + matmod: Entity + for matmod in ctx.vmf.by_class['material_modify_control']: + var_name = matmod['materialvar'] if var_name and not var_name.startswith(('$', '%')): - ent['materialvar'] = '$' + var_name + matmod['materialvar'] = '$' + var_name + + if not conv_bool(matmod.pop('srctools_search_parent')): + continue + filter_mat = matmod['materialname'].casefold() + + materials: Set[str] = set() + ent_materials: Iterable[str] + for parent in ctx.vmf.search(matmod['parentname']): + try: + bsp_model: BModel = ctx.bsp.bmodels[parent] + except KeyError: # It must be a prop? + prop_model = parent['model'] + if not prop_model: + LOGGER.warning( + 'Parent "{}" of mat-mod-control "{}" has no model?', + parent['targetname'], matmod['targetname'], + ) + continue + try: + ent_materials = model_mats[prop_model] + except KeyError: + # Get all the materials this model uses. + try: + mdl = Model(fsys, fsys[prop_model]) + except FileNotFoundError: + LOGGER.warning( + 'Model "{}" does not exist for "{}"', + prop_model, parent['targetname'], + ) + model_mats[prop_model] = () + continue + except ValueError: + LOGGER.warning('Invalid model "{}"', prop_model) + model_mats[prop_model] = () + continue + ent_materials = model_mats[prop_model] = list(mdl.iter_textures()) + del mdl # Complicated. + else: # A BSP model. + ent_materials = { + texinfo.mat + for face in bsp_model.faces + if (texinfo := face.texinfo) is not None + } + for mat in ent_materials: + if material_has_proxy(mat) and (not filter_mat or filter_mat in mat.casefold()): + materials.add(mat) + LOGGER.debug('"{}": {} materials', matmod['targetname'], len(materials)) + if not materials: + LOGGER.warning('"{}"\'s parent has no valid materials!', matmod['targetname']) + # Leave it unchanged, in case the material name happens to actually be correct. + else: + mat_iter = iter(materials) + matmod['materialname'] = next(mat_iter) + for extra_mat in mat_iter: + matmod_extra = matmod.copy() + ctx.vmf.add_ent(matmod_extra) + matmod_extra['materialname'] = extra_mat From d88170c54a10b3fa0057e91d6b080fe3588568d1 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:37:43 +1000 Subject: [PATCH 158/243] Use typing.X instead of builtin generics --- src/hammeraddons/bsp_transform/common.py | 4 +-- transforms/comp_adv_output.py | 4 +-- transforms/comp_case.py | 10 +++---- transforms/comp_entity_finder.py | 8 ++++-- transforms/comp_flicker.py | 2 +- transforms/comp_scriptvar_setter.py | 16 +++++++---- transforms/comp_sequential_call.py | 16 +++++------ transforms/geocable.py | 36 ++++++++++++------------ transforms/p2_antline.py | 12 ++++---- transforms/sceneset.py | 4 ++- transforms/vactubes/__init__.py | 29 ++++++++++--------- transforms/vactubes/animations.py | 5 ++-- 12 files changed, 82 insertions(+), 64 deletions(-) diff --git a/src/hammeraddons/bsp_transform/common.py b/src/hammeraddons/bsp_transform/common.py index 424a09a30..8314ff99d 100644 --- a/src/hammeraddons/bsp_transform/common.py +++ b/src/hammeraddons/bsp_transform/common.py @@ -1,7 +1,7 @@ """Operations that can be reused across different transforms.""" import operator import re -from typing import Callable, Tuple +from typing import Callable, Dict, Tuple from decimal import Decimal, InvalidOperation from typing_extensions import Literal, TypeAlias @@ -14,7 +14,7 @@ NumericOp: TypeAlias = Callable[[Decimal, Decimal], bool] NumericSpecifier: TypeAlias = Tuple[NumericOp, Decimal] -OPERATIONS: dict[str, NumericOp] = { +OPERATIONS: Dict[str, NumericOp] = { '<': operator.lt, '>': operator.gt, '>=': operator.ge, diff --git a/transforms/comp_adv_output.py b/transforms/comp_adv_output.py index c74af8a0f..1ce893e82 100644 --- a/transforms/comp_adv_output.py +++ b/transforms/comp_adv_output.py @@ -3,7 +3,7 @@ """ import itertools import string -from typing import Any, Mapping, Sequence, Union +from typing import Any, List, Mapping, Sequence, Union from srctools import EmptyMapping, conv_float, conv_int from srctools.vmf import Output, Entity @@ -57,7 +57,7 @@ def advanced_output(ctx: Context) -> None: ) delay = 0.0 - param_args: list[str] = [] + param_args: List[str] = [] for ind in itertools.count(1): val = adv_out[f'params_local{ind}'] or adv_out[f'params_global{ind}'] if not val: diff --git a/transforms/comp_case.py b/transforms/comp_case.py index 4191924db..d00e5780a 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -1,5 +1,5 @@ """comp_case is a compile-time collapsible version of logic_case.""" -from typing import Iterator, List, Tuple +from typing import Dict, Iterator, List, Tuple import hashlib import re @@ -37,9 +37,9 @@ def collapse_case(ctx: Context, case: Entity) -> None: hasher_template.update(struct.pack(' None: elif out.output.casefold() == 'onused': out_used.append(out) - case_params: dict[int, str] = {} + case_params: Dict[int, str] = {} for k, v in case.items(): if k.casefold().startswith('case'): try: diff --git a/transforms/comp_entity_finder.py b/transforms/comp_entity_finder.py index 1aaff6819..46e615eda 100644 --- a/transforms/comp_entity_finder.py +++ b/transforms/comp_entity_finder.py @@ -2,6 +2,8 @@ import itertools import math from enum import Enum +from typing import Dict, Optional, Set + from srctools import FrozenVec, conv_bool, conv_float, Vec, Entity, Angle from srctools.logger import get_logger @@ -32,7 +34,7 @@ class FinderModes(Enum): @trans('comp_entity_finder') def entity_finder(ctx: Context): """Finds the closest entity of a given type.""" - target_cache: dict[tuple, Entity] = {} + target_cache: Dict[tuple, Entity] = {} for finder in ctx.vmf.by_class['comp_entity_finder']: finder.remove() @@ -74,6 +76,7 @@ def entity_finder(ctx: Context): continue key = (targ_classes, targ_radius, blacklist, targ_fov, targ_pos) + found_ent: Optional[Entity] try: found_ent = target_cache[key] except KeyError: @@ -98,6 +101,7 @@ def blacklist_func(name: str) -> bool: # If multiple, it's the union of the sets. If there's a single one # we don't need to copy by_class[]. + ent_set: Set[Entity] if len(targ_classes) == 1: [single_class] = targ_classes ent_set = ctx.vmf.by_class[single_class] @@ -186,7 +190,7 @@ def blacklist_func(name: str) -> bool: needs_src, needs_known = NEEDS[kv_mode] - known_ent = None + known_ent: Optional[Entity] = None if needs_known: known_ent_name = finder['kv{}_known'.format(ind)] if not known_ent_name: diff --git a/transforms/comp_flicker.py b/transforms/comp_flicker.py index 12d5fa31a..7d2b17394 100644 --- a/transforms/comp_flicker.py +++ b/transforms/comp_flicker.py @@ -77,7 +77,7 @@ def comp_flicker(ctx: Context) -> None: (OUT_FLICK_ON, False, 0.0, total_time), (OUT_FLICK_OFF, True, total_time, 0.0), ]: - time = 0 + time = 0.0 state = start_state limit = 0 while time < total_time: diff --git a/transforms/comp_scriptvar_setter.py b/transforms/comp_scriptvar_setter.py index 7d25cc7b7..42c1bc9bb 100644 --- a/transforms/comp_scriptvar_setter.py +++ b/transforms/comp_scriptvar_setter.py @@ -2,7 +2,9 @@ from __future__ import annotations import re from collections import defaultdict -from typing import Type, Optional, Callable, Union +from typing import Dict, TYPE_CHECKING, Optional, Callable, Union + +from typing_extensions import TypeAlias from srctools.fgd import EntityDef, ValueTypes from srctools.logger import get_logger @@ -14,6 +16,10 @@ LOGGER = get_logger(__name__) MODES: dict[str, Callable[[Entity, Entity], str]] = {} +if TYPE_CHECKING: + EllipsisType: TypeAlias = ellipsis # Fake name before 3.10 +else: + EllipsisType = type(...) def vs_vec(vec: Vec) -> str: @@ -68,7 +74,7 @@ def make_code(self) -> str: for x in array ])) else: - return self.scalar + return self.scalar if self.scalar is not None else 'null' @trans('comp_scriptvar_setter') @@ -78,12 +84,12 @@ def comp_scriptvar(ctx: Context): set_vars: dict[Entity | None, dict[str, VarData]] = defaultdict(lambda: defaultdict(VarData)) # If the index is None, there's no index. # If an int, that specific one. - # If ..., blank index and it's inserted anywhere that fits. + # If ..., blank index, and it's inserted anywhere that fits. for comp_ent in ctx.vmf.by_class['comp_scriptvar_setter']: comp_ent.remove() var_name = comp_ent['variable'] - index: Union[int, Type[Ellipsis], None] = None + index: Union[int, EllipsisType, None] = None if not check_control_enabled(comp_ent): continue @@ -339,7 +345,7 @@ def mode_func(comp_ent: Entity, ent: Entity) -> str: ) # Keyvalue types -> equivalent Squirrel code, if not just stringified. -KEYVALUES = { +KEYVALUES: Dict[ValueTypes, Callable[[str], str]] = { ValueTypes.VOID: lambda val: 'null', ValueTypes.SPAWNFLAGS: str, diff --git a/transforms/comp_sequential_call.py b/transforms/comp_sequential_call.py index 13850d7f5..d55062528 100644 --- a/transforms/comp_sequential_call.py +++ b/transforms/comp_sequential_call.py @@ -1,7 +1,7 @@ +from typing import Iterable, List, Dict, Iterator, Tuple import itertools import random import re -from collections.abc import Iterator from srctools import Vec, Entity, Output, conv_bool, conv_float, lerp import srctools.logger @@ -27,7 +27,7 @@ def sequential_call(ctx: Context) -> None: for seq_call in ctx.vmf.by_class['comp_sequential_call']: seq_call['classname'] = 'logic_relay' - target_ents: list[Entity] = list(ctx.vmf.search(seq_call['target'])) + target_ents: List[Entity] = list(ctx.vmf.search(seq_call['target'])) if not target_ents: LOGGER.warning( 'Sequential call "{}" at {} could find no target entities named "{}"!', @@ -43,7 +43,7 @@ def sequential_call(ctx: Context) -> None: origin = Vec.from_str(seq_call['origin']) if order_mode.startswith('dist'): - dist_to_ent: dict[Entity, float] = { + dist_to_ent: Dict[Entity, float] = { ent: (Vec.from_str(ent['origin']) - origin).mag() for ent in target_ents } @@ -62,10 +62,10 @@ def sequential_call(ctx: Context) -> None: f'"{seq_call["targetname"]}" at ({seq_call["origin"]}).' ) - ent_and_delay: Iterator[tuple[Entity, float]] = [] + ent_and_delay: Iterable[Tuple[Entity, float]] = [] if max_dist < 1e-6 or time_val == 0.0: # No total delay, skip computation and any divide by zero. - ent_and_delay[:] = zip(target_ents, itertools.repeat(0.0)) + ent_and_delay = zip(target_ents, itertools.repeat(0.0)) elif time_mode == 'total': time_start, time_end = (time_val, 0.0) if order_mode.endswith('_inv') else (0.0, time_val) # Special case, if total and dist selected, lerp by distance, not evenly spaced. @@ -88,9 +88,9 @@ def sequential_call(ctx: Context) -> None: f'"{seq_call["targetname"]}" at ({seq_call["origin"]}).' ) - outputs_rep: list[Output] = [] - outputs_final: list[Output] = [] - outputs_other: list[Output] = [] + outputs_rep: List[Output] = [] + outputs_final: List[Output] = [] + outputs_other: List[Output] = [] for out in seq_call.outputs: out_name = out.output.casefold() if out_name == 'onseq': diff --git a/transforms/geocable.py b/transforms/geocable.py index 73869cdd6..0f149790a 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import ( Optional, List, Tuple, FrozenSet, - TypeVar, MutableMapping, NewType, Set, Iterable, Dict, Iterator, + TypeVar, MutableMapping, NewType, Set, Iterable, Dict, Iterator, Union, ) import attrs @@ -26,7 +26,7 @@ LOGGER = logger.get_logger(__name__) NodeID = NewType('NodeID', str) -Number = TypeVar('Number', int, float) +Number = TypeVar('Number', bound=Union[int, float]) try: from .vactubes import nodes as vac_node_mod # type: ignore @@ -491,15 +491,15 @@ def build_node_tree( id_to_node: dict[str, tuple[Node, Optional[Node]]] = {} vis_nodes: set[Node] = set() coll_nodes: Set[Node] = set() - for node in ents: - vis_node = Node(node.pos.copy(), node.config) + for node_ent in ents: + vis_node = Node(node_ent.pos.copy(), node_ent.config) vis_nodes.add(vis_node) - if node.config.coll_side_count >= 3: - coll_node = Node(node.pos.copy(), node.config.coll()) + if node_ent.config.coll_side_count >= 3: + coll_node = Node(node_ent.pos.copy(), node_ent.config.coll()) coll_nodes.add(coll_node) else: coll_node = None - id_to_node[node.id] = (vis_node, coll_node) + id_to_node[node_ent.id] = (vis_node, coll_node) def maybe_split(nodes: Set[Node], node: Node, direction: str) -> Node: """Split nodes to ensure they only have 1 or 2 connections. @@ -580,7 +580,7 @@ def interpolate_catmull_rom(node1: Node, node2: Node, seg_count: int) -> List[No t1 = t0 + (p1-p0).mag() t2 = t1 + (p2-p1).mag() t3 = t2 + (p3-p2).mag() - points: list[Node] = [] + points: List[Node] = [] for i in range(1, seg_count + 1): t = lerp(i, 0, seg_count + 1, t1, t2) A1 = (t1-t)/(t1-t0)*p0 + (t-t0)/(t1-t0)*p1 @@ -711,8 +711,8 @@ def compute_orients(nodes: Iterable[Node]) -> None: """Compute the appropriate orientation for each node.""" # This is based on the info at: # https://janakiev.com/blog/framing-parametric-curves/ - tangents: dict[Node, Vec] = {} - all_nodes: set[Node] = set() + tangents: Dict[Node, Vec] = {} + all_nodes: Set[Node] = set() for node in nodes: if node.prev is node.next is None: continue @@ -874,7 +874,7 @@ def generate_vac_beams(nodes: Iterable[Node], bone: Bone, vac_points: List[List[ BEAM_WID = 2.17316 node_pos: Vec - def vert_node1(y: float, z: float, norm: tuple[float, float, float], u: float) -> Vertex: + def vert_node1(y: float, z: float, norm: Tuple[float, float, float], u: float) -> Vertex: """Helper for generating at the first node.""" return Vertex( pos1 + (0.0, y, z) @ orient1, @@ -987,8 +987,8 @@ def vert_node2(y: float, z: float, norm: Tuple[float, float, float], u: float) - def place_seg_props(nodes: Iterable[Node], fsys: FileSystem, mesh: Mesh) -> Iterator[SegProp]: """Place segment props, across the nodes.""" - mesh_cache: dict[str, Mesh] = {} - prop_dists: dict[SegPropConf, float] = {} + mesh_cache: Dict[str, Mesh] = {} + prop_dists: Dict[SegPropConf, float] = {} for start_node in nodes: # Find start nodes, we then loop in order over the nodes. if start_node.prev is not None: @@ -1123,7 +1123,7 @@ async def compile_rope( center = (bbox_min + bbox_max) / 2 node: Optional[NodeEnt] = None has_coll = False - local_nodes: set[NodeEnt] = set() + local_nodes: Set[NodeEnt] = set() for node in nodes: local_nodes.add(attrs.evolve(node, pos=node.pos - center)) if node.config.coll_side_count >= 3: @@ -1199,7 +1199,7 @@ async def comp_prop_rope(ctx: Context) -> None: # Dynamic ents which will be given the static props. group_dyn_ents: Dict[str, List[Entity]] = defaultdict(list) # Name -> segprop configurations. - name_to_segprops_lst: dict[str, list[SegPropConf]] = defaultdict(list) + name_to_segprops_lst: Dict[str, List[SegPropConf]] = defaultdict(list) for ent in ctx.vmf.by_class['comp_prop_rope_bunting']: ent.remove() @@ -1214,7 +1214,7 @@ async def comp_prop_rope(ctx: Context) -> None: # Put into a set, so they're immutable and have no ordering. # We use that to identify the same config in previous compiles. - name_to_segprops_set: dict[str, frozenset[SegPropConf]] = { + name_to_segprops_set: Dict[str, FrozenSet[SegPropConf]] = { name: frozenset(lst) for name, lst in name_to_segprops_lst.items() } @@ -1268,9 +1268,9 @@ async def comp_prop_rope(ctx: Context) -> None: found: List[NodeEnt] = [] if target.endswith('*'): search = target[:-1] - for name, nodes in name_to_nodes.items(): + for name, node_list in name_to_nodes.items(): if name.startswith(search): - found.extend(nodes) + found.extend(node_list) else: found.extend(name_to_nodes.get(target, ())) for dest in found: diff --git a/transforms/p2_antline.py b/transforms/p2_antline.py index 473c0a352..8dd20ef02 100644 --- a/transforms/p2_antline.py +++ b/transforms/p2_antline.py @@ -4,6 +4,8 @@ If the one of the target entities is a prop_indicator_panel, it also toggles that. """ +from typing import List, Set + from srctools import Entity, Output from srctools.logger import get_logger @@ -46,15 +48,15 @@ def comp_antlines(ctx: Context): # These are the names, not the ents themselves. # Or brush ents holding overlays. - ind_overlays: set[str] = set() - ind_toggles: set[str] = set() + ind_overlays: Set[str] = set() + ind_toggles: Set[str] = set() # These need the right inputs. - ind_panel_tim: set[str] = set() - ind_panel_check: set[str] = set() + ind_panel_tim: Set[str] = set() + ind_panel_check: Set[str] = set() # Panels without an indicator set - we can use # these instead of a texturetoggle. - unused_panels: list[Entity] = [] + unused_panels: List[Entity] = [] for ind_ent in ctx.vmf.search(ind_name): cls = ind_ent['classname'] diff --git a/transforms/sceneset.py b/transforms/sceneset.py index 829c2aa8b..bc17e3ef1 100644 --- a/transforms/sceneset.py +++ b/transforms/sceneset.py @@ -1,4 +1,6 @@ """Implement comp_choreo_sceneset.""" +from typing import List + from srctools import Entity, Output, conv_bool, conv_float from srctools.logger import get_logger @@ -33,7 +35,7 @@ def sceneset(ctx: Context): ent.remove() - scene_ents: list[Entity] = [] + scene_ents: List[Entity] = [] name = ent.make_unique('_choreo')['targetname'] for i, scene in enumerate(scenes): diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index 2530a5d95..b174b461b 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -227,8 +227,8 @@ async def vactube_transform(ctx: Context) -> None: for tri in mesh.triangles: for point in tri: point.tex_u, point.tex_v = U[point.tex_u], V[point.tex_v] - with open(temp_dir + '/ref.smd', 'wb') as f: - mesh.export(f) + with open(temp_dir + '/ref.smd', 'wb') as mesh_file: + mesh.export(mesh_file) with open(temp_dir + '/prop.qc', 'w') as qc_file: qc_file.write(QC_TEMPLATE.format(path=anim_mdl_name)) @@ -237,8 +237,8 @@ async def vactube_transform(ctx: Context) -> None: anim.name = anim_name = f'anim_{i:03x}' qc_file.write(SEQ_TEMPLATE.format(name=anim_name, fps=animations.FPS)) - with open(temp_dir + f'/{anim_name}.smd', 'wb') as f: - anim.mesh.export(f) + with open(temp_dir + f'/{anim_name}.smd', 'wb') as mesh_file: + anim.mesh.export(mesh_file) args = [ str(ctx.studiomdl), @@ -258,12 +258,15 @@ async def vactube_transform(ctx: Context) -> None: # Ensure they're all packed. for ext in MDL_EXTS: try: - f = full_loc.with_suffix(ext).open('rb') + mdl_file = full_loc.with_suffix(ext).open('rb') except FileNotFoundError: pass else: - with f: - ctx.pack.pack_file(Path('models', anim_mdl_name.with_suffix(ext)), data=f.read()) + with mdl_file: + ctx.pack.pack_file( + Path('models', anim_mdl_name.with_suffix(ext)), + data=mdl_file.read(), + ) LOGGER.info('Setting up vactube ents...') # Generate the shared template. @@ -337,7 +340,7 @@ async def vactube_transform(ctx: Context) -> None: # Now, generate the code so the VScript knows about the animations. code = [f'// Node: {start_node.ent["targetname"]}, {start_node.origin}'] for anim in anims: - target = anim.end_node + anim_dest = anim.end_node anim_speed = anim.start_node.speed pass_code = ','.join([ f'Output({time:.2f}, "{node.ent["targetname"]}", ' @@ -345,11 +348,11 @@ async def vactube_transform(ctx: Context) -> None: for time, node in anim.pass_points ]) cube_name = 'null' - if isinstance(target, nodes.Dropper): - cube_model = target.cube['model'].replace('\\', '/') - cube_skin = conv_int(target.cube['skin']) + if isinstance(anim_dest, nodes.Dropper): + cube_model = anim_dest.cube['model'].replace('\\', '/') + cube_skin = conv_int(anim_dest.cube['skin']) try: - cube_name = objects.find_for_cube(vac_objects, start_node.group, target.cube).id + cube_name = objects.find_for_cube(vac_objects, start_node.group, anim_dest.cube).id except LookupError: LOGGER.warning( 'Cube model "{}", skin {} is not a type of cube travelling ' @@ -361,7 +364,7 @@ async def vactube_transform(ctx: Context) -> None: ) continue # Skip this animation so it's not broken. else: - dropper_to_anim[target] = anim + dropper_to_anim[anim_dest] = anim code.append( f'{anim.name} <- anim("{anim.name}", {anim.duration}, ' f'{cube_name}, [{pass_code}]);' diff --git a/transforms/vactubes/animations.py b/transforms/vactubes/animations.py index e78df94cc..321d52161 100644 --- a/transforms/vactubes/animations.py +++ b/transforms/vactubes/animations.py @@ -74,9 +74,9 @@ def __init__(self, start_node: nodes.Spawner) -> None: [self.move_bone] = self.mesh.bones.values() self.cur_frame = 0 # For nodes with OnPass outputs, the time to fire each of those. - self.pass_points: list[tuple[float, nodes.Node]] = [] + self.pass_points: List[tuple[float, nodes.Node]] = [] # Set of nodes in this animation, to prevent loops. - self.history: list[nodes.Node] = [start_node] + self.history: List[nodes.Node] = [start_node] # The kind of curve used for the current node. self.curve_type = DestType.PRIMARY @@ -183,6 +183,7 @@ def generate(sources: List[nodes.Spawner]) -> List[Animation]: # Now generate the straight part between this node and the next. next_node = node.outputs[anim.curve_type] + assert next_node is not None cur_end = node.vec_point(1.0, anim.curve_type) straight_off = next_node.vec_point(0.0) - cur_end From 5d154d94f88b2e4704f51762bec17ec0848b0d48 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:39:16 +1000 Subject: [PATCH 159/243] Use formatting instead of concatenation here --- transforms/vactubes/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index b174b461b..f71bcdcfb 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -357,10 +357,10 @@ async def vactube_transform(ctx: Context) -> None: LOGGER.warning( 'Cube model "{}", skin {} is not a type of cube travelling ' 'in this vactube!\n\n' - 'Add a comp_vactube_object entity with this cube model' - # Mention groups if they're used, otherwise it's not important. - + (f' with the group "{start_node.group}".' if start_node.group else '.'), + 'Add a comp_vactube_object entity with this cube model{}', cube_model, cube_skin, + # Mention groups if they're used, otherwise it's not important. + f' with the group "{start_node.group}".' if start_node.group else '.', ) continue # Skip this animation so it's not broken. else: From 21896318fa025a221270e640805c743e564f9ac6 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:44:30 +1000 Subject: [PATCH 160/243] Fix this being totally wrong --- transforms/p2_brush_ents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transforms/p2_brush_ents.py b/transforms/p2_brush_ents.py index 7585a305f..175217baa 100644 --- a/transforms/p2_brush_ents.py +++ b/transforms/p2_brush_ents.py @@ -54,7 +54,7 @@ def comp_trigger_coop(ctx: Context): ) # Only keep OnChangeToAllTrue outputs, and remove # them once they've fired. - for out in list(manager): + for out in list(manager.outputs): if out.output.casefold() == 'onchangetoalltrue': out.only_once = True else: From 70136e2740f0675be00289c151e32b67d049e93d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:47:02 +1000 Subject: [PATCH 161/243] Note that trigger_look uses cos(angle) as the FOV. --- fgd/brush/trigger/trigger_look.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/brush/trigger/trigger_look.fgd b/fgd/brush/trigger/trigger_look.fgd index d117a3f52..5000d393e 100644 --- a/fgd/brush/trigger/trigger_look.fgd +++ b/fgd/brush/trigger/trigger_look.fgd @@ -19,7 +19,7 @@ looktime(float) : "LookTime" : "0.5" : "The time, in seconds, that the player must look the target before firing the output. " + "Resets if player leaves trigger, or looks outside the Field of View threshold." fieldofview(float) : "FieldOfView" : "0.9" : "How close the player has to be looking at the target. " + - "1.0 = straight ahead\n 0.0 = +/- 90 degrees\n -1.0 = all directions)." + "1.0 = perfectly straight ahead, 0.0 = +/- 90 degrees, -1.0 = all directions). This is actually cos(angle)." timeout(float) : "Timeout" : 0 : "The time, in seconds, to wait after player enters the trigger before firing the OnTimeout output, 0 = never." NotLookingFrequency(float) : "Not-Looking Frequency" : "0.5" : "time in second between 2 fires of OnNotLooking output." From b1ebf424c66875ca2f5ec252c11c447206f83c8b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:47:12 +1000 Subject: [PATCH 162/243] Avoid iter(str) here. --- transforms/portal2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transforms/portal2.py b/transforms/portal2.py index ec3f3c10e..b5f96226c 100644 --- a/transforms/portal2.py +++ b/transforms/portal2.py @@ -11,7 +11,7 @@ def laser_catcher_skins(ctx: Context): if not conv_bool(ent['src_fix_skins'], True): continue - deact_skin, act_skin = '23' if ent['SkinType'] == '1' else '01' + deact_skin, act_skin = ('2', '3') if ent['SkinType'] == '1' else ('0', '1') # Look for outputs which do this already. name = ent['targetname'] @@ -57,6 +57,7 @@ def needs_paint(vmf: VMF) -> bool: # If the cube is bouncy, enable paint. if conv_int(ent['paintpower', '4'], 4) != 4: return True + return False @trans('Force Paint in Map') From 9dff9486868243e023a642b6a027d4409166490c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 5 Jul 2023 14:47:38 +1000 Subject: [PATCH 163/243] Assert needs-known is set when being used. --- transforms/comp_entity_finder.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transforms/comp_entity_finder.py b/transforms/comp_entity_finder.py index 46e615eda..198d50e10 100644 --- a/transforms/comp_entity_finder.py +++ b/transforms/comp_entity_finder.py @@ -226,13 +226,17 @@ def blacklist_func(name: str) -> bool: found_ent[kv_dest] = kv_src elif kv_mode is FinderModes.CONST_KNOWN: # Set constant value on known entity. + assert needs_known and known_ent is not None known_ent[kv_dest] = kv_src elif kv_mode is FinderModes.TARG_TO_KNOWN: + assert needs_known and known_ent is not None known_ent[kv_dest] = found_ent[kv_src] elif kv_mode is FinderModes.KNOWN_TO_TARG: + assert needs_known and known_ent is not None found_ent[kv_dest] = known_ent[kv_src] elif kv_mode is FinderModes.OUTPUT_MERGE: output_name = '!' + kv_dest.lstrip('!').casefold() + assert needs_known and known_ent is not None for out in known_ent.outputs: if out.target.casefold() == output_name: out.target = found_ent['targetname'] From ae9faee0d2335d861d8fe065fa8cd0ee8fb53eb7 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 6 Jul 2023 10:52:48 +1000 Subject: [PATCH 164/243] Check for and warn if multiple parents are found for material_modify_control --- transforms/fgd_tweaks/mat_modify_control.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py index 277fe0571..16b73f993 100644 --- a/transforms/fgd_tweaks/mat_modify_control.py +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -1,6 +1,6 @@ """Tweak material_modify_control to avoid the variable showing up in instances.""" from collections.abc import Iterable -from typing import Dict, Set +from typing import Counter, Dict, Set import srctools.logger from hammeraddons.bsp_transform import trans, Context @@ -70,7 +70,9 @@ def material_has_proxy(mat_name: str) -> bool: materials: Set[str] = set() ent_materials: Iterable[str] + found_count = Counter[str]() for parent in ctx.vmf.search(matmod['parentname']): + found_count[parent['targetname']] += 1 try: bsp_model: BModel = ctx.bsp.bmodels[parent] except KeyError: # It must be a prop? @@ -108,9 +110,17 @@ def material_has_proxy(mat_name: str) -> bool: } for mat in ent_materials: if material_has_proxy(mat) and (not filter_mat or filter_mat in mat.casefold()): - materials.add(mat) - LOGGER.debug('"{}": {} materials', matmod['targetname'], len(materials)) - if not materials: + targets.add((parent['targetname'], mat)) + duplicates = found_count.most_common(2) + if duplicates: + LOGGER.warning( + '"{}" has multiple entities with the same name in parents! ' + 'Only one with each name will be affected:\n{}', + matmod['targetname'], + '\n'.join([f'- {name}' for name, count in duplicates]), + ) + LOGGER.debug('"{}": {} ents to generate', matmod['targetname'], len(targets)) + if not targets: LOGGER.warning('"{}"\'s parent has no valid materials!', matmod['targetname']) # Leave it unchanged, in case the material name happens to actually be correct. else: From ef89964ffb17c7c7186b2c6eabcd1e19bd0e19e9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 6 Jul 2023 10:53:31 +1000 Subject: [PATCH 165/243] Allow physboxes to completely override their mass. --- CHANGELOG.md | 1 + fgd/brush/func/func_physbox.fgd | 1 + transforms/fgd_tweaks/func_physbox.py | 29 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 transforms/fgd_tweaks/func_physbox.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a6fd77105..9a1a24203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. * Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. * Set Precache/OnPostSpawn in generated VScript, to prevent double-firing functions already in Entity Scripts. +* Allow physboxes to completely override their mass. -------------------- diff --git a/fgd/brush/func/func_physbox.fgd b/fgd/brush/func/func_physbox.fgd index 36387b13d..9cfd3a5b4 100644 --- a/fgd/brush/func/func_physbox.fgd +++ b/fgd/brush/func/func_physbox.fgd @@ -27,6 +27,7 @@ ] massscale(float) : "Mass Scale" : 0 : "A scale multiplier for the object's mass." + ha_override_mass[srctools](float) : "[HA] Mass Override" : : "If set, the postcompiler will modify the brush data to directly set it to this mass value." overridescript(string) : "Override Parameters" : : "A list of physics key/value pairs that are usually in a physics prop .qc file. Format is 'key,value,key,value,etc'." damagetoenablemotion(integer) : "Health Level to Override Motion" : 0 : "If specified, this object will start motion disabled. Once its health has dropped below this specified amount, it will enable motion." forcetoenablemotion(float) : "Physics Impact Force to Override Motion" : 0 : "If specified, this object will start motion disabled. Any impact that imparts a force greater than this value on the physbox will enable motion." diff --git a/transforms/fgd_tweaks/func_physbox.py b/transforms/fgd_tweaks/func_physbox.py new file mode 100644 index 000000000..4d1cf1a6e --- /dev/null +++ b/transforms/fgd_tweaks/func_physbox.py @@ -0,0 +1,29 @@ +"""Allow physboxes to completely override the mass of physics brushes.""" +from srctools import conv_float, logger + +from hammeraddons.bsp_transform import trans, Context +from srctools.bsp import BModel + + +LOGGER = logger.get_logger(__name__) + + +@trans('FGD - func_physbox mass override') +def mass_override(ctx: Context) -> None: + """Allow overriding the mass entirely for physboxes.""" + for ent in iter(ctx.vmf.by_class['func_physbox']): + override = ent.pop('ha_override_mass') + if conv_float(override) <= 0.0: + continue + try: + bmodel: BModel = ctx.bsp.bmodels[ent] + except KeyError: + LOGGER.warning( + 'func_physbox "{}" @ {} is not a brush?', + ent['targetname'], ent['origin'], + ) + continue + if bmodel.phys_keyvalues is None: + continue + for solid in bmodel.phys_keyvalues.find_all('solid'): + solid['mass'] = override From f9d92a1f7db8d95641817329fde948ff82907bff Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 6 Jul 2023 10:53:56 +1000 Subject: [PATCH 166/243] Fix some types here --- transforms/geocable.py | 42 +++++++++++++++++-------------- transforms/vactubes/animations.py | 19 +++++++------- transforms/vactubes/objects.py | 2 +- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 0f149790a..c3660f059 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -1,4 +1,9 @@ """Compile static prop cables, instead of sprites.""" +from typing import ( + Optional, List, Tuple, FrozenSet, Callable, + TypeVar, MutableMapping, NewType, Set, Iterable, Dict, Iterator, cast, +) +from typing_extensions import Final, Self import itertools import math import struct @@ -6,16 +11,12 @@ from collections import defaultdict from enum import Enum from pathlib import Path -from typing import ( - Optional, List, Tuple, FrozenSet, - TypeVar, MutableMapping, NewType, Set, Iterable, Dict, Iterator, Union, -) import attrs import trio from srctools import ( - logger, conv_int, conv_float, conv_bool, + FrozenVec, logger, conv_int, conv_float, conv_bool, Vec, Entity, Matrix, Angle, lerp, FileSystem, ) from srctools.bsp import StaticProp, StaticPropFlags, VisLeaf, VisTree @@ -26,7 +27,7 @@ LOGGER = logger.get_logger(__name__) NodeID = NewType('NodeID', str) -Number = TypeVar('Number', bound=Union[int, float]) +Number = TypeVar('Number', int, float) try: from .vactubes import nodes as vac_node_mod # type: ignore @@ -97,9 +98,9 @@ class RopePhys: radius: float # Just to transfer to the node. -ROPE_GRAVITY = -1500 -SIM_TIME = 5.00 -TIME_STEP = 1/50 +ROPE_GRAVITY: Final = -1500 +SIM_TIME: Final = 5.00 +TIME_STEP: Final = 1/50 @attrs.frozen(hash=False) @@ -160,7 +161,7 @@ class Config: coll_segments: int coll_side_count: int seg_props: FrozenSet[SegPropConf] - prop_rendercolor: Tuple[float, float, float] + prop_rendercolor: FrozenVec prop_renderalpha: int prop_no_shadows: bool prop_no_vert_light: bool @@ -173,7 +174,10 @@ class Config: @staticmethod def _parse_min(ent: Entity, keyvalue: str, minimum: Number, message: str) -> Number: """Helper for passing all the numeric keys.""" - value = (conv_float if isinstance(minimum, float) else conv_int)(ent[keyvalue], minimum) + value: Number = cast( + 'Callable[[str, Number], Number]', + (conv_float if isinstance(minimum, float) else conv_int), + )(ent[keyvalue], minimum) if value < minimum: LOGGER.warning(message, ent['origin']) return minimum @@ -185,7 +189,7 @@ def is_vactube(self) -> bool: return self.type.is_vactube @classmethod - def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) -> 'Config': + def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) -> Self: """Parse from an entity.""" segments = cls._parse_min( ent, 'segments', 0, @@ -211,7 +215,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) coll_side_count = 0 coll_segments = -1 radius = VAC_RADIUS - slack = 0 # Unused. + slack = 0.0 # Unused. interp_type = InterpType.CATMULL_ROM u_min = 0.0 u_max = 1.0 @@ -282,7 +286,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) coll_segments, coll_side_count, seg_props, - tuple(Vec.from_str(ent['rendercolor'], 255, 255, 255)), + FrozenVec.from_str(ent['rendercolor'], 255, 255, 255), alpha, conv_bool(ent['disableshadows']), conv_bool(ent['disablevertexlighting']), @@ -293,7 +297,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) conv_float(ent['fadescale'], 0.0), ) - def coll(self) -> Optional['Config']: + def coll(self) -> Optional[Self]: """Extract the collision options from the ent.""" return attrs.evolve( self, @@ -327,7 +331,7 @@ def __hash__(self) -> int: )) -@attrs.define(eq=False) +@attrs.define(eq=False, repr=False) class Node: """All the data for a node, used during construction of the geo. @@ -488,8 +492,8 @@ def build_node_tree( ) -> Tuple[Set[Node], Set[Node]]: """Convert the ents/connections definitions into a node tree.""" # Convert them all into the real node objects. - id_to_node: dict[str, tuple[Node, Optional[Node]]] = {} - vis_nodes: set[Node] = set() + id_to_node: Dict[str, Tuple[Node, Optional[Node]]] = {} + vis_nodes: Set[Node] = set() coll_nodes: Set[Node] = set() for node_ent in ents: vis_node = Node(node_ent.pos.copy(), node_ent.config) @@ -622,7 +626,7 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: ] springs = list(zip(points, points[1:])) - time = 0 + time = 0.0 step = TIME_STEP gravity = Vec(z=ROPE_GRAVITY) * step**2 # Valve uses 3 iterations, but they only ever have 10 subdivisions. diff --git a/transforms/vactubes/animations.py b/transforms/vactubes/animations.py index 321d52161..57676672e 100644 --- a/transforms/vactubes/animations.py +++ b/transforms/vactubes/animations.py @@ -3,7 +3,7 @@ from typing import Tuple, List, Optional, Union from . import nodes -from .nodes import DestType +from .nodes import Node, DestType from srctools import Vec, Angle from srctools.smd import BoneFrame, Mesh from random import Random @@ -74,7 +74,7 @@ def __init__(self, start_node: nodes.Spawner) -> None: [self.move_bone] = self.mesh.bones.values() self.cur_frame = 0 # For nodes with OnPass outputs, the time to fire each of those. - self.pass_points: List[tuple[float, nodes.Node]] = [] + self.pass_points: List[Tuple[float, nodes.Node]] = [] # Set of nodes in this animation, to prevent loops. self.history: List[nodes.Node] = [start_node] # The kind of curve used for the current node. @@ -84,7 +84,7 @@ def __init__(self, start_node: nodes.Spawner) -> None: self.start_node = start_node # Either the start point, or the splitter to move in the secondary direction. self.cur_node: Union[nodes.Spawner, nodes.Splitter] = start_node - # Once done, the ending node so we can determine if it's a dropper or not. + # Once done, this is the ending node so that we can determine if it's a dropper or not. self.end_node: Optional[nodes.Destroyer] = None # When branching, the amount we overshot into this node from last time. self.start_overshoot = 0.0 @@ -133,7 +133,7 @@ def generate(sources: List[nodes.Spawner]) -> List[Animation]: anims = [Animation(node) for node in sources] for anim in anims: - node = anim.cur_node + node: Node = anim.cur_node speed = anim.start_node.speed / FPS offset = anim.start_node.origin.copy() @@ -160,12 +160,12 @@ def generate(sources: List[nodes.Spawner]) -> List[Animation]: seg_frames = math.ceil((seg_len - overshoot) / speed) for i in range(int(seg_frames)): # Make each frame. - pos = (overshoot + speed * i) / seg_len - if needs_out and pos > 0.5: + fraction = (overshoot + speed * i) / seg_len + if needs_out and fraction > 0.5: anim.pass_points.append((anim.duration, node)) needs_out = False # Place the point. - last_loc = node.vec_point(pos, anim.curve_type) + last_loc = node.vec_point(fraction, anim.curve_type) anim.add_point(last_loc - offset) # If short, we might not have placed the output. @@ -200,7 +200,7 @@ def generate(sources: List[nodes.Spawner]) -> List[Animation]: for i in range(int(seg_frames)): # Make each frame. - pos = cur_end + ((overshoot + speed * i) / straight_dist) * straight_off + pos = cur_end + straight_off * ((overshoot + speed * i) / straight_dist) anim.add_point(pos - offset) overshoot += (speed * seg_frames) - straight_dist @@ -208,7 +208,8 @@ def generate(sources: List[nodes.Spawner]) -> List[Animation]: overshoot += straight_off.mag() # And advance to the next node. - anim.cur_node = node = next_node + anim.cur_node = next_node + node = next_node anim.history.append(node) # We only do secondary for the first node, we always continue diff --git a/transforms/vactubes/objects.py b/transforms/vactubes/objects.py index 7ba91b3b9..217b1acc7 100644 --- a/transforms/vactubes/objects.py +++ b/transforms/vactubes/objects.py @@ -125,7 +125,7 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]] LOGGER.info('Group "{}" has common factor of {}, simplifying.', group, multiple) code = [] for obj in objects: - obj.weight /= multiple + obj.weight //= multiple code.append(obj.make_code()) codes[group] = pack.inject_vscript('\n'.join(code)) From 863ef3af93635fdabc0832b255f89d81c24638a9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 6 Jul 2023 10:54:19 +1000 Subject: [PATCH 167/243] Duplicate mat-mod-control for each found parent, as well. --- transforms/fgd_tweaks/mat_modify_control.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py index 16b73f993..78b27b6ae 100644 --- a/transforms/fgd_tweaks/mat_modify_control.py +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -68,7 +68,7 @@ def material_has_proxy(mat_name: str) -> bool: continue filter_mat = matmod['materialname'].casefold() - materials: Set[str] = set() + targets: Set[tuple[str, str]] = set() ent_materials: Iterable[str] found_count = Counter[str]() for parent in ctx.vmf.search(matmod['parentname']): @@ -124,9 +124,10 @@ def material_has_proxy(mat_name: str) -> bool: LOGGER.warning('"{}"\'s parent has no valid materials!', matmod['targetname']) # Leave it unchanged, in case the material name happens to actually be correct. else: - mat_iter = iter(materials) - matmod['materialname'] = next(mat_iter) - for extra_mat in mat_iter: + targ_iter = iter(targets) + matmod['parentname'], matmod['materialname'] = next(targ_iter) + for parent_name, extra_mat in targ_iter: matmod_extra = matmod.copy() ctx.vmf.add_ent(matmod_extra) + matmod_extra['parentname'] = parent_name matmod_extra['materialname'] = extra_mat From c2774cbdede1d6389b9b87a45a4e00791bdb8501 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 6 Jul 2023 13:22:27 +1000 Subject: [PATCH 168/243] Bump srctools version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0205b3c22..d312e3298 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs >= 21.4.0 typing_extensions >= 4.2.0 -srctools >= 2.3.12 +srctools >= 2.3.13 trio >= 0.20.0 trio-typing >= 0.7.0 pyinstaller >= 5.12.0 From 40eb636b947b8bd5239218d0e63cdd4f8d1a8e40 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 7 Jul 2023 20:35:06 -0700 Subject: [PATCH 169/243] Fix point_viewcontrol frustum rotation --- fgd/point/point/point_viewcontrol.fgd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fgd/point/point/point_viewcontrol.fgd b/fgd/point/point/point_viewcontrol.fgd index f61f3a018..e8ab72d5f 100644 --- a/fgd/point/point/point_viewcontrol.fgd +++ b/fgd/point/point/point_viewcontrol.fgd @@ -1,7 +1,8 @@ @PointClass base(BaseEntityPoint) studioprop("models/editor/camera.mdl") color(200 0 0) - frustum(fov, 4, _frustum_far, 250 250 250, -1) + // Frustum color must be a keyvalue name, not a raw RGB value + frustum(fov, 4, _frustum_far, _frustum_color, -1) line(255 255 0, targetname, target) line(255 255 0, targetname, moveto) = point_viewcontrol: "A camera entity that controls the player's view. While it's active, the player will see out of the camera." From b8a18aca80a5d4a185190ed02e8986c146932b45 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 7 Jul 2023 20:36:06 -0700 Subject: [PATCH 170/243] logic_playerproxy cleanup --- fgd/point/logic/logic_playerproxy.fgd | 39 ++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/fgd/point/logic/logic_playerproxy.fgd b/fgd/point/logic/logic_playerproxy.fgd index bc7ccc3cc..80307f532 100644 --- a/fgd/point/logic/logic_playerproxy.fgd +++ b/fgd/point/logic/logic_playerproxy.fgd @@ -22,8 +22,8 @@ HandsVMBody[MBase](string) : "Hands Viewmodel Bodygroup" : : "Bodygroup value for the custom hands viewmodel." // Inputs and Outputs - input RequestPlayerHealth(void) : "Requests the current player's health from the proxy. This will fire the PlayerHealth output with the value." - output PlayerHealth(integer) : "The player's current health value, fired in response to RequestPlayerHealth." + input RequestPlayerHealth[HL2, P1](void) : "Requests the current player's health from the proxy. This will fire the PlayerHealth output with the value." + output PlayerHealth[HL2, P1](integer) : "The player's current health value, fired in response to RequestPlayerHealth." input RequestPlayerArmor[MBase](void) : "Requests the current player's armor from the proxy. This will fire the PlayerArmor output with the value." output PlayerArmor[MBase](integer) : "The player's current armor value, fired in response to RequestPlayerArmor." @@ -34,25 +34,25 @@ input RequestPlayerFlashBattery[MBase](void) : "Requests the current player's current flashlight battery from the proxy. This will fire the PlayerFlashBattery output with the value.\n\nNOTE: If legacy flashlight is enabled (aux power flashlight), this will return the player's current auxiliary power." output PlayerFlashBattery[MBase](float) : "The player's current flashlight battery percentage, fired in response to RequestPlayerFlashBattery." - input RequestAmmoState(void) : "Request the ammo state of the player. It will fire PlayerHasAmmo or PlayerHasNoAmmo outputs." - output PlayerHasAmmo(void) : "Fired by request if the player has any ammo." - output PlayerHasNoAmmo(void) : "Fired by request if the player doesn't have any ammo." + input RequestAmmoState[HL2, P1](void) : "Request the ammo state of the player. It will fire PlayerHasAmmo or PlayerHasNoAmmo outputs." + output PlayerHasAmmo[HL2, P1](void) : "Fired by request if the player has any ammo." + output PlayerHasNoAmmo[HL2, P1](void) : "Fired by request if the player doesn't have any ammo." - input SetFlashlightSlowDrain(void) : "Puts the player's flashlight in slow-power-drain mode (for Episodic darkness)" - input SetFlashlightNormalDrain(void) : "Puts the player's flashlight to default power drain" + input SetFlashlightSlowDrain[HL2, P1](void) : "Puts the player's flashlight in slow-power-drain mode (for Episodic darkness)" + input SetFlashlightNormalDrain[HL2, P1](void) : "Puts the player's flashlight to default power drain" - input SetPlayerHealth(integer) : "Sets the player's health to this value." + input SetPlayerHealth[HL2, P1](integer) : "Sets the player's health to this value." input SetMaxInputArmor[MBase](integer) : "Sets the maximum armor value that could be set by armor inputs." input SetSuitZoomFOV[MBase](integer) : "Sets the FOV used by suit zoom." - input LowerWeapon(void) : "Lowers the players weapon." + input LowerWeapon[HL2, P1](void) : "Lowers the players weapon." input SuppressCrosshair[P1](void) : "Disable the player's crosshair." - input EnableCappedPhysicsDamage(void) : "Cause player to take less damage from physics objects, and never more than 30 points from any individual strike." - input DisableCappedPhysicsDamage(void) : "Undo effects of EnableCappedPhysicsDamage" + input EnableCappedPhysicsDamage[HL2, P1](void) : "Cause player to take less damage from physics objects, and never more than 30 points from any individual strike." + input DisableCappedPhysicsDamage[HL2, P1](void) : "Undo effects of EnableCappedPhysicsDamage" - input SetLocatorTargetEntity(string) : "Set the entity that the HUD locator should track. (Usually a vehicle)" + input SetLocatorTargetEntity[HL2, P1](string) : "Set the entity that the HUD locator should track. (Usually a vehicle)" input AddPotatosToPortalgun[P2](void) : "Change portalgun bodygroup to show potatos." input RemovePotatosFromPortalgun[P2](void) : "Change portalgun bodygroup to not show potatos." @@ -61,24 +61,27 @@ input ForcePhysicsGrabController[P2](void) : "Force the player to use the physics grab controller for all objects that are picked up." input ResetGrabControllerBehavior[P2](void) : "Resets the grab controller used by the player to its default behavior." input SetMotionBlurAmount[P2](float) : "Forces the motion blur effect on the player. Set to < 0 to disable this override." + input PaintPlayerWithPortalPaint[P2](void) : "Displays a Conversion Gel splashing effect on the player's screen." // Outputs - output OnFlashlightOn(float) : "Fired when the player turns on their flashlight. This output has the value of how much energy the player had when this happened [0..1]." - output OnFlashlightOff(float) : "Fired when the player turns off their flashlight. This output has the value of how much energy the player had when this happened [0..1]." + output OnFlashlightOn[HL2, P1](float) : "Fired when the player turns on their flashlight. This output has the value of how much energy the player had when this happened [0..1]." + output OnFlashlightOff[HL2, P1](float) : "Fired when the player turns off their flashlight. This output has the value of how much energy the player had when this happened [0..1]." output OnSquadMemberKilled[MBase](void) : "Fires when a member of the player's squad dies. Fires with this member as the activator." output PlayerDied(void) : "Fires when the player dies." output PlayerDamaged[MBase](void) : "Fires when the player is damaged." - output PlayerMissedAR2AltFire(void) : "Player fired an AR2 combine ball that didn't kill any enemies." + output PlayerMissedAR2AltFire[EP1, P1](void) : "Player fired an AR2 combine ball that didn't kill any enemies." output OnPlayerSpawn[MBase](void) : "Fires when the player respawns, if 'Allow SP respawn' is enabled in hl2_gamerules." - output OnStartSlowingTime[!TF2, ASW](void) : "Fired when a Portal player initiates slow time." - output OnStopSlowingTime[!TF2, ASW](void) : "Fired when a Portal player stops slowing time." + // Cut mechanic + // output OnStartSlowingTime[P2](void) : "Fired when a Portal player initiates slow time." + // output OnStopSlowingTime[P2](void) : "Fired when a Portal player stops slowing time." output OnPrimaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the primary portal." output OnSecondaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the secondary portal." - output OnCoopPing[P2](void) : "Fired when a coop ping is sent." + // Only responds to the old unused +coop_ping command, not the normal pings + output OnCoopPing[P2, +complete](void) : "Fired in response to the unused +coop_ping command." output OnDuck[since_P2](void) : "Fired when a player starts to duck." output OnUnDuck[since_P2](void) : "Fired when a player releases the duck button." output OnJump[since_P2](void) : "Fired when a player jumps." From fff32ea5e75d78ab3c5a36eb2f53865e3a3e4f3c Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 7 Jul 2023 20:42:14 -0700 Subject: [PATCH 171/243] Fix wrong starting skin for prop_glados_core --- fgd/point/prop/prop_glados_core.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/prop/prop_glados_core.fgd b/fgd/point/prop/prop_glados_core.fgd index 88468c686..1d2ea316a 100644 --- a/fgd/point/prop/prop_glados_core.fgd +++ b/fgd/point/prop/prop_glados_core.fgd @@ -23,8 +23,8 @@ 1: "Anger Core" ] - skin[engine](integer) : "Skin" : 3 - skin(choices) : "[H] Skin" : 3 : "Skin to show in Hammer." = + skin[engine](integer) : "Skin" : 0 + skin(choices) : "[H] Skin" : 0 : "Skin to show in Hammer." = [ // Ordered to match P1 boss fight 0: "Morality Core" From 7275259f8d6fa89a5d89530b2a8847d5a05fbcf2 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 7 Jul 2023 20:45:12 -0700 Subject: [PATCH 172/243] SRCModel needs to be P2 only --- fgd/bases/SRCModel.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/bases/SRCModel.fgd b/fgd/bases/SRCModel.fgd index 9a845c1e5..93e16a8c9 100644 --- a/fgd/bases/SRCModel.fgd +++ b/fgd/bases/SRCModel.fgd @@ -1,4 +1,4 @@ -@BaseClass appliesto(srctools) +@BaseClass appliesto(+P2, +srctools) = SRCModel: "Adds keyvalues to set a custom model." [ comp_custom_model_type[srctools, -engine](boolean) : "[COMP] Override Model" : 0 : "If enabled, automatically pack a VScript to override the entity's model on spawn."+ From 0bc0efb02b04865c0a2980f1756d342bf870bc3b Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 7 Jul 2023 21:03:31 -0700 Subject: [PATCH 173/243] Further player proxy changes --- fgd/point/logic/logic_playerproxy.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/logic/logic_playerproxy.fgd b/fgd/point/logic/logic_playerproxy.fgd index 80307f532..f1b8cd874 100644 --- a/fgd/point/logic/logic_playerproxy.fgd +++ b/fgd/point/logic/logic_playerproxy.fgd @@ -4,7 +4,7 @@ color(200 0 0) iconsprite("editor/logic_playerproxy.vmt") autovis(Point Entities, Globals, Player Proxy) - appliesto(!L4D, !L4D2, !TF2) + appliesto(HL2, P1, P2) // Also in Alien Swarm and CS:GO, but it doesn't work in those games = logic_playerproxy: "An entity that is used to relay inputs/outputs to the player and back to the world." [ SetMaxInputArmor[MBase](integer) : "Max Input Armor" : 100 : "Sets the maximum armor a player can receive from the " + @@ -69,7 +69,7 @@ output OnSquadMemberKilled[MBase](void) : "Fires when a member of the player's squad dies. Fires with this member as the activator." - output PlayerDied(void) : "Fires when the player dies." + output PlayerDied[EP1, P1](void) : "Fires when the player dies." output PlayerDamaged[MBase](void) : "Fires when the player is damaged." output PlayerMissedAR2AltFire[EP1, P1](void) : "Player fired an AR2 combine ball that didn't kill any enemies." output OnPlayerSpawn[MBase](void) : "Fires when the player respawns, if 'Allow SP respawn' is enabled in hl2_gamerules." From 60743c72d81d42d5dde6dd8b6c2e4c71c279d477 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 8 Jul 2023 11:07:53 +1000 Subject: [PATCH 174/243] Log the size of the packfile --- src/hammeraddons/postcompiler.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index ede51fde8..5b05e2216 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -21,7 +21,7 @@ import re from srctools import __version__ as version_lib, conv_bool -from srctools.bsp import BSP +from srctools.bsp import BSP, BSP_LUMPS from srctools.filesys import ZipFileSystem from srctools.packlist import PackList @@ -33,6 +33,18 @@ install_depmodule_hook() +def format_bytesize(val: float) -> str: + """Add mb, gb etc suffixes to a size in bytes.""" + if val < 1024: + return f'{val} bytes' # No rounding. + val /= 1024.0 + for size in ['kB', 'mB', 'gB']: + if val <= 1024.0: + return f'{val:.3f}{size}' + val /= 1024.0 + return f'{val:.03f}tB' + + async def main(argv: List[str]) -> None: """Run the postcompiler.""" parser = argparse.ArgumentParser( @@ -343,6 +355,10 @@ def pack_callback(path: str) -> Optional[bool]: LOGGER.info('Writing BSP...') bsp_file.save() + LOGGER.info('Packfile size: {}', format_bytesize( + len(bsp_file.lumps[BSP_LUMPS.PAKFILE].data) + )) + try: from srctools.fgd import _engine_db_stats # noqa except AttributeError: From ae4a24f277d3852937d579328bc80ee65b5635e8 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 9 Jul 2023 14:44:17 +1000 Subject: [PATCH 175/243] Add OnMatched to comp_case --- fgd/point/comp/comp_case.fgd | 1 + transforms/comp_case.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/fgd/point/comp/comp_case.fgd b/fgd/point/comp/comp_case.fgd index 064d1245a..0a7c5c03f 100644 --- a/fgd/point/comp/comp_case.fgd +++ b/fgd/point/comp/comp_case.fgd @@ -65,6 +65,7 @@ output OnCase16(void) : "Fired when the input value equals the Case16 value." output OnDefault(void) : "Fired when the input value does not equal any of the Case values." output OnUsed(string) : "Fired when an input value is received, regardless of whether it matches a case." + output OnMatched(string) : "Fired when an input value matches any of the cases." @resources [] ] diff --git a/transforms/comp_case.py b/transforms/comp_case.py index d00e5780a..53ead8e59 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -40,6 +40,7 @@ def collapse_case(ctx: Context, case: Entity) -> None: out_cases: Dict[int, List[Output]] = defaultdict(list) out_default: List[Output] = [] out_used: List[Output] = [] + out_matched: List[Output] = [] for out in case.outputs: if out.output.casefold().startswith('oncase'): try: @@ -52,6 +53,8 @@ def collapse_case(ctx: Context, case: Entity) -> None: out_default.append(out) elif out.output.casefold() == 'onused': out_used.append(out) + elif out.output.casefold() == 'onmatched': + out_matched.append(out) case_params: Dict[int, str] = {} for k, v in case.items(): @@ -116,6 +119,7 @@ def compute_outputs(param: str) -> Iterator[Output]: yield from out_default return yield from out_cases[first_match] + yield from out_matched if multi_cases: # Include all matches. for match in matching: yield from out_cases[match] From 6acf0b21e977ece51d8d82149a1e1bd827028399 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 9 Jul 2023 15:34:03 +1000 Subject: [PATCH 176/243] Fix HL2 export being broken --- src/hammeraddons/unify_fgd.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index 48914fc69..fe24b1147 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -268,7 +268,7 @@ def expand_tags(tags: FrozenSet[str]) -> FrozenSet[str]: else: break - if pos > 0: + if pos != -1: exp_tags.update( 'SINCE_' + tag for tag in GAME_ORDER[:pos + 1] @@ -1013,9 +1013,13 @@ def action_export( ent.bases.remove(base) if not engine_mode: + print('Applying polyfills:') for poly_tag, polyfill in POLYFILLS: if match_tags(tags, poly_tag): + print(f' - {polyfill.__name__[10:]}: Applying') polyfill(fgd) + else: + print(f' - {polyfill.__name__[10:]}: Not required') print('Applying helpers to child entities and optimising...') for ent in fgd.entities.values(): From cb71113f1164cdae82bd0bae6c3767c1d23e0a34 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 9 Jul 2023 15:45:23 +1000 Subject: [PATCH 177/243] Implement comp_random_outputs (#209) as part of comp_case --- examples/hadd_comp_case.vmf | 1128 ++++++++++++++++++++++++++++++++-- fgd/point/comp/comp_case.fgd | 41 +- transforms/comp_case.py | 79 ++- 3 files changed, 1165 insertions(+), 83 deletions(-) diff --git a/examples/hadd_comp_case.vmf b/examples/hadd_comp_case.vmf index ef88dd51d..b8a6e16cc 100644 --- a/examples/hadd_comp_case.vmf +++ b/examples/hadd_comp_case.vmf @@ -2,7 +2,7 @@ versioninfo { "editorversion" "400" "editorbuild" "9520" - "mapversion" "52" + "mapversion" "59" "formatversion" "100" "prefab" "0" } @@ -14,13 +14,13 @@ viewsettings "bSnapToGrid" "1" "bShowGrid" "1" "bShowLogicalGrid" "0" - "nGridSpacing" "16" + "nGridSpacing" "32" "bShow3DGrid" "1" } world { "id" "1" - "mapversion" "52" + "mapversion" "59" "classname" "worldspawn" "detailmaterial" "detail/detailsprites" "detailvbsp" "detail.vbsp" @@ -260,7 +260,7 @@ world side { "id" "54" - "plane" "(384 0 128) (384 -64 128) (0 -64 128)" + "plane" "(0 -64 192) (0 0 192) (384 0 192)" "material" "TOOLS/TOOLSNODRAW" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 -1 0 0] 0.25" @@ -271,7 +271,7 @@ world side { "id" "53" - "plane" "(384 -64 0) (384 0 0) (0 0 0)" + "plane" "(0 0 0) (0 -64 0) (384 -64 0)" "material" "TOOLS/TOOLSNODRAW" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 -1 0 0] 0.25" @@ -282,7 +282,7 @@ world side { "id" "52" - "plane" "(0 0 128) (0 -64 128) (0 -64 0)" + "plane" "(0 -64 0) (0 0 0) (0 0 192)" "material" "TOOLS/TOOLSNODRAW" "uaxis" "[0 1 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -293,7 +293,7 @@ world side { "id" "51" - "plane" "(384 -64 128) (384 0 128) (384 0 0)" + "plane" "(384 0 0) (384 -64 0) (384 -64 192)" "material" "TOOLS/TOOLSNODRAW" "uaxis" "[0 1 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -304,7 +304,7 @@ world side { "id" "50" - "plane" "(384 0 128) (0 0 128) (0 0 0)" + "plane" "(0 0 0) (384 0 0) (384 0 192)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[-1 0 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -315,7 +315,7 @@ world side { "id" "49" - "plane" "(0 -64 128) (384 -64 128) (384 -64 0)" + "plane" "(384 -64 0) (0 -64 0) (0 -64 192)" "material" "TOOLS/TOOLSNODRAW" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -488,7 +488,7 @@ world side { "id" "96" - "plane" "(384 0 256) (384 -64 256) (0 -64 256)" + "plane" "(0 -64 256) (0 0 256) (384 0 256)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 -1 0 0] 0.25" @@ -499,7 +499,7 @@ world side { "id" "95" - "plane" "(384 -64 128) (384 0 128) (0 0 128)" + "plane" "(0 0 192) (0 -64 192) (384 -64 192)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 -1 0 0] 0.25" @@ -510,7 +510,7 @@ world side { "id" "94" - "plane" "(0 0 256) (0 -64 256) (0 -64 128)" + "plane" "(0 -64 192) (0 0 192) (0 0 256)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[0 1 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -521,7 +521,7 @@ world side { "id" "93" - "plane" "(384 -64 256) (384 0 256) (384 0 128)" + "plane" "(384 0 192) (384 -64 192) (384 -64 256)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[0 1 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -532,7 +532,7 @@ world side { "id" "92" - "plane" "(384 0 256) (0 0 256) (0 0 128)" + "plane" "(0 0 192) (384 0 192) (384 0 256)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -543,7 +543,7 @@ world side { "id" "91" - "plane" "(0 -64 256) (384 -64 256) (384 -64 128)" + "plane" "(384 -64 192) (0 -64 192) (0 -64 256)" "material" "TOOLS/TOOLSSKYBOX" "uaxis" "[1 0 0 0] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -5551,7 +5551,7 @@ entity "classname" "comp_case" "ctrl_type" "0" "ctrl_value" "1" - "mode" "string" + "mode" "casefold" "multiplecasesallowed" "0" "targetname" "case_random_a" connections @@ -5569,7 +5569,7 @@ entity "OnCase11" "!self,Color,128 255 64,0,-1" "OnCase12" "!self,Color,64 255 128,0,-1" } - "origin" "207.635 86.2613 8" + "origin" "184 72 8" editor { "color" "220 30 220" @@ -5601,6 +5601,7 @@ entity "OnUser2" "case_random_b,PickRandom,,0,-1" "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" } "origin" "96 16 48" editor @@ -5632,6 +5633,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "128 16 48" @@ -5664,6 +5666,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "160 16 48" @@ -5696,6 +5699,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "192 16 48" @@ -5728,6 +5732,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "224 16 48" @@ -5760,6 +5765,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "256 16 48" @@ -5792,6 +5798,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "288 16 48" @@ -5824,6 +5831,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "320 16 48" @@ -5856,6 +5864,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "352 16 48" @@ -5888,6 +5897,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "96 16 80" @@ -5920,6 +5930,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "128 16 80" @@ -5952,6 +5963,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "160 16 80" @@ -5984,6 +5996,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "192 16 80" @@ -6016,6 +6029,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "224 16 80" @@ -6048,6 +6062,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "256 16 80" @@ -6080,6 +6095,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "288 16 80" @@ -6112,6 +6128,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "320 16 80" @@ -6134,7 +6151,7 @@ entity "lip" "20" "locked_sound" "0" "movedir" "90 0 0" - "origin" "200 56 16" + "origin" "168 56 16" "renderamt" "255" "rendercolor" "255 255 255" "renderfx" "0" @@ -6155,9 +6172,9 @@ entity side { "id" "1107" - "plane" "(192 64 32) (208 64 32) (208 48 32)" + "plane" "(160 48 32) (160 64 32) (176 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 -32] 0.25" + "uaxis" "[1 0 0 96] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6166,9 +6183,9 @@ entity side { "id" "1106" - "plane" "(192 48 -7.94729e-008) (208 48 -7.94729e-008) (208 64 -7.94729e-008)" + "plane" "(160 64 0) (160 48 0) (176 48 0)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 -32] 0.25" + "uaxis" "[1 0 0 96] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6177,7 +6194,7 @@ entity side { "id" "1105" - "plane" "(192 64 32) (192 48 32) (192 48 -7.94729e-008)" + "plane" "(160 48 0) (160 64 0) (160 64 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6188,7 +6205,7 @@ entity side { "id" "1104" - "plane" "(208 64 -7.94729e-008) (208 48 -7.94729e-008) (208 48 32)" + "plane" "(176 64 0) (176 48 0) (176 48 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6199,9 +6216,9 @@ entity side { "id" "1103" - "plane" "(208 64 32) (192 64 32) (192 64 -7.94729e-008)" + "plane" "(160 64 0) (176 64 0) (176 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 -32] 0.25" + "uaxis" "[1 0 0 96] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6210,9 +6227,9 @@ entity side { "id" "1102" - "plane" "(208 48 -7.94729e-008) (192 48 -7.94729e-008) (192 48 32)" + "plane" "(176 48 0) (160 48 0) (160 48 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 -32] 0.25" + "uaxis" "[1 0 0 96] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6244,7 +6261,7 @@ entity "lip" "20" "locked_sound" "0" "movedir" "90 0 0" - "origin" "232 56 16" + "origin" "200 56 16" "renderamt" "255" "rendercolor" "255 255 255" "renderfx" "0" @@ -6265,9 +6282,9 @@ entity side { "id" "1119" - "plane" "(224 64 32) (240 64 32) (240 48 32)" + "plane" "(192 48 32) (192 64 32) (208 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 352] 0.25" + "uaxis" "[1 0 0 480] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6276,9 +6293,9 @@ entity side { "id" "1118" - "plane" "(224 48 -7.94729e-008) (240 48 -7.94729e-008) (240 64 -7.94729e-008)" + "plane" "(192 64 0) (192 48 0) (208 48 0)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 352] 0.25" + "uaxis" "[1 0 0 480] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6287,7 +6304,7 @@ entity side { "id" "1117" - "plane" "(224 64 32) (224 48 32) (224 48 -7.94729e-008)" + "plane" "(192 48 0) (192 64 0) (192 64 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6298,7 +6315,7 @@ entity side { "id" "1116" - "plane" "(240 64 -7.94729e-008) (240 48 -7.94729e-008) (240 48 32)" + "plane" "(208 64 0) (208 48 0) (208 48 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6309,9 +6326,9 @@ entity side { "id" "1115" - "plane" "(240 64 32) (224 64 32) (224 64 -7.94729e-008)" + "plane" "(192 64 0) (208 64 0) (208 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 352] 0.25" + "uaxis" "[1 0 0 480] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6320,9 +6337,9 @@ entity side { "id" "1114" - "plane" "(240 48 -7.94729e-008) (224 48 -7.94729e-008) (224 48 32)" + "plane" "(208 48 0) (192 48 0) (192 48 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 352] 0.25" + "uaxis" "[1 0 0 480] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6354,7 +6371,7 @@ entity "lip" "20" "locked_sound" "0" "movedir" "90 0 0" - "origin" "264 56 16" + "origin" "232 56 16" "renderamt" "255" "rendercolor" "255 255 255" "renderfx" "0" @@ -6375,9 +6392,9 @@ entity side { "id" "1131" - "plane" "(256 64 32) (272 64 32) (272 48 32)" + "plane" "(224 48 32) (224 64 32) (240 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 224] 0.25" + "uaxis" "[1 0 0 352] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6386,9 +6403,9 @@ entity side { "id" "1130" - "plane" "(256 48 -7.94729e-008) (272 48 -7.94729e-008) (272 64 -7.94729e-008)" + "plane" "(224 64 0) (224 48 0) (240 48 0)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 224] 0.25" + "uaxis" "[1 0 0 352] 0.25" "vaxis" "[0 -1 0 -256] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6397,7 +6414,7 @@ entity side { "id" "1129" - "plane" "(256 64 32) (256 48 32) (256 48 -7.94729e-008)" + "plane" "(224 48 0) (224 64 0) (224 64 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6408,7 +6425,7 @@ entity side { "id" "1128" - "plane" "(272 64 -7.94729e-008) (272 48 -7.94729e-008) (272 48 32)" + "plane" "(240 64 0) (240 48 0) (240 48 32)" "material" "DEV/DEV_MEASUREWALL01A" "uaxis" "[0 1 0 256] 0.25" "vaxis" "[0 0 -1 0] 0.25" @@ -6419,9 +6436,9 @@ entity side { "id" "1127" - "plane" "(272 64 32) (256 64 32) (256 64 -7.94729e-008)" + "plane" "(224 64 0) (240 64 0) (240 64 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 224] 0.25" + "uaxis" "[1 0 0 352] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6430,9 +6447,9 @@ entity side { "id" "1126" - "plane" "(272 48 -7.94729e-008) (256 48 -7.94729e-008) (256 48 32)" + "plane" "(240 48 0) (224 48 0) (224 48 32)" "material" "DEV/DEV_MEASUREWALL01A" - "uaxis" "[1 0 0 224] 0.25" + "uaxis" "[1 0 0 352] 0.25" "vaxis" "[0 0 -1 0] 0.25" "rotation" "0" "lightmapscale" "16" @@ -6474,6 +6491,7 @@ entity { "OnUser1" "case_random_a,PickRandom,,0,-1" "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" "OnUser3" "case_random_a,PickRandom,,0,-1" } "origin" "352 16 80" @@ -6491,7 +6509,7 @@ entity "classname" "comp_case" "ctrl_type" "0" "ctrl_value" "1" - "mode" "string" + "mode" "casefold" "multiplecasesallowed" "0" "targetname" "case_random_b" connections @@ -6509,16 +6527,1020 @@ entity "OnCase11" "!self,Color,128 255 64,0,-1" "OnCase12" "!self,Color,64 255 128,0,-1" } - "origin" "239.635 86.2613 8" + "origin" "216 72 8" editor { "color" "220 30 220" "visgroupshown" "1" "visgroupautoshown" "1" - "comments" "Each sprite fires PickRandom, so they get random colours added for the OnUserX outputs. The choice is consistent across compiles though." + "comments" "Each sprite fires PickRandom, so they get random colours added for the OnUserX outputs. The choice is consistent across compiles though. " "logicalpos" "[0 10000]" } } +entity +{ + "id" "5672" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_19" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "352 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5676" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_20" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "320 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5680" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_21" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "288 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5684" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_22" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "256 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5688" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_23" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "224 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5692" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_24" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "192 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5696" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_25" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "160 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5700" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_26" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "128 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5704" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_27" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "96 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "5860" + "classname" "comp_case" + "case01" "5" + "case02" "5" + "case03" "5" + "case04" "1" + "case05" "1" + "case06" "1" + "case07" "0.1" + "ctrl_type" "0" + "ctrl_value" "1" + "mode" "randweight" + "multiplecasesallowed" "0" + "targetname" "case_random_c" + connections + { + "OnCase01" "!self,Color,255 0 0,0,-1" + "OnCase02" "!self,Color,0 255 0,0,-1" + "OnCase03" "!self,Color,0 0 255,0,-1" + "OnCase06" "!self,Color,255 255 0,0,-1" + "OnCase05" "!self,Color,255 0 255,0,-1" + "OnCase04" "!self,Color,0 255 255,0,-1" + "OnCase07" "!self,Color,255 255 255,0,-1" + } + "origin" "280 72 8" + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "comments" "This demonstrates weighted randomisation." + "logicalpos" "[0 10000]" + } +} +entity +{ + "id" "5910" + "classname" "func_button" + "_minlight" "0" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "health" "0" + "lip" "20" + "locked_sound" "0" + "movedir" "90 0 0" + "origin" "280 56 16" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "0" + "sounds" "0" + "spawnflags" "1024" + "speed" "20" + "unlocked_sound" "0" + "vrad_brush_cast_shadows" "1" + "wait" "0.5" + connections + { + "OnPressed" "spr_rand_*,FireUser4,,0,-1" + } + solid + { + "id" "5911" + side + { + "id" "1215" + "plane" "(272 48 32) (272 64 32) (288 64 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 160] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1214" + "plane" "(272 64 0) (272 48 0) (288 48 0)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 160] 0.25" + "vaxis" "[0 -1 0 -256] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1213" + "plane" "(272 48 0) (272 64 0) (272 64 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1212" + "plane" "(288 64 0) (288 48 0) (288 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[0 1 0 256] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1211" + "plane" "(272 64 0) (288 64 0) (288 64 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 160] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + side + { + "id" "1210" + "plane" "(288 48 0) (272 48 0) (272 48 32)" + "material" "DEV/DEV_MEASUREWALL01A" + "uaxis" "[1 0 0 160] 0.25" + "vaxis" "[0 0 -1 0] 0.25" + "rotation" "0" + "lightmapscale" "16" + "smoothing_groups" "0" + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + } + } + editor + { + "color" "220 30 220" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 2500]" + } +} +entity +{ + "id" "6161" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_28" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "96 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6165" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_29" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "128 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6169" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_30" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "160 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6173" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_31" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "192 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6177" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_32" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "224 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6181" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_33" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "256 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6185" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_34" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "288 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6189" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_35" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "320 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6193" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_36" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "352 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6214" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_37" + connections + { + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + } + "origin" "32 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6218" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_38" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "32 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6222" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_39" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "32 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6226" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_40" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "32 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6231" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_41" + connections + { + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + } + "origin" "64 16 48" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6235" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_42" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "64 16 80" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6239" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_43" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "64 16 112" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} +entity +{ + "id" "6243" + "classname" "env_sprite" + "angles" "0 0 0" + "disablereceiveshadows" "0" + "framerate" "10.0" + "glowproxysize" "20" + "hdrcolorscale" "0.7" + "model" "sprites/glow01.vmt" + "renderamt" "255" + "rendercolor" "255 255 255" + "renderfx" "0" + "rendermode" "9" + "scale" "0.75" + "spawnflags" "1" + "targetname" "spr_rand_44" + connections + { + "OnUser1" "case_random_a,PickRandom,,0,-1" + "OnUser2" "case_random_b,PickRandom,,0,-1" + "OnUser4" "case_random_c,PickRandom,,0,-1" + "OnUser3" "case_random_a,PickRandom,,0,-1" + } + "origin" "64 16 144" + editor + { + "color" "20 140 20" + "visgroupshown" "1" + "visgroupautoshown" "1" + "logicalpos" "[0 1500]" + } +} cameras { "activecamera" "-1" diff --git a/fgd/point/comp/comp_case.fgd b/fgd/point/comp/comp_case.fgd index 0a7c5c03f..8b3d55cf2 100644 --- a/fgd/point/comp/comp_case.fgd +++ b/fgd/point/comp/comp_case.fgd @@ -15,35 +15,37 @@ mode[engine](string) : "Mode" : "string" mode(choices) : "Mode" : "casefold" : "Specifies how comparisons are performed. Text mode simply checks for a case that matches the input text. " + - "Numeric treats values as numbers, allowing cases to additionally specify a comparison like '< 3.14'. In all modes, each case is compared in order." = + "Numeric treats values as numbers, allowing cases to additionally specify a comparison like '< 3.14'. Weighted Random instead changes the case values to specify the chance to produce any given case. In all modes, each case is compared in order." = [ "string" : "Text - Case Sensitive" "casefold" : "Text - Case Insensitive" "numeric" : "Numeric" + "randweight" : "Weighted Random" ] seed(string) : "Random Seed" : : "For the PickRandom input, the position and name of the input entity and the case are used to seed a random number generator. This can be set to further randomise the chosen case." + misschance(float) : "Miss Chance (%)" : 0 : "If nonzero, PickRandom will skip picking cases entirely this often. Only OnUsed and OnMissed will be fired." - case01(string) : "Case 01" : : "Fires OnCase01 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case02(string) : "Case 02" : : "Fires OnCase02 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case03(string) : "Case 03" : : "Fires OnCase03 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case04(string) : "Case 04" : : "Fires OnCase04 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case05(string) : "Case 05" : : "Fires OnCase05 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case06(string) : "Case 06" : : "Fires OnCase06 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case07(string) : "Case 07" : : "Fires OnCase07 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case08(string) : "Case 08" : : "Fires OnCase08 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case09(string) : "Case 09" : : "Fires OnCase09 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case10(string) : "Case 10" : : "Fires OnCase10 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case11(string) : "Case 11" : : "Fires OnCase11 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case12(string) : "Case 12" : : "Fires OnCase12 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case13(string) : "Case 13" : : "Fires OnCase13 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case14(string) : "Case 14" : : "Fires OnCase14 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case15(string) : "Case 15" : : "Fires OnCase15 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." - case16(string) : "Case 16" : : "Fires OnCase16 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value." + case01(string) : "Case 01" : : "Fires OnCase01 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case02(string) : "Case 02" : : "Fires OnCase02 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case03(string) : "Case 03" : : "Fires OnCase03 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case04(string) : "Case 04" : : "Fires OnCase04 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case05(string) : "Case 05" : : "Fires OnCase05 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case06(string) : "Case 06" : : "Fires OnCase06 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case07(string) : "Case 07" : : "Fires OnCase07 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case08(string) : "Case 08" : : "Fires OnCase08 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case09(string) : "Case 09" : : "Fires OnCase09 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case10(string) : "Case 10" : : "Fires OnCase10 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case11(string) : "Case 11" : : "Fires OnCase11 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case12(string) : "Case 12" : : "Fires OnCase12 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case13(string) : "Case 13" : : "Fires OnCase13 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case14(string) : "Case 14" : : "Fires OnCase14 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case15(string) : "Case 15" : : "Fires OnCase15 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." + case16(string) : "Case 16" : : "Fires OnCase16 if the InValue parameter matches this value. In Numeric mode, comparison operators such as <, >, =, !=, >=, or <= may be put at the start of the value. In Weighted Random mode, this instead is the chance that this case will be picked." // Inputs - input InValue(string) : "Replaced by whichever case matches the parameter." - input Trigger(void) : "Replaced by whichever case matches the input keyvalue." + input InValue(string) : "Replaced by whichever case matches the parameter. Does nothing in Weighted Random mode." + input Trigger(void) : "Replaced by whichever case matches the input keyvalue. In Weighted Random mode, behaves like PickRandom." input PickRandom(void) : "Replaced by a random case with outputs defined." // Outputs @@ -66,6 +68,7 @@ output OnDefault(void) : "Fired when the input value does not equal any of the Case values." output OnUsed(string) : "Fired when an input value is received, regardless of whether it matches a case." output OnMatched(string) : "Fired when an input value matches any of the cases." + output OnMissed(string) : "Fired when the miss chance succeeded." @resources [] ] diff --git a/transforms/comp_case.py b/transforms/comp_case.py index 53ead8e59..466dfaa97 100644 --- a/transforms/comp_case.py +++ b/transforms/comp_case.py @@ -1,4 +1,5 @@ """comp_case is a compile-time collapsible version of logic_case.""" +import math from typing import Dict, Iterator, List, Tuple import hashlib @@ -8,7 +9,7 @@ from decimal import Decimal from collections import defaultdict -from srctools import Entity, Output, conv_bool +from srctools import Entity, Output, conv_bool, conv_float from srctools.math import parse_vec_str from srctools.logger import get_logger @@ -29,7 +30,7 @@ def collapse_case(ctx: Context, case: Entity) -> None: case_name = case['targetname'] mode = case['mode'].casefold() default_value = case['value'] - + miss_chance = conv_float(case['misschance'], 0.0) / 100.0 desc = f'for comp_case "{case_name}" @ ({case["origin"]})' hasher_template = hashlib.sha512() @@ -41,6 +42,7 @@ def collapse_case(ctx: Context, case: Entity) -> None: out_default: List[Output] = [] out_used: List[Output] = [] out_matched: List[Output] = [] + out_missed: List[Output] = [] for out in case.outputs: if out.output.casefold().startswith('oncase'): try: @@ -55,6 +57,8 @@ def collapse_case(ctx: Context, case: Entity) -> None: out_used.append(out) elif out.output.casefold() == 'onmatched': out_matched.append(out) + elif out.output.casefold() == 'onmissed': + out_missed.append(out) case_params: Dict[int, str] = {} for k, v in case.items(): @@ -66,6 +70,13 @@ def collapse_case(ctx: Context, case: Entity) -> None: continue case_params[num] = v + def make_rng(source: Entity) -> random.Random: + """Create a seeded RNG, based on the input source.""" + hasher = hasher_template.copy() + hasher.update((source['targetname'] or source['classname']).encode('utf8')) + hasher.update(struct.pack(' Iterator[int]: for (operation, num_b), case_num in numeric_cases: if operation(num_a, num_b): yield case_num + elif mode == 'randweight': # Weighted Random, rather different. + warned_rand_weight = False + weight_outs: List[List[Output]] = [ + # Pre-concatenate, so we don't have to do it each time. + [*out_cases[case_num], *out_used, *out_matched] + for case_num in key_out + ] + cur_val = 0.0 + cum_weights: List[float] = [ + cur_val := cur_val + conv_float(case_params.get(case_num, 0.0)) + for case_num in key_out + ] + # If we missed, it was used too. + out_missed += out_used + + def handle_rand_weight(source: Entity, out: Output) -> List[Output]: + """Pick a weighted-random case.""" + rng = make_rng(source) + if 0.0 < miss_chance < rng.random(): + return out_missed + [chosen] = make_rng(source).choices(weight_outs, cum_weights=cum_weights) + return chosen + + def warn_rand_weight(source: Entity, out: Output) -> List[Output]: + """Warn about invalid use of InValue.""" + nonlocal warned_rand_weight + if not warned_rand_weight: + warned_rand_weight = True + LOGGER.warning( + '{} @ ({}) fired InValue input to comp_case "{}" @ ({}), which is in ' + 'weighted random mode! Use PickRandom instead, parameters are ignored.', + source['targetname'] or source['classname'], + source['origin'], + case_name, case['origin'], + ) + return [] + + ctx.add_io_remap_func(case_name, 'InValue', warn_rand_weight) + ctx.add_io_remap_func(case_name, 'Trigger', handle_rand_weight) + ctx.add_io_remap_func(case_name, 'PickRandom', handle_rand_weight) + return else: LOGGER.error( 'Invalid mode "{}" for comp_case "{}" @ ({})', @@ -108,10 +160,15 @@ def find_matches(param: str) -> Iterator[int]: ) return - def compute_outputs(param: str) -> Iterator[Output]: + def compute_outputs(source: Entity, param: str) -> Iterator[Output]: """Compute the matching cases, then yield the outputs.""" - matching = find_matches(param) yield from out_used # Always used. + + if 0.0 < miss_chance < make_rng(source).random(): + yield from out_missed + return + + matching = find_matches(param) try: first_match = next(matching) except StopIteration: @@ -126,19 +183,19 @@ def compute_outputs(param: str) -> Iterator[Output]: def handle_pick_random(source: Entity, out: Output) -> List[Output]: """Handle the PickRandom input.""" - hasher = hasher_template.copy() - hasher.update((source['targetname'] or source['classname']).encode('utf8')) - hasher.update(struct.pack(' Date: Mon, 10 Jul 2023 15:25:09 +1000 Subject: [PATCH 178/243] Implement #221: "[HA]" label to postcompiler-specific keyvalues --- CHANGELOG.md | 1 + fgd/base_entity.fgd | 2 +- fgd/bases/BaseEntity.fgd | 4 ++-- fgd/bases/BaseEntityBrush.fgd | 6 +++--- fgd/bases/BaseEntityPoint.fgd | 6 +++--- fgd/bases/DamageType.fgd | 8 ++++---- fgd/bases/SRCIndicator.fgd | 2 +- fgd/bases/SRCModel.fgd | 4 ++-- fgd/bases/SetSkin.fgd | 2 +- fgd/point/material_modify_control.fgd | 2 +- fgd/point/npc/npc_personality_core.fgd | 2 +- fgd/point/npc/npc_rocket_turret.fgd | 2 +- fgd/point/prop/prop_button.fgd | 2 +- fgd/point/prop/prop_exploding_futbol.fgd | 2 +- fgd/point/prop/prop_floor_ball_button.fgd | 2 +- fgd/point/prop/prop_floor_cube_button.fgd | 2 +- fgd/point/prop/prop_glados_core.fgd | 2 +- fgd/point/prop/prop_glass_futbol.fgd | 2 +- fgd/point/prop/prop_laser_catcher.fgd | 2 +- fgd/point/prop/prop_under_button.fgd | 2 +- fgd/point/prop/prop_under_floor_button.fgd | 2 +- fgd/point/prop/prop_wall_projector.fgd | 2 +- fgd/point/prop/prop_weighted_cube.fgd | 2 +- fgd/point/team/team_control_point.fgd | 2 +- 24 files changed, 33 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1a24203..31ed9a2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ * Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. * Set Precache/OnPostSpawn in generated VScript, to prevent double-firing functions already in Entity Scripts. * Allow physboxes to completely override their mass. +* The postcompiler can now automatically handle custom models for various Portal 2 entities. -------------------- diff --git a/fgd/base_entity.fgd b/fgd/base_entity.fgd index 7192e2067..6f5f8e090 100644 --- a/fgd/base_entity.fgd +++ b/fgd/base_entity.fgd @@ -39,7 +39,7 @@ fadescale(float) : "Fade Scale": 0 parentname(target_destination) : "Parent" - parent_attachment_point[srctools](string) : "Attachment Point" + parent_attachment_point[srctools](string) : "[HA] Attachment Point" responseContext(string) : "Response Context" : "" addon(string) : "AI Addon" : : "Broken ASW feature." diff --git a/fgd/bases/BaseEntity.fgd b/fgd/bases/BaseEntity.fgd index 3767e5e2b..8e5171af2 100644 --- a/fgd/bases/BaseEntity.fgd +++ b/fgd/bases/BaseEntity.fgd @@ -9,8 +9,8 @@ vscripts[VSCRIPT](scriptlist) : "Entity Scripts" : : "Name(s) of script files that are executed after all entities have spawned." thinkfunction[VSCRIPT](string) : "Script think function" : : "Name of a function in this entity's script scope which will be called automatically." - vscript_init_code[+VSCRIPT, +srctools](string) : "Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." - vscript_init_code2[+VSCRIPT, +srctools](string) : "Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + + vscript_init_code[+VSCRIPT, +srctools](string) : "[HA] Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." + vscript_init_code2[+VSCRIPT, +srctools](string) : "[HA] Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + "Additional Init Code keyvalues can be added with SmartEdit off." // etc diff --git a/fgd/bases/BaseEntityBrush.fgd b/fgd/bases/BaseEntityBrush.fgd index 5836a610d..16b8ad3fd 100644 --- a/fgd/bases/BaseEntityBrush.fgd +++ b/fgd/bases/BaseEntityBrush.fgd @@ -9,15 +9,15 @@ parentname[!srctools](target_destination) : "Parent" : : "The name of this entity's parent in the movement hierarchy. Entities with parents move with their parent. Set an attachment point via 'parentname,attachment'." parentname[srctools](target_destination) : "Parent" : : "The name of this entity's parent in the movement hierarchy. Entities with parents move with their parent." - parent_attachment_point[srctools](string) : "Attachment Point" : : "If set, attach to this attachment point on the parent during spawn." + parent_attachment_point[srctools](string) : "[HA] Attachment Point" : : "If set, attach to this attachment point on the parent during spawn." linedivider_vscript[+VSCRIPT, -engine](string) readonly : "-------------------------------------------------------------------------------------------------------" : "" vscripts[VSCRIPT](scriptlist) : "Entity Scripts" : : "Name(s) of script files that are executed after all entities have spawned." thinkfunction[VSCRIPT](string) : "Script think function" : : "Name of a function in this entity's script scope which will be called automatically." - vscript_init_code[+VSCRIPT, +srctools](string) : "Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." - vscript_init_code2[+VSCRIPT, +srctools](string) : "Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + + vscript_init_code[+VSCRIPT, +srctools](string) : "[HA] Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." + vscript_init_code2[+VSCRIPT, +srctools](string) : "[HA] Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + "Additional Init Code keyvalues can be added with SmartEdit off." // etc diff --git a/fgd/bases/BaseEntityPoint.fgd b/fgd/bases/BaseEntityPoint.fgd index e0911b160..0a7d54bf8 100644 --- a/fgd/bases/BaseEntityPoint.fgd +++ b/fgd/bases/BaseEntityPoint.fgd @@ -12,15 +12,15 @@ parentname[!srctools](target_destination) : "Parent" : : "The name of this entity's parent in the movement hierarchy. Entities with parents move with their parent. Set an attachment point via 'parentname,attachment'." parentname[srctools](target_destination) : "Parent" : : "The name of this entity's parent in the movement hierarchy. Entities with parents move with their parent." - parent_attachment_point[srctools](string) : "Attachment Point" : : "If set, attach to this attachment point on the parent during spawn." + parent_attachment_point[srctools](string) : "[HA] Attachment Point" : : "If set, attach to this attachment point on the parent during spawn." linedivider_vscript[+VSCRIPT, !engine](string) readonly : "-------------------------------------------------------------------------------------------------------" : "" vscripts[VSCRIPT](scriptlist) : "Entity Scripts" : : "Name(s) of script files that are executed after all entities have spawned." thinkfunction[VSCRIPT](string) : "Script think function" : : "Name of a function in this entity's script scope which will be called automatically." - vscript_init_code[+VSCRIPT, +srctools](string) : "Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." - vscript_init_code2[+VSCRIPT, +srctools](string) : "Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + + vscript_init_code[+VSCRIPT, +srctools](string) : "[HA] Init Code" : : "This code will be executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings." + vscript_init_code2[+VSCRIPT, +srctools](string) : "[HA] Init Code 2" : : "This code will be the second line executed after the Entity Scripts option. Backtick ( ` ) characters will be converted to quotes in-game for strings. " + "Additional Init Code keyvalues can be added with SmartEdit off." // etc diff --git a/fgd/bases/DamageType.fgd b/fgd/bases/DamageType.fgd index 321fa6162..d214a74be 100644 --- a/fgd/bases/DamageType.fgd +++ b/fgd/bases/DamageType.fgd @@ -60,23 +60,23 @@ damageor3[engine](integer) : "Damage Or" : 0 damageor4[engine](integer) : "Damage Or" : 0 - damageor1[srctools](choices) : "Damage - Gibbing" : 0 : "Optional flags that can accompany the damage type." = + damageor1[srctools](choices) : "[HA] Damage - Gibbing" : 0 : "Optional flags that can accompany the damage type." = [ 0 : "Normal Behaviour" 4096 : "Never use gibs" 8192 : "Always gib if possible" ] - damageor2[srctools](choices) : "Damage - Prevent Physics Force" : 0 : "Prevent applying physics force to the target." = + damageor2[srctools](choices) : "[HA] Damage - Prevent Physics Force" : 0 : "Prevent applying physics force to the target." = [ 0 : "Apply force" 2048 : "Prevent force" ] - damageor3[srctools](choices) : "Damage - No Ragdoll On Death" : 0 : "Prevent any ragdoll on death" = + damageor3[srctools](choices) : "[HA] Damage - No Ragdoll On Death" : 0 : "Prevent any ragdoll on death" = [ 0 : "Allow ragdolls" 4194304 : "Prevent ragdolls" ] - damageor4[srctools](choices) : "Damage - Blast Surface" : 0 : "This is ignored by players when fully underwater." = + damageor4[srctools](choices) : "[HA] Damage - Blast Surface" : 0 : "This is ignored by players when fully underwater." = [ 0 : "Damage underwater" 134217728 : "Damage only above the surface" diff --git a/fgd/bases/SRCIndicator.fgd b/fgd/bases/SRCIndicator.fgd index 9539f096e..b03b43450 100644 --- a/fgd/bases/SRCIndicator.fgd +++ b/fgd/bases/SRCIndicator.fgd @@ -3,6 +3,6 @@ appliesto(srctools) = SRCIndicator: "Adds an Indicator Name option to toggle overlays." [ - indicatorname(target_destination) : "Indicator Name" : : "Set to the name of a set of info_overlays to toggle when this is activated and deactivated. The name may also point to a prop_indicator_panel, which will also be toggled appropriately." + indicatorname(target_destination) : "[HA] Indicator Name" : : "Set to the name of a set of info_overlays to toggle when this is activated and deactivated. The name may also point to a prop_indicator_panel, which will also be toggled appropriately." input SetTextureIndex(integer) : "Manually change the index of the overlays. prop_indicator_panels must not be used." ] diff --git a/fgd/bases/SRCModel.fgd b/fgd/bases/SRCModel.fgd index 93e16a8c9..a492572e2 100644 --- a/fgd/bases/SRCModel.fgd +++ b/fgd/bases/SRCModel.fgd @@ -1,7 +1,7 @@ @BaseClass appliesto(+P2, +srctools) = SRCModel: "Adds keyvalues to set a custom model." [ - comp_custom_model_type[srctools, -engine](boolean) : "[COMP] Override Model" : 0 : "If enabled, automatically pack a VScript to override the entity's model on spawn."+ + comp_custom_model_type[srctools, -engine](boolean) : "[HA] Override Model" : 0 : "If enabled, automatically pack a VScript to override the entity's model on spawn."+ " NOTE: If you run into problems with players getting stuck on the model, make sure it has the same collisions as the base model,"+ " as this is known to sometimes cause issues." -] \ No newline at end of file +] diff --git a/fgd/bases/SetSkin.fgd b/fgd/bases/SetSkin.fgd index c075bfbff..627dec0f0 100644 --- a/fgd/bases/SetSkin.fgd +++ b/fgd/bases/SetSkin.fgd @@ -3,6 +3,6 @@ [ skin(integer) : "Skin" : 0 : "Some models have multiple versions of their textures, called skins. " + "Set this to a number other than 0 to use that skin instead of the default." - skinset[srctools](string) : "Used Skins" : : "Set this to a space seperated list of all the skin numbers which will be used by this ent ('0 4 8' for example). " + + skinset[srctools](string) : "[HA] Used Skins" : : "Set this to a space seperated list of all the skin numbers which will be used by this ent ('0 4 8' for example). " + "This allows auto-packing to skip unused ones. If blank all skins are assumed to be used." ] diff --git a/fgd/point/material_modify_control.fgd b/fgd/point/material_modify_control.fgd index 93da48d7b..f28879b00 100644 --- a/fgd/point/material_modify_control.fgd +++ b/fgd/point/material_modify_control.fgd @@ -8,7 +8,7 @@ [ parentname(target_destination) : "Parent / Target" : : "Materials are modified only on the parent of the material_modify_control." - srctools_search_parent[+srctools](boolean) : "Search Parent" : : "If set, the postcompiler will duplicate this entity for each material on the parent that have the appropriate proxy. " + + srctools_search_parent[+srctools](boolean) : "[HA] Search Parent" : : "If set, the postcompiler will duplicate this entity for each material on the parent that have the appropriate proxy. " + "If Material Name is set, only materials containing that value will be considered." materialname(material): "Material Name" : : "The single material to modify on the parent. 'materials/' and '.vmt' is not required. Maximum of 255 characters allowed." diff --git a/fgd/point/npc/npc_personality_core.fgd b/fgd/point/npc/npc_personality_core.fgd index 440430e8b..e77995ad5 100644 --- a/fgd/point/npc/npc_personality_core.fgd +++ b/fgd/point/npc/npc_personality_core.fgd @@ -25,7 +25,7 @@ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" ] - model[+srctools](choices) : "Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins. Alternatively, if Override Model is enabled you can type/paste in a custom model path here." = + model[+srctools](choices) : "[HA] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins. Alternatively, if Override Model is enabled you can type/paste in a custom model path here." = [ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" diff --git a/fgd/point/npc/npc_rocket_turret.fgd b/fgd/point/npc/npc_rocket_turret.fgd index 002641182..e26f1d44b 100644 --- a/fgd/point/npc/npc_rocket_turret.fgd +++ b/fgd/point/npc/npc_rocket_turret.fgd @@ -18,7 +18,7 @@ ] model[P2, -srctools](studio) : "[H] Model" : "models/props_bts/rocket_sentry.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[P2, +srctools](studio) : "Custom Model" : "models/props_bts/rocket_sentry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[P2, +srctools](studio) : "[HA] Custom Model" : "models/props_bts/rocket_sentry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." rocketspeed[P2](float) : "Rocket Speed" : 450 : "Speed the rocket will travel at." rocketlifetime[P2](float) : "Rocket Lifetime" : 20 : "The rocket will automatically detonate after this number of seconds." diff --git a/fgd/point/prop/prop_button.fgd b/fgd/point/prop/prop_button.fgd index 7fbba6fb0..c30d53b61 100644 --- a/fgd/point/prop/prop_button.fgd +++ b/fgd/point/prop/prop_button.fgd @@ -6,7 +6,7 @@ = prop_button: "A button which is activated by player use or by game inputs. While pressed it can play a tick-tock sound to indicate limited time." [ model[-srctools](studio) : "[H] Model" : "models/props/switch001.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props/switch001.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/switch001.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." skin[engine](integer) : "Skin" : 0 : "Should it appear dirty or clean?" skin(choices) : "Skin" : "0" : "Should it appear dirty or clean?" = diff --git a/fgd/point/prop/prop_exploding_futbol.fgd b/fgd/point/prop/prop_exploding_futbol.fgd index f9b9e9fef..fdba3ebe3 100644 --- a/fgd/point/prop/prop_exploding_futbol.fgd +++ b/fgd/point/prop/prop_exploding_futbol.fgd @@ -4,7 +4,7 @@ = prop_exploding_futbol: "The bombs used by Wheatley." [ model[-srctools](studio) : "[H] Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." explodeontouch(boolean) : "Explode on touch" : 1 : "If the bomb should explode when it touches something." diff --git a/fgd/point/prop/prop_floor_ball_button.fgd b/fgd/point/prop/prop_floor_ball_button.fgd index 00b3e7193..f5b920a5f 100644 --- a/fgd/point/prop/prop_floor_ball_button.fgd +++ b/fgd/point/prop/prop_floor_ball_button.fgd @@ -5,7 +5,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/props/ball_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props/ball_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/ball_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." @resources [ model "models/props/ball_button.mdl" diff --git a/fgd/point/prop/prop_floor_cube_button.fgd b/fgd/point/prop/prop_floor_cube_button.fgd index 108a92e86..b89ae7fe7 100644 --- a/fgd/point/prop/prop_floor_cube_button.fgd +++ b/fgd/point/prop/prop_floor_cube_button.fgd @@ -5,7 +5,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/props/box_socket.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props/box_socket.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/box_socket.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." acceptsball(boolean) : "Accepts Balls" : 0 : "Do Edgeless Safety Cubes activate this? Should almost always be No." @resources diff --git a/fgd/point/prop/prop_glados_core.fgd b/fgd/point/prop/prop_glados_core.fgd index 1d2ea316a..347540d34 100644 --- a/fgd/point/prop/prop_glados_core.fgd +++ b/fgd/point/prop/prop_glados_core.fgd @@ -11,7 +11,7 @@ "Portal 2 uses the wrong core model, so this will need to be swapped back with VScript." [ model[P2, -srctools](studio) : "[H] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[P2, +srctools](studio) : "Custom Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[P2, +srctools](studio) : "[HA] Custom Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." coretype[engine](integer) : "Core Personality" : 3 coretype(choices) : "Core Personality" : 3 : "Which personality the core is set to, determines the voice lines and skin." = diff --git a/fgd/point/prop/prop_glass_futbol.fgd b/fgd/point/prop/prop_glass_futbol.fgd index 10e70f095..18d7afbed 100644 --- a/fgd/point/prop/prop_glass_futbol.fgd +++ b/fgd/point/prop/prop_glass_futbol.fgd @@ -4,7 +4,7 @@ line(255 255 0, targetname, spawnername) = prop_glass_futbol: "A fragile glass ball that the player can pick up and toss. On contact with surfaces it will shatter, and it can be put into holders to power them. It is affected by gel, but the shattering means this has little effect." [ model[-srctools](studio) : "[H] Model" : "models/props/futbol.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." spawnername(target_destination) : "Spawner Name" : : "Name of prop_glass_futbol_spawner for this futbol to respawn in once broken." diff --git a/fgd/point/prop/prop_laser_catcher.fgd b/fgd/point/prop/prop_laser_catcher.fgd index 7382953d3..93b25e32b 100644 --- a/fgd/point/prop/prop_laser_catcher.fgd +++ b/fgd/point/prop/prop_laser_catcher.fgd @@ -26,7 +26,7 @@ "models/props/laser_catcher.mdl": "Offset" ] - src_fix_skins[srctools](boolean) : "Fix Skins Logic" : 1 : "If set, add extra outputs to fix the bug with skins not changing after a reload from save." + src_fix_skins[srctools](boolean) : "[HA] Fix Skins Logic" : 1 : "If set, add extra outputs to fix the bug with skins not changing after a reload from save." // Inputs input Skin(integer) : "Set the skin of the catcher manually." diff --git a/fgd/point/prop/prop_under_button.fgd b/fgd/point/prop/prop_under_button.fgd index 5e2aab489..71cfe5f64 100644 --- a/fgd/point/prop/prop_under_button.fgd +++ b/fgd/point/prop/prop_under_button.fgd @@ -8,7 +8,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." @resources [ diff --git a/fgd/point/prop/prop_under_floor_button.fgd b/fgd/point/prop/prop_under_floor_button.fgd index f58a8f248..12ddb43f9 100644 --- a/fgd/point/prop/prop_under_floor_button.fgd +++ b/fgd/point/prop/prop_under_floor_button.fgd @@ -7,7 +7,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." // Outputs output OnPressedBlue(void) : "Called in Coop when the button has been pressed by ATLAS." diff --git a/fgd/point/prop/prop_wall_projector.fgd b/fgd/point/prop/prop_wall_projector.fgd index b029cb69f..90ea848a8 100644 --- a/fgd/point/prop/prop_wall_projector.fgd +++ b/fgd/point/prop/prop_wall_projector.fgd @@ -6,7 +6,7 @@ "Note: To avoid lag when it first turns on, place an info_particle_system to precache 'projected_wall_impact'." [ model[-srctools](studio) : "[H] Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." - model[+srctools](studio) : "Custom Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." skin[engine](integer) : "Skin" : 0 skin(choices) : "Skin" : 0 : "Which skin to use." = diff --git a/fgd/point/prop/prop_weighted_cube.fgd b/fgd/point/prop/prop_weighted_cube.fgd index db23c10ea..74f731ca8 100644 --- a/fgd/point/prop/prop_weighted_cube.fgd +++ b/fgd/point/prop/prop_weighted_cube.fgd @@ -30,7 +30,7 @@ "models/props_underground/underground_weighted_cube.mdl": "Antique" ] - comp_custom_model_type[srctools, -engine](choices) : "[COMP] Custom Model Type" : 0 : "Automatically handles various methods of setting custom cube models. " + + comp_custom_model_type[srctools, -engine](choices) : "[HA] Custom Model Type" : 0 : "Automatically handles various methods of setting custom cube models. " + "Cube Type sets the behavior and which skin numbers are used for gel, and the Model keyvalue sets the actual model. " + "Script Override mode requires the collisions to be the same as the base cube type, but tends to produce more correct physics than Cube Type 6." = [ diff --git a/fgd/point/team/team_control_point.fgd b/fgd/point/team/team_control_point.fgd index 83e999468..dc2faa901 100644 --- a/fgd/point/team/team_control_point.fgd +++ b/fgd/point/team/team_control_point.fgd @@ -66,7 +66,7 @@ team_previouspoint_3_2(target_source) : "BLU Previous Required Point 3" : : "The name of a third previous capture point that BLU must own to be able to capture this point." model(studio) : "[H] Model" : "models/effects/cappoint_hologram.mdl" : "Model to show in Hammer" - src_propname[srctools](target_destination) : "Linked Prop" : : "Set to the name of a prop that should change its skin based on who owns the point (such as the control point base model)" + src_propname[srctools](target_destination) : "[HA] Linked Prop" : : "Set to the name of a prop that should change its skin based on who owns the point (such as the control point base model)" // Inputs input SetOwner(integer) : "Set the owner of the point." From d7fccfcff28c578616820718566ee50a4f92b7c9 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Jul 2023 02:46:17 -0700 Subject: [PATCH 179/243] worldspawn FGD cleanup --- fgd/worldspawn.fgd | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fgd/worldspawn.fgd b/fgd/worldspawn.fgd index 8fdb09d5a..0a273a1cc 100644 --- a/fgd/worldspawn.fgd +++ b/fgd/worldspawn.fgd @@ -1,7 +1,8 @@ // Super-extra special, so don't use normal bases. @SolidClass base(BaseEntity, ResponseContext) = worldspawn: "This is the world entity. Each map can only contain one, and it's automatically created for you." [ - message(string) : "Map Description / Title" + // "Doesn't seem to be used anywhere anymore." - VDC + // message(string) : "Map Description / Title" skyname(string) : "SkyBox Texture Name" : : "Texture used for the 2D skybox." skyname[HL2, EP1, EP2](string) : "SkyBox Texture Name" : "sky_day01_01" : "Texture used for the 2D skybox." @@ -23,11 +24,13 @@ "when the map loads or whether it should only be used for savegame comments and RPC. " + "This would normally be set to ''Yes'' outside of Mapbase." - startdark(boolean) : "Level Fade In" : 0 - gametitle[!P2](boolean) : "Display Game Title" : 0 : "Game Title that appears onscreen when this level starts." + startdark(boolean) : "Level Fade In" : 0 : "Fade from black after starting the level or loading a save. If you need something more configurable, use a logic_auto and env_fade instead." + gametitle[+complete](boolean) : "Display Game Title" : 0 : "Display the game logo onscreen when this level starts. Crashes unless the HL1 logo sprite is ported." newunit[engine](boolean) : "New Level Unit" : 0 - newunit(choices) : "New Level Unit" : 0 : "Used to clear out savegame data of previous levels to keep the savegame size as small as possible. " + + // Portal 2 force enables this + // TODO: we can probably hide it in multiplayer games too + newunit[!P2, complete](choices) : "New Level Unit" : 0 : "Used to clear out savegame data of previous levels to keep the savegame size as small as possible. " + "Only set it to Yes if the player cannot return to any previous levels." = [ 0: "No, keep current" @@ -62,10 +65,11 @@ musicpostfix[L4D2](string) : "Music Post-Fix String" : "Waterfront" - maxoccludeearea(float) : "Max occludee area" : 0 : "[Used on PC] Prevents occlusion testing for entities that take up more than X% of the screen." - minoccluderarea(float) : "Min occluder area" : 0 : "[Used on PC] Prevents occluders from being used if they take up less than X% of the screen." - maxoccludeearea_x360(float) : "Max occludee area (Xbox)" : 0 : "[Used on 360] Prevents occlusion testing for entities that take up more than X% of the screen." - minoccluderarea_x360(float) : "Min occluder area (Xbox)" : 0 : "[Used on 360] Prevents occluders from being used if they take up less than X% of the screen." + maxoccludeearea(float) : "Max occludee area" : 0 : "Prevents occlusion testing for entities that take up more than X% of the screen." + minoccluderarea(float) : "Min occluder area" : 0 : "Prevents occluders from being used if they take up less than X% of the screen." + // Most users are not mapping for Xbox + maxoccludeearea_x360[+complete](float) : "Max occludee area (Xbox)" : 0 : "[Used on 360] Prevents occlusion testing for entities that take up more than X% of the screen." + minoccluderarea_x360[+complete](float) : "Min occluder area (Xbox)" : 0 : "[Used on 360] Prevents occluders from being used if they take up less than X% of the screen." maxpropscreenwidth(float) : "Start Fade Pixels" : -1 : "Number of pixels wide at which all props in the level start to fade (<0 = use fademaxdist). " + "This number is ignored if the prop has a specific fade distance specified." minpropscreenwidth(float) : "End Fade Pixels" : 0 : "Minimum number of pixels wide at which the prop is visible (0 = don't fade out). " + @@ -99,7 +103,7 @@ //4 : "Python (Unsupported)" ] - coldworld(boolean) : "World is cold" : 0 : "Emit steam from NPC's mouths and similar effects." + coldworld[DODS](boolean) : "World is cold" : 0 : "Emit steam from players' mouths and similar effects." WaveHeight[engine](float) : "Wave Height" : : "Unused keyvalue, likely to indicate the amount of HL1-style wavy water." fixtriggerpushbug[engine](integer) : "Fix contact trigger_push bug" : 1 : "This fixes a bug with very thin contact mode trigger_push pushing downwards. This should be set to Yes unless this is an old map which worked around the bug and needs it in order to work correctly." From ba4e8f9867995f4af89b66725f88f5a623e4ff05 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 11 Jul 2023 02:50:36 -0700 Subject: [PATCH 180/243] Various minor entity changes/fixes --- fgd/point/point/point_camera.fgd | 3 ++- .../point/point_viewcontrol_multiplayer.fgd | 8 +++---- fgd/point/prop/prop_contraption_cube.fgd | 24 +++++++++++++++---- .../prop/prop_contraption_cube_button.fgd | 6 ++--- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/fgd/point/point/point_camera.fgd b/fgd/point/point/point_camera.fgd index a96209ce2..1ceba18e4 100644 --- a/fgd/point/point/point_camera.fgd +++ b/fgd/point/point/point_camera.fgd @@ -1,6 +1,7 @@ @PointClass base(BaseEntityPoint) studioprop("models/editor/camera.mdl") - frustum(FOV,fogstart,fogend,250 250 250,-1) + // Frustum color must be a KV name, not a raw RGB value + frustum(FOV,fogstart,fogend,_frustum_color,-1) = point_camera: "Camera" [ spawnflags(flags) : "spawnflags" = diff --git a/fgd/point/point/point_viewcontrol_multiplayer.fgd b/fgd/point/point/point_viewcontrol_multiplayer.fgd index 83dc031b9..7d5c100a0 100644 --- a/fgd/point/point/point_viewcontrol_multiplayer.fgd +++ b/fgd/point/point/point_viewcontrol_multiplayer.fgd @@ -4,7 +4,7 @@ studioprop("models/editor/camera.mdl") frustum(fov, _frustum_near, _frustum_far, _frustum_color, -1) line(255 255 0, targetname, target_entity) -= point_viewcontrol_multiplayer: "A camera entity that controls players' views. which works in multiplayer. " + += point_viewcontrol_multiplayer: "Multiplayer version of point_viewcontrol which can affect multiple players at once. " + "While it's active, the players will see out of the camera." [ fov(float) : "Field of view" : 90 : "Player FOV" @@ -16,14 +16,14 @@ target_team[engine](integer) : "Target Team" : -1 target_team[P2](choices) : "Target Team" : -1 : "Which team (or all) to take over the camera for." = [ - -1: "All Players" - 0: "Chell/Bendy" + -1: "All players" + 0: "Not on a team/singleplayer" 2: "P-Body" 3: "ATLAS" ] target_team[CSGO](choices) : "Target Team" : -1 : "Which team (or all) to take over the camera for." = [ - -1: "All Teams" + -1: "All players" 2: "Terrorist" 3: "Counter-Terrorist" ] diff --git a/fgd/point/prop/prop_contraption_cube.fgd b/fgd/point/prop/prop_contraption_cube.fgd index 81bd070cd..93fba9daa 100644 --- a/fgd/point/prop/prop_contraption_cube.fgd +++ b/fgd/point/prop/prop_contraption_cube.fgd @@ -1,9 +1,25 @@ @PointClass base(BasePortalCube) - studioprop("models/props/reflection_cube.mdl") + studioprop("models/props_ingame/cubecontraption.mdl") appliesto(P2EDU) = prop_contraption_cube : "Contraption Cube" [ - cube_mass(float) : "Mass" : 40 : "Mass of the cube (kg)" - ContraptionFrictionIndex(integer) : "Friction" : 0 : "Friction index of the cube [0,4]" - ContraptionElasticityIndex(integer) : "Elasticity" : 0 : "Elasticity index of the cube [0,4]" + cube_mass(float) : "Mass" : 40 : "Mass of the cube (kg)" + ContraptionFrictionIndex(integer) : "Friction" : 0 + ContraptionFrictionIndex(choices) : "Friction" : 0 : "Friction index of the cube" = + [ + 0 : "Frictionless" + 1 : "Slippery" + 2 : "Normal" + 3 : "Sticky" + 4 : "Very sticky" + ] + ContraptionElasticityIndex(integer) : "Elasticity" : 0 + ContraptionElasticityIndex(choices) : "Elasticity" : 0 : "Elasticity index of the cube" = + [ + 0 : "No rebound" + 1 : "Slight rebound" + 2 : "Normal" + 3 : "Bouncy" + 4 : "Very bouncy" + ] ] diff --git a/fgd/point/prop/prop_contraption_cube_button.fgd b/fgd/point/prop/prop_contraption_cube_button.fgd index 82aff3f9c..a50da7f97 100644 --- a/fgd/point/prop/prop_contraption_cube_button.fgd +++ b/fgd/point/prop/prop_contraption_cube_button.fgd @@ -1,7 +1,7 @@ @PointClass base(BasePortButton) appliesto(P2EDU) - studioprop("models/props_ingame/button_socket_contraption..mdl") -= prop_contraption_cube_button: "A floor button which is activated by a prop_contraption_cube." + studioprop("models/props_ingame/button_socket_contraption.mdl") += prop_contraption_cube_button: "A floor button which is activated by a prop_contraption_cube. Doesn't seem to work." [ - output OnPulse(string) : "Should be called when the button is being pressed." + output OnPulse(string) : "Fired when the button is being pressed." ] From d8cea169f4ef376d33b54dd9a11aeda38c6e9b6d Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 15 Jul 2023 18:02:46 +1000 Subject: [PATCH 181/243] Suit batteries and AR2 ball ammo use EZ2 variants --- fgd/point/item/item_ammo_ar2_altfire.fgd | 3 ++- fgd/point/item/item_battery.fgd | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fgd/point/item/item_ammo_ar2_altfire.fgd b/fgd/point/item/item_ammo_ar2_altfire.fgd index 69196b8d6..88192b5a0 100644 --- a/fgd/point/item/item_ammo_ar2_altfire.fgd +++ b/fgd/point/item/item_ammo_ar2_altfire.fgd @@ -4,7 +4,8 @@ [ @resources [ - model "models/items/combine_rifle_ammo01.mdl" + model "models/items/combine_rifle_ammo01.mdl" [-entropyzero2] + func item_ammo_ar2_altfire [+entropyzero2] // EZ2 has multiple variants. particle "combineball" ] ] diff --git a/fgd/point/item/item_battery.fgd b/fgd/point/item/item_battery.fgd index e2d3a57ca..dc8f45582 100644 --- a/fgd/point/item/item_battery.fgd +++ b/fgd/point/item/item_battery.fgd @@ -1,21 +1,24 @@ -@BaseClass appliesto(-mesa, -MBase) studio("models/items/battery.mdl") = _item_battery_hl2 [] -@BaseClass appliesto(-mesa, +MBase) studio() = _item_battery_mbase [] +@BaseClass appliesto(-mesa, -MBase, -EZ2) studio("models/items/battery.mdl") = _item_battery_hl2 [] +@BaseClass appliesto(-mesa, +MBase, -EZ2) studio() = _item_battery_mbase [] +@BaseClass appliesto(-mesa, -MBase, -EZ2) studio("models/items/battery.mdl") = _item_battery_ez2 [] @BaseClass appliesto(+mesa) studio("models/weapons/w_battery.mdl") = _item_battery_mesa [] @PointClass base(Item, BaseMesaPickup) appliesto(EP1, EP2, HL2, P1, Mesa) - base(_item_battery_hl2, _item_battery_mbase, _item_battery_mesa) + base(_item_battery_hl2, _item_battery_mbase, _item_battery_ez2, _item_battery_mesa) = item_battery: "HEV battery" [ PowerMultiplier[MBase](float) : "Power Multiplier" : "1.0" : "Multiplies the amount of armor this item gives." - model[MBase](studio) : "Model" : "models/items/battery.mdl" : "The battery's model." + // Mapbase makes this customisable, but EZ2 removes that. + model[MBase, -EZ2](studio) : "Model" : "models/items/battery.mdl" : "The battery's model." // Inputs input SetPowerMultiplier[MBase](float) : "Sets the multiplier for the amount of armor this item gives." @resources [ - model "models/items/battery.mdl" [-Mbase] // Taken case of by model keyvalue. + model "models/items/battery.mdl" [-Mbase, -entropyzero2] // Taken case of by model keyvalue. sound "ItemBattery.Touch" + func item_battery [+entropyzero2] // EZ2 has multiple variants. ] ] From d21feadef846170bb72d806690a92aeebfe89ca0 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 16 Jul 2023 01:59:22 -0700 Subject: [PATCH 182/243] Make bonus map accessor game specific --- fgd/point/point/point_bonusmaps_accessor.fgd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fgd/point/point/point_bonusmaps_accessor.fgd b/fgd/point/point/point_bonusmaps_accessor.fgd index e2048cf9c..1b8781faf 100644 --- a/fgd/point/point/point_bonusmaps_accessor.fgd +++ b/fgd/point/point/point_bonusmaps_accessor.fgd @@ -1,4 +1,6 @@ @PointClass base(BaseEntityPoint) + // Everything has this, but only 2013 singleplayer games will reasonably use it + appliesto(HL2, P1, complete) iconsprite("editor/ficool2/point_bonusmaps_accessor") color(200 0 0) = point_bonusmaps_accessor: "An entity that relays bonus maps changes." [ From 4c2d3b9d5c22ba225dbd1b02f827718f21714a00 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 16 Jul 2023 03:11:04 -0700 Subject: [PATCH 183/243] Add extra teams under complete fgd Spectators is still missing in some places but I think those are cases that would be physically impossible since spectators are nonsolid --- fgd/bases/BaseObject.fgd | 2 ++ fgd/bases/NavBlocker.fgd | 3 +++ fgd/bases/TeamNum.fgd | 4 +++- fgd/brush/func/func_tfbot_hint.fgd | 1 + fgd/point/bot/bot_controller.fgd | 1 + fgd/point/env/env_weaponfire.fgd | 5 +++-- fgd/point/filter/filter_activator_team.fgd | 18 +++++++++++++----- fgd/point/game/game_intro_viewpoint.fgd | 1 + fgd/point/game/game_round_win.fgd | 2 ++ fgd/point/game/game_text_tf.fgd | 1 + fgd/point/info/info_powerup_spawn.fgd | 1 + fgd/point/logic/logic_eventlistener.fgd | 9 ++++++--- .../logic/logic_eventlistener_itemequip.fgd | 1 + .../point/point_viewcontrol_multiplayer.fgd | 9 ++++++--- fgd/point/prop/prop_glowing_object.fgd | 2 ++ .../tf/tf_robot_destruction_spawn_group.fgd | 2 ++ fgd/point/weapon/weapon_portalgun.fgd | 2 +- 17 files changed, 49 insertions(+), 15 deletions(-) diff --git a/fgd/bases/BaseObject.fgd b/fgd/bases/BaseObject.fgd index 207a14e3e..de7f8553b 100644 --- a/fgd/bases/BaseObject.fgd +++ b/fgd/bases/BaseObject.fgd @@ -3,6 +3,8 @@ teamnum[engine](integer) : "Team" : 2 teamnum(choices) : "Team" : 2 : "Team" = [ + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] 2: "RED" 3: "BLU" ] diff --git a/fgd/bases/NavBlocker.fgd b/fgd/bases/NavBlocker.fgd index 787050ebf..1a9525602 100644 --- a/fgd/bases/NavBlocker.fgd +++ b/fgd/bases/NavBlocker.fgd @@ -5,6 +5,7 @@ teamtoblock[TF2](choices) : "Team(s) to block" : -1 : "Team(s) this entity should block." = [ -1: "Everyone" + 0: "Unassigned" [+complete] 2: "RED" 3: "BLU" 5: "Halloween Bosses" @@ -13,6 +14,7 @@ teamtoblock[CSGO](choices) : "Team(s) to block" : -1 : "Team(s) this entity should block" = [ -1: "Everyone" + 0: "Unassigned" [+complete] 2: "Terrorists" 3: "Counter-Terrorists" ] @@ -20,6 +22,7 @@ teamtoblock[L4D, L4D2](choices) : "Team(s) to block" : -1 : "Team(s) this entity should block" = [ -1: "Everyone" + 0: "Unassigned" [+complete] 2: "Survivors" 3: "Infected" ] diff --git a/fgd/bases/TeamNum.fgd b/fgd/bases/TeamNum.fgd index 49e947301..682aed2b9 100644 --- a/fgd/bases/TeamNum.fgd +++ b/fgd/bases/TeamNum.fgd @@ -14,13 +14,15 @@ [ -1: "None" 0: "All Teams" + 1: "Spectators" [+complete] 2: "Terrorist" 3: "Counter-Terrorist" ] teamnum[P2](choices) : "Team" : 0 = [ - 0: "Chell/Bendy" + 0: "Singleplayer/Unassigned" + 1: "Spectators" [+complete] 2: "P-Body" 3: "ATLAS" ] diff --git a/fgd/brush/func/func_tfbot_hint.fgd b/fgd/brush/func/func_tfbot_hint.fgd index c25c9ed30..ad2be2e65 100644 --- a/fgd/brush/func/func_tfbot_hint.fgd +++ b/fgd/brush/func/func_tfbot_hint.fgd @@ -5,6 +5,7 @@ team(choices) : "Team" : "-2" : "Which team will use this hint" = [ -2: "Everyone" + 0: "Unassigned" [+complete] 2: "RED" 3: "BLU" 5 : "Halloween Bosses" diff --git a/fgd/point/bot/bot_controller.fgd b/fgd/point/bot/bot_controller.fgd index 014fb534d..a3dd89187 100644 --- a/fgd/point/bot/bot_controller.fgd +++ b/fgd/point/bot/bot_controller.fgd @@ -7,6 +7,7 @@ teamnum[engine](integer) : "Team" : 2 teamnum(choices) : "Team" : 2 : "Team" = [ + 0: "Unassigned" [+complete] 2: "RED" 3: "BLU" ] diff --git a/fgd/point/env/env_weaponfire.fgd b/fgd/point/env/env_weaponfire.fgd index 052b0da56..3bd2bd117 100644 --- a/fgd/point/env/env_weaponfire.fgd +++ b/fgd/point/env/env_weaponfire.fgd @@ -19,9 +19,10 @@ targetteam[engine](integer) : "Target Team" : 3 targetteam(choices) : "Target Team" : 3 = [ - 3: "Zombies" + -1: "Any" + 0: "Unassigned" [+complete] 2: "Survivors" - -1: "Either" + 3: "Infected" ] ignoreplayers[engine](boolean) : "Ignore Players" : 0 diff --git a/fgd/point/filter/filter_activator_team.fgd b/fgd/point/filter/filter_activator_team.fgd index 954eb2b44..8345284cb 100644 --- a/fgd/point/filter/filter_activator_team.fgd +++ b/fgd/point/filter/filter_activator_team.fgd @@ -13,15 +13,19 @@ "If the filter mode is Allow, only entities whose team number matches the given team will pass the filter. " + "If the filter mode is Disallow, all entities EXCEPT those whose team number matches the given team will pass the filter." = [ - 2: "Terrorist" - 3: "Counter-Terrorist" + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] + 2: "Terrorists" + 3: "Counter-Terrorists" ] filterteam[L4D, L4D2](choices) : "Filter Team Number" : 2 : "The team number to filter by. " + "If the filter mode is Allow, only entities whose team number matches the given team will pass the filter. " + "If the filter mode is Disallow, all entities EXCEPT those whose team number matches the given team will pass the filter." = [ - 2: "Survivor" + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] + 2: "Survivors" 3: "Infected" ] @@ -29,6 +33,8 @@ "If the filter mode is Allow, only entities whose team number matches the given team will pass the filter. " + "If the filter mode is Disallow, all entities EXCEPT those whose team number matches the given team will pass the filter." = [ + 0: "Singleplayer/Unassigned" + 1: "Spectators" [+complete] 2: "P-Body" 3: "ATLAS" ] @@ -37,8 +43,10 @@ "If the filter mode is Allow, only entities whose team number matches the given team will pass the filter. " + "If the filter mode is Disallow, all entities EXCEPT those whose team number matches the given team will pass the filter." = [ - 2 : "Hgrunt" - 3 : "Scientist" + 0: "Unassigned" + 1: "Spectators" [+complete] + 2 : "HECU" + 3 : "Scientists" ] @resources [] diff --git a/fgd/point/game/game_intro_viewpoint.fgd b/fgd/point/game/game_intro_viewpoint.fgd index 01b5a038d..341abf87a 100644 --- a/fgd/point/game/game_intro_viewpoint.fgd +++ b/fgd/point/game/game_intro_viewpoint.fgd @@ -7,6 +7,7 @@ teamnum(choices) : "Team" : 0 : "Team" = [ 0: "Both" + 1: "Spectators" [+complete] 2: "RED" 3: "BLU" ] diff --git a/fgd/point/game/game_round_win.fgd b/fgd/point/game/game_round_win.fgd index 4500b3a33..3faed6af4 100644 --- a/fgd/point/game/game_round_win.fgd +++ b/fgd/point/game/game_round_win.fgd @@ -8,6 +8,7 @@ teamnum[TF2](choices) : "Team" : 0 : "Team" = [ 0: "None (Sudden Death)" + 1: "Spectators (?)" [+complete] 2: "RED" 3: "BLU" ] @@ -16,6 +17,7 @@ team[Mesa](choices) : "Team" : 0 : "Team" = [ 0 : "None" + 1 : "Spectators (?)" [+complete] 2 : "Marines (Team-based game modes only)" 3 : "Scientists (Team-based game modes only)" ] diff --git a/fgd/point/game/game_text_tf.fgd b/fgd/point/game/game_text_tf.fgd index 07f6a1dbe..da4f77aaa 100644 --- a/fgd/point/game/game_text_tf.fgd +++ b/fgd/point/game/game_text_tf.fgd @@ -412,6 +412,7 @@ display_to_team(choices) : "Audience" : 0 = [ 0: "Everyone" + 1: "Spectators Only" [+complete] 2: "RED Team Only" 3: "BLU Team Only" ] diff --git a/fgd/point/info/info_powerup_spawn.fgd b/fgd/point/info/info_powerup_spawn.fgd index 93912c778..4fba0141d 100644 --- a/fgd/point/info/info_powerup_spawn.fgd +++ b/fgd/point/info/info_powerup_spawn.fgd @@ -11,6 +11,7 @@ team(choices) : "Owner Team" : -2 : "Teams can own spawn points. When Powerups are dropped, they resposition themselves after timing out. They will try to pick a spawn point based on what team designation they had when they repositioned. Set to Everyone to have no team bias in spawn positioning" = [ -2: "Everyone" + 0: "Unassigned" [+complete] 2: "RED" 3: "BLU" ] diff --git a/fgd/point/logic/logic_eventlistener.fgd b/fgd/point/logic/logic_eventlistener.fgd index 8bbe55f49..21f23a28b 100644 --- a/fgd/point/logic/logic_eventlistener.fgd +++ b/fgd/point/logic/logic_eventlistener.fgd @@ -13,13 +13,16 @@ teamnum[P2](choices) : "Team Number" : -1 : "If set, will only fire its output if the event is generated from someone of the specified team." = [ -1: "Don't care" - 1: "Team 1" - 2: "Team 2 (ORANGE)" - 3: "Team 3 (BLUE)" + 0: "Singleplayer/Unassigned" + 1: "Spectators" [+complete] + 2: "P-Body" + 3: "ATLAS" ] teamnum[CSGO](choices) : "Team Number" : -1 : "If set, will only fire its output if the event is generated from someone of the specified team." = [ -1: "Don't care" + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] 2: "Terrorists" 3: "Counter-Terrorists" ] diff --git a/fgd/point/logic/logic_eventlistener_itemequip.fgd b/fgd/point/logic/logic_eventlistener_itemequip.fgd index 990c487ae..a798dff5b 100644 --- a/fgd/point/logic/logic_eventlistener_itemequip.fgd +++ b/fgd/point/logic/logic_eventlistener_itemequip.fgd @@ -23,6 +23,7 @@ teamnum(choices) : "Team Number" : -1 : "If set, will only fire its output if the event is generated from someone of the specified team." = [ -1: "Don't care" + 0: "Unassigned" [+complete] 2: "Terrorists" 3: "Counter-Terrorists" ] diff --git a/fgd/point/point/point_viewcontrol_multiplayer.fgd b/fgd/point/point/point_viewcontrol_multiplayer.fgd index 7d5c100a0..c68b104d1 100644 --- a/fgd/point/point/point_viewcontrol_multiplayer.fgd +++ b/fgd/point/point/point_viewcontrol_multiplayer.fgd @@ -17,15 +17,18 @@ target_team[P2](choices) : "Target Team" : -1 : "Which team (or all) to take over the camera for." = [ -1: "All players" - 0: "Not on a team/singleplayer" + 0: "Singleplayer/Unassigned" + 1: "Spectators" [+complete] 2: "P-Body" 3: "ATLAS" ] target_team[CSGO](choices) : "Target Team" : -1 : "Which team (or all) to take over the camera for." = [ -1: "All players" - 2: "Terrorist" - 3: "Counter-Terrorist" + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] + 2: "Terrorists" + 3: "Counter-Terrorists" ] spawnflags(flags) = diff --git a/fgd/point/prop/prop_glowing_object.fgd b/fgd/point/prop/prop_glowing_object.fgd index e80461b71..0f29f5175 100644 --- a/fgd/point/prop/prop_glowing_object.fgd +++ b/fgd/point/prop/prop_glowing_object.fgd @@ -10,6 +10,8 @@ glowforteam(choices) : "Glow For Team" : -1 : "Team(s) this entity should glow for" = [ -1: "Everyone" + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] 2: "Survivors" 3: "Infected" ] diff --git a/fgd/point/tf/tf_robot_destruction_spawn_group.fgd b/fgd/point/tf/tf_robot_destruction_spawn_group.fgd index 64e1e1a1d..2e80b6ad1 100644 --- a/fgd/point/tf/tf_robot_destruction_spawn_group.fgd +++ b/fgd/point/tf/tf_robot_destruction_spawn_group.fgd @@ -8,6 +8,8 @@ team_number[engine](integer): "Team Number" : 2 team_number(choices) : "Team Number" : 2 = [ + 0: "Unassigned" [+complete] + 1: "Spectators" [+complete] 2: "RED" 3: "BLU" ] diff --git a/fgd/point/weapon/weapon_portalgun.fgd b/fgd/point/weapon/weapon_portalgun.fgd index 7a3fb6a20..786b3c536 100644 --- a/fgd/point/weapon/weapon_portalgun.fgd +++ b/fgd/point/weapon/weapon_portalgun.fgd @@ -12,7 +12,7 @@ startingteamnum[engine](integer) : "Player" : 0 : "Which player this gun belongs to. Will decide which portals it fires before being picked up, as well as the skin." startingteamnum[P2](choices) : "Player" : 0 : "Which player this gun belongs to. Will decide which portals it fires before being picked up, as well as the skin." = [ - 0: "Chell/Bendy" + 0: "Singleplayer/Unassigned" 2: "P-Body" 3: "ATLAS" ] From 030128f53b32890b414812c35c01e403f8866ab2 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 27 Jul 2023 11:50:49 -0700 Subject: [PATCH 184/243] Remove ball type keyvalue As far as I can tell there's no code for this to actually do anything --- fgd/bases/CombineBallSpawners.fgd | 7 ------- fgd/point/point/point_energy_ball_launcher.fgd | 4 ---- 2 files changed, 11 deletions(-) diff --git a/fgd/bases/CombineBallSpawners.fgd b/fgd/bases/CombineBallSpawners.fgd index 6ad2d629a..40c326175 100644 --- a/fgd/bases/CombineBallSpawners.fgd +++ b/fgd/bases/CombineBallSpawners.fgd @@ -12,13 +12,6 @@ maxspeed(float) : "Max ball speed" : 600.0 : "The maximum speed of balls that fly in the spawner" ballradius(float) : "Ball radius" : 12.0 : "The size of the sprite and the collsion. This is restricted to within 1-12 units." - balltype(choices) : "Ball Type" : "Combine Energy Ball 1" = - [ - 0: "Combine Energy Ball 1" - 1: "Combine Energy Ball 2" - 2: "Combine Energy Ball 3" - ] - balltype[engine](integer): "Ball Type": 0 ballrespawntime(float) : "Ball Respawn Time" : 4.0 : "The energy balls respawn time" diff --git a/fgd/point/point/point_energy_ball_launcher.fgd b/fgd/point/point/point_energy_ball_launcher.fgd index fef83f78f..62b6eb447 100644 --- a/fgd/point/point/point_energy_ball_launcher.fgd +++ b/fgd/point/point/point_energy_ball_launcher.fgd @@ -7,10 +7,6 @@ ballcount(integer) : "Ball count" : 1 : "The number of Pellets that are alive at once." maxspeed(float) : "Max ball speed" : 150.0 : "The maximum starting speed." minspeed(float) : "Min ball speed" : 150.0 : "The minimum starting speed." - balltype(choices) readonly : "Ball Type" : 0 : "Has no effect." = - [ - 0 : "HEP" - ] balllifetime(float) : "Ball Lifetime" : 12 : "The time in seconds the ball will live before self-destructing. A negative value will give infinite life, appearing green." minlifeafterportal(float) : "Min life after portal transition" : 6 : "When energy balls created by this launcher pass through a portal their life is refreshed to be this number at minimum." From f3210b780dd99307991865ac3d23444b0cac8434 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sun, 30 Jul 2023 15:30:21 -0500 Subject: [PATCH 185/243] add ASRD tag to unify_fgd.py --- src/hammeraddons/unify_fgd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index fe24b1147..0910c579a 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -53,6 +53,9 @@ ('EZ2', 'Entropy: Zero 2'), ('KZ', 'Kreedz Climbing'), ], + 'ASW': [ + ('ASRD', 'Alien Swarm: Reactive Drop'), + ], 'P2': [ ('P2SIXENSE', 'Portal 2 Sixense MotionPack'), ('P2EDU', 'Portal 2 Educational Version'), From 32fdbe4a4ad4cd599eff2f62d68acf3d64d76b84 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 31 Jul 2023 14:16:49 -0500 Subject: [PATCH 186/243] parallax-corrected cubemap support for Alien Swarm: Reactive Drop --- fgd/point/comp/comp_cubemap_parallax.fgd | 13 +++ transforms/comp_cubemap_parallax.py | 118 +++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 fgd/point/comp/comp_cubemap_parallax.fgd create mode 100644 transforms/comp_cubemap_parallax.py diff --git a/fgd/point/comp/comp_cubemap_parallax.fgd b/fgd/point/comp/comp_cubemap_parallax.fgd new file mode 100644 index 000000000..3ac62356c --- /dev/null +++ b/fgd/point/comp/comp_cubemap_parallax.fgd @@ -0,0 +1,13 @@ +@PointClass + appliesto(+srctools, ASRD) + base(Angles) + iconsprite("editor/comp_cubemap_parallax") + wirebox(mins, maxs) + sphere() += comp_cubemap_parallax : +"Specifies the positions of the walls of a box-shaped room for parallax-corrected cubemaps." +[ + mins(vector) : "BBox Mins" : "-32 -32 -32" : "Minimum offset from the entity, defining the shape." + maxs(vector) : "BBox Maxes" : "32 32 32" : "Maximum offset from the entity, defining the shape." + radius(float) : "Cubemap Radius" : "32" : "Bounding radius to find env_cubemap entities to modify." +] diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py new file mode 100644 index 000000000..ff12dbce3 --- /dev/null +++ b/transforms/comp_cubemap_parallax.py @@ -0,0 +1,118 @@ +"""Adds keys to generated cubemap materials to map them to the bounds of a cubeoid.""" + +from srctools import Matrix, Vec, conv_float +from srctools.logger import get_logger + +from hammeraddons.bsp_transform import trans, Context + +import re + + +LOGGER = get_logger(__name__) + + +@trans('comp_cubemap_parallax') +def comp_cubemap_parallax(ctx: Context): + """Modify cubemap materials to contain parallax information.""" + cubemap_material_name_pattern = re.compile(r"materials/maps/.*_(-?[0-9]+)_(-?[0-9]+)_(-?[0-9]+)\.vmt") + cubemap_material_extract_pattern = re.compile(br'"patch"\r\n\{\r\n\t"include"\t\t"([^"]*)"\r\n\t"(?:replace|insert)"\r\n\t\{\r\n\t\t"\$envmap"\t\t"([^"]*)"\r\n(.*)\t\}\r\n}\r\n') + for parallax in ctx.vmf.by_class['comp_cubemap_parallax']: + parallax.remove() + + origin = Vec.from_str(parallax['origin']) + angles = Matrix.from_angstr(parallax['angles']) + radius = conv_float(parallax['radius']) + radius_squared = radius**2 + mins = Vec.from_str(parallax['mins']) + maxs = Vec.from_str(parallax['maxs']) + diff = maxs - mins + + # ensure bounding box has volume + if diff[0] == 0.0: + diff[0] = 1.0 + if diff[1] == 0.0: + diff[1] = 1.0 + if diff[2] == 0.0: + diff[2] = 1.0 + + # we need a 4-component matrix here because we need to translate + def matmul(a, b): + def helper(a, b, x, y): + return a[x] * b[y * 4] + a[x + 4] * b[y * 4 + 1] + a[x + 8] * b[y * 4 + 2] + a[x + 12] * b[y * 4 + 3] + + return ( + helper(a, b, 0, 0), helper(a, b, 1, 0), helper(a, b, 2, 0), helper(a, b, 3, 0), + helper(a, b, 0, 1), helper(a, b, 1, 1), helper(a, b, 2, 1), helper(a, b, 3, 1), + helper(a, b, 0, 2), helper(a, b, 1, 2), helper(a, b, 2, 2), helper(a, b, 3, 2), + helper(a, b, 0, 3), helper(a, b, 1, 3), helper(a, b, 2, 3), helper(a, b, 3, 3), + ) + + translate1_matrix = ( + 1.0, 0.0, 0.0, -origin[0], + 0.0, 1.0, 0.0, -origin[1], + 0.0, 0.0, 1.0, -origin[2], + 0.0, 0.0, 0.0, 1.0, + ) + + rotation_matrix = matmul(translate1_matrix, ( + angles[(0, 0)], angles[(0, 1)], angles[(0, 2)], 0.0, + angles[(1, 0)], angles[(1, 1)], angles[(1, 2)], 0.0, + angles[(2, 0)], angles[(2, 1)], angles[(2, 2)], 0.0, + 0.0, 0.0, 0.0, 1.0, + )) + + translate2_matrix = matmul(rotation_matrix, ( + 1.0, 0.0, 0.0, -mins[0], + 0.0, 1.0, 0.0, -mins[1], + 0.0, 0.0, 1.0, -mins[2], + 0.0, 0.0, 0.0, 1.0, + )) + + scale_matrix = matmul(translate2_matrix, ( + 1.0 / diff[0], 0.0, 0.0, 0.0, + 0.0, 1.0 / diff[1], 0.0, 0.0, + 0.0, 0.0, 1.0 / diff[2], 0.0, + 0.0, 0.0, 0.0, 1.0, + )) + + parallax_material_keys = (b"\t\t\"$envmapparallax\"\t\t\"1\"\r\n" + + b"\t\t\"$envmapparallaxobb1\"\t\t\"[%f %f %f %f]\"\r\n" + + b"\t\t\"$envmapparallaxobb2\"\t\t\"[%f %f %f %f]\"\r\n" + + b"\t\t\"$envmapparallaxobb3\"\t\t\"[%f %f %f %f]\"\r\n") % ( + scale_matrix[0], scale_matrix[1], scale_matrix[2], scale_matrix[3], + scale_matrix[4], scale_matrix[5], scale_matrix[6], scale_matrix[7], + scale_matrix[8], scale_matrix[9], scale_matrix[10], scale_matrix[11], + ) + + any_found = False + for name in ctx.bsp.pakfile.namelist(): + match = cubemap_material_name_pattern.fullmatch(name) + if match is None: + continue + + cubemap_origin = Vec(int(match.group(1)), int(match.group(2)), int(match.group(3))) + if (cubemap_origin - origin).len_sq() > radius_squared: + continue + + any_found = True + material_bytes = ctx.bsp.pakfile.read(name) + match = cubemap_material_extract_pattern.fullmatch(material_bytes) + if match is None: + LOGGER.error( + 'Failed to parse cubemap material {}', + name + ) + continue + + ctx.pack.pack_file(name, data = (b"\"patch\"\r\n{\r\n\t\"include\"\t\t\"" + match.group(1) + + b"\"\r\n\t\"insert\"\r\n\t{\r\n\t\t\"$envmap\"\t\t\"" + match.group(2) + + b"\"\r\n" + match.group(3) + parallax_material_keys + + b"\t\t\"$envmaporigin\"\t\t\"[" + bytes(str(cubemap_origin), 'ascii') + + b"]\"\r\n\t}\r\n}\r\n")) + + if not any_found: + LOGGER.warning( + 'No cubemapped materials found within {} units for comp_cubemap_parallax at ({})!', + parallax['radius'], + parallax['origin'], + ) From 6866029b7f87dcd5c56287c0db36768f2e454dba Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 31 Jul 2023 16:40:06 -0500 Subject: [PATCH 187/243] parse all comp_cubemap_parallax entities and use the closest one to each cubemap. use srctools for VMT manipulation. --- transforms/comp_cubemap_parallax.py | 95 ++++++++++++++++++----------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index ff12dbce3..2df7c2e5e 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -1,10 +1,12 @@ """Adds keys to generated cubemap materials to map them to the bounds of a cubeoid.""" from srctools import Matrix, Vec, conv_float +from srctools.vmt import Material from srctools.logger import get_logger from hammeraddons.bsp_transform import trans, Context +import io import re @@ -14,15 +16,13 @@ @trans('comp_cubemap_parallax') def comp_cubemap_parallax(ctx: Context): """Modify cubemap materials to contain parallax information.""" - cubemap_material_name_pattern = re.compile(r"materials/maps/.*_(-?[0-9]+)_(-?[0-9]+)_(-?[0-9]+)\.vmt") - cubemap_material_extract_pattern = re.compile(br'"patch"\r\n\{\r\n\t"include"\t\t"([^"]*)"\r\n\t"(?:replace|insert)"\r\n\t\{\r\n\t\t"\$envmap"\t\t"([^"]*)"\r\n(.*)\t\}\r\n}\r\n') + parallax_cubemap_configs = [] for parallax in ctx.vmf.by_class['comp_cubemap_parallax']: parallax.remove() origin = Vec.from_str(parallax['origin']) angles = Matrix.from_angstr(parallax['angles']) radius = conv_float(parallax['radius']) - radius_squared = radius**2 mins = Vec.from_str(parallax['mins']) maxs = Vec.from_str(parallax['maxs']) diff = maxs - mins @@ -75,44 +75,71 @@ def helper(a, b, x, y): 0.0, 0.0, 0.0, 1.0, )) - parallax_material_keys = (b"\t\t\"$envmapparallax\"\t\t\"1\"\r\n" + - b"\t\t\"$envmapparallaxobb1\"\t\t\"[%f %f %f %f]\"\r\n" + - b"\t\t\"$envmapparallaxobb2\"\t\t\"[%f %f %f %f]\"\r\n" + - b"\t\t\"$envmapparallaxobb3\"\t\t\"[%f %f %f %f]\"\r\n") % ( - scale_matrix[0], scale_matrix[1], scale_matrix[2], scale_matrix[3], - scale_matrix[4], scale_matrix[5], scale_matrix[6], scale_matrix[7], - scale_matrix[8], scale_matrix[9], scale_matrix[10], scale_matrix[11], - ) + parallax_cubemap_configs.append({ + 'origin': origin, + 'radius': radius, + 'radius_sqr': radius**2, + 'used': 0, + 'obb1': "[%f %f %f %f]" % (scale_matrix[0], scale_matrix[1], scale_matrix[2], scale_matrix[3]), + 'obb2': "[%f %f %f %f]" % (scale_matrix[4], scale_matrix[5], scale_matrix[6], scale_matrix[7]), + 'obb3': "[%f %f %f %f]" % (scale_matrix[8], scale_matrix[9], scale_matrix[10], scale_matrix[11]), + }) - any_found = False - for name in ctx.bsp.pakfile.namelist(): - match = cubemap_material_name_pattern.fullmatch(name) - if match is None: + cubemap_material_name_pattern = re.compile(r"materials/maps/.*_(-?[0-9]+)_(-?[0-9]+)_(-?[0-9]+)\.vmt") + for name in ctx.bsp.pakfile.namelist(): + match = cubemap_material_name_pattern.fullmatch(name) + if match is None: + continue + + cubemap_origin = Vec(int(match.group(1)), int(match.group(2)), int(match.group(3))) + + best_match = None + best_match_distance_sqr = -1 + for config in parallax_cubemap_configs: + distance_sqr = (cubemap_origin - config['origin']).len_sq() + if distance_sqr > config['radius_sqr']: continue + if best_match is None or best_match_distance_sqr > distance_sqr: + best_match = config + best_match_distance_sqr = distance_sqr + + if best_match is None: + continue + + material = Material.parse(ctx.bsp.pakfile.read(name).decode('utf-8'), filename = name) + if material.shader != 'patch': + LOGGER.error( + 'Expected cubemap material to have the "patch" shader, but shader is "{}" for {}', + material.shader, + name + ) + continue - cubemap_origin = Vec(int(match.group(1)), int(match.group(2)), int(match.group(3))) - if (cubemap_origin - origin).len_sq() > radius_squared: - continue + if len(material.blocks) != 1 or (material.blocks[0].name != 'replace' and material.blocks[0].name != 'insert'): + LOGGER.error( + 'Expected cubemap material to have exactly one block named either "replace" or "insert" for {}', + name + ) + continue - any_found = True - material_bytes = ctx.bsp.pakfile.read(name) - match = cubemap_material_extract_pattern.fullmatch(material_bytes) - if match is None: - LOGGER.error( - 'Failed to parse cubemap material {}', - name - ) - continue + config['used'] += 1 + + material.blocks[0].name = 'insert' + with material.blocks[0].build() as builder: + builder['$envmapparallax']('1') + builder['$envmapparallaxobb1'](config['obb1']) + builder['$envmapparallaxobb2'](config['obb2']) + builder['$envmapparallaxobb3'](config['obb3']) + builder['$envmaporigin']('[%s]' % str(cubemap_origin)) - ctx.pack.pack_file(name, data = (b"\"patch\"\r\n{\r\n\t\"include\"\t\t\"" + match.group(1) + - b"\"\r\n\t\"insert\"\r\n\t{\r\n\t\t\"$envmap\"\t\t\"" + match.group(2) + - b"\"\r\n" + match.group(3) + parallax_material_keys + - b"\t\t\"$envmaporigin\"\t\t\"[" + bytes(str(cubemap_origin), 'ascii') + - b"]\"\r\n\t}\r\n}\r\n")) + encoded = io.StringIO() + material.export(encoded) + ctx.pack.pack_file(name, data = encoded.getvalue()) - if not any_found: + for config in parallax_cubemap_configs: + if config['used'] == 0: LOGGER.warning( - 'No cubemapped materials found within {} units for comp_cubemap_parallax at ({})!', + 'No materials found affected by a cubemap within {} units for comp_cubemap_parallax at ({})!', parallax['radius'], parallax['origin'], ) From 4dc1e7d86c52cd6aed740b77c434c32991a63401 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:31:58 +1000 Subject: [PATCH 188/243] Make parallax cubemap editor icon --- .../materials/editor/comp_cubemap_parallax.vmt | 8 ++++++++ .../materials/editor/comp_cubemap_parallax.vtf | Bin 0 -> 22052 bytes materialsrc/editor/comp_cubemap_parallax.blend | Bin 0 -> 804052 bytes materialsrc/editor/comp_cubemap_parallax.pdn | Bin 0 -> 17108 bytes 4 files changed, 8 insertions(+) create mode 100644 hammer/materials/editor/comp_cubemap_parallax.vmt create mode 100644 hammer/materials/editor/comp_cubemap_parallax.vtf create mode 100644 materialsrc/editor/comp_cubemap_parallax.blend create mode 100644 materialsrc/editor/comp_cubemap_parallax.pdn diff --git a/hammer/materials/editor/comp_cubemap_parallax.vmt b/hammer/materials/editor/comp_cubemap_parallax.vmt new file mode 100644 index 000000000..48f23d3e4 --- /dev/null +++ b/hammer/materials/editor/comp_cubemap_parallax.vmt @@ -0,0 +1,8 @@ +Sprite +{ + $baseTexture "editor/comp_cubemap_parallax" + $spriteorientation "vp_parallel" + $spriteorigin "[ 0.50 0.50 ]" + $spriterendermode 2 + $no_fullbright 1 +} diff --git a/hammer/materials/editor/comp_cubemap_parallax.vtf b/hammer/materials/editor/comp_cubemap_parallax.vtf new file mode 100644 index 0000000000000000000000000000000000000000..c338b86659a4ae3aeedff6b0ef66bf51fb388fcb GIT binary patch literal 22052 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 aMBP>``)-s?d2lzB{RwsaFJs@JaQqhsAVZ}9 literal 0 HcmV?d00001 diff --git a/materialsrc/editor/comp_cubemap_parallax.blend b/materialsrc/editor/comp_cubemap_parallax.blend new file mode 100644 index 0000000000000000000000000000000000000000..5cde2601ab88ab3e09893517da58c4a556958ee6 GIT binary patch literal 804052 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

P{5|Y@%!h;D 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

tEWxu;xei?;8SF zvG1+V^EuL2DtA1)L!agQ7xDbnQspKIAX`Sh<4&FmE}A!M74eY{oU2^mbQQBQ&SU0y zpj*LV@B_Vho;Kovu8--8<^D>Hgu`>A5f60JnGXCyc~LsV1D(rs;0HSMJZ;1S-3G=7 z{6e~4Mb5=WJkSj>9r%TGlOlA82fF1<2Y#UI-fmJvFNg;^59Nqm(77#9{16XxhzEWM zyDgzU={)T5%opbYk9Y3an?`r}9Shh!6V5nI3$hr@jPw#0S04^x(UJ(}#4J z@(~~O=P*6^f*$EG=@B3FZA=foz%eQPh!6U=u2A{|U(gp4@{jnSFEBm$f*$!W(~tO| zU%~X?yMpDul^`GSLEp#p;0wJa=^ybyKb`5pcRAyiBp>lX?=n63f<8%p5MRvOQj@uX z+gtEOy;zZuf5eA;wAbJZdg#H_KjMQP&%qb;$OoQN{gLNsbH9M|==^2T{sH+0{wNP- zdBS;#G;cOhv?%I4?fnwZqjT@#^R(IZ{;^DcN51gC%0)WUx>&aXAIw|%e~aVSGfVNs zd30Yk!lk9w^E_?(x&i6Wyc*@aP~ZjpP#%D*(o4*F+WF3>O`Ro$ah`TljGhGzjgO1Y z({66r-)R={lja?%{Np_Bxbx5zbpxEI&Gi@f0R!MF!}9#>m)GN&Lt)16f5gpA{D97u zrz`0^?Naz@=h2CI|3jLZTj-aJpMIWp9Da&grDrt$rS?;vr_Js0=s8cDzI{4I_RTtO zsn65q{t*3Rf$b-c`%UzV=%4$zpGQ9lndl#n5oPd~;wR>>#0kXr%5=rGDgHfb-XE^} zSJjVse~IhQASKyF{|G*)pRxS5;`6lEv;NGSHcI~pynr9{09<7__B`#~*VD5~&eOgD z%psn65q_89H6 z%VF3tZ*hAJ`zy&FqSS0B$|cz^)IQUB+HOlB*?zfSou}=xTlkY|m;K*Y*FJ*}>bL)9 z48P7kW1nER5iYG;$|qgwdqAfOT5+Csp53B-gLIy@D|Y?SFS&jbKjNFzJ}c_czi$wD z0YCST3P0dVdfJj#Qu~}PO{0I`An*cy-Wy!6BlVEZ)BeV~P-h~Xzbv)=iSx8y=Xx0O zkv?gEYW9mfPy6@cJni3$_(}Lt-9!5o*S|K(zBAwj{Lmf)SEXy(_j&dDQ15h}_JtBZ z{XFgazyeAMwL^+Hv?P>IOJZoBL1TM|zrDw>PW?tOl$GtOl$GtOl$GtOl$GtOl$GtOl$GtOl$GtOl$GtOgof1C`Au%CmZ; z#%QBxaQsTv^R$ikVgLo+U&MI;cpnDuFXDZF#NS>d2IpzhzE(Ql>@Uq~FKgUhShv9E z%)tE}zyI)?nDl7x43c%k?@^55;>F zYxHv%!uKjFIZwNm?^RI!>l8nFudkust6;oJBa>+&tn$vH9g||tmSU;OMb6OKTkUjzeahl33vfN=oj@6 z;VtDRwV&w!@LsK%K8z*I`&(<+{bk1Um_aAXI*siP{n!7b`VhG`$9c>Xeza&UovT{u zdCa*vv%4lQzkkir7k=7F^pE`hxX5|TfBo*iMb2YhdD)uS^Oz63cc4q8q3grjFX*`7 z`Ig+cxo`gKyLN4!G$_tv_QZM2_wsqnlRmL*-CjT3&^7MPZPpzv&SO4d+F`z8P3xUMb?R|* zCe4{TapH3EJL=<$Ykr#U7A<}DJm!?@KXaBi?@&yjc&jdv=Rfv}IT#urrM=Fab>`gO zg}u(wo(p=Np8kbt>C(j8RYdYe_IcrmN)l9tvHRCIen9;EMe6+NGSL$rm+MEv=SVua z@as$qFT5(#<9ZiF^7;I8&&hMa4?B0)ne>}Hd2$EN-Y0kW=2qv!yYhsY&M?7$>LLMB z71xRV0}J}+R>44$hI#>~C1;$}4gc2Ut`UjJ|Et4aJ&2~8zK2A;aj6S<|F02==A`MT zN`vH^LtMJwY5c}J^tH1Oi=X41E`Ahud{B~*F1Qp+zvw=GYor_%ZgO_hisPa^J}bv< zM=xEl_*|Xb`mF}61{y{K2X5myJ2gyhZAPsI;x$0uaKW#5&LrOcC*s3Mw;jy+Owq$zAC+& zHmT75!Yje1HsV_l7uHgHEh^zXDR^e*-7c_-#8EzI2VtR^G7qt%bU(mO-b#Bkx zzTVdUg{@2b`j@mWk&8Kkr2N&{xx8SqL;-qz~l{=|j0ixuo)UQH{zTC1L1URqon;ldk+BKGNs?F-o_+;Mu!; z@uI<{a|Ze=88}j6w5#uzQNG$E=_Q=#hYR`x9ny#AaBJPZjN}iJ=D46Nm&|EtGG{m7FE=nKOPo0j^ zPgBK6JJO1@gBE$B_}tEk?PLpjDln13DwB)!p9Vx1eY6!puEJ zVO_R%7<6X(kZHX<;a)Xv;U$$$_qAwxb=-+R^oRIJpGimfR^2#c$Pm(p^dNm;g>+NC z&KFZObC%5Q8K|xa18c)o^Hau6r9Uw^#eSrZiiD7^3IoS}ljcLFFHo-c>=YRW{Se>x z7pmuPEmLmaMao4yvKQojlcq7wHi9$nJ!zWYI1|hdf->3sgi zMUvk8oU#{XOaXPL_~fiItwkJqtu=Br ziG5E8i964I_Y$)EMYLnRoTD*Byqa z&VV;u^;BhmdQG~bb?X~FU!pt^T+U0oo=kpOt|`y!9)D;i-Fs=*XT|U2PI{(aZ~coJ z9}V?RmGzC<59&!#A3q?}NqpQ3m&$ToI8ntj>r&y?XuT;{zQ>BJV2n%r@ssVB2!J2$ z7sE5*l^2E81w9nIBH7|fUOyS(d?-GR{+IL@_bb*5;WJ*`hW=>hpwhJ-7raz|N%2u` zJQ117j{CDqRC{|kzfZnag;D;{518d|D53nhw?xZdp-{Sg9Vf~kY7@#AxhQ|&SFiHd zc9N<`y4D)kkMd%=#Tr5RgZ|Q%Ka`s^ZU z&6}5}a!zH6#?sOH?p}(|vqW!2Bo_%(_qk*MksQa}j>;<0CUm84D30m|M)ZgfRg^PY9Wp|Zw-KHh#eybWM`8#rWD)&dLnEq$V{&br-e7SOwBe#j^r}B7fEQhaAawwnY ziO8_CPZ#N?{>VLCjr-dY#$EnR(f$VVh&JxphX!5RLy}AF8Tizz{Cl?iYjEggUiWKJ z*o*QH{iP}YG^9a!3C3Goj@+@T{JKr5T%p`yUgr!hcf~0we@81;MUnTVa)$xG@Io%; zk-2F(V;EfA%F>iHNo#gIMa$#T#FUh5PhIua3vr6|R(tM)m4`)e7*o@}BME|=7 z+VP)K4(X{PyZS6a0sT=uQ@WOOImZ)AF~8OEiJ3dl9F?=|UsiOycKYMeFM=$`am0I^ zIozMQ!v}MSPiYZzostjA0_HlimW6VARENHOxh)F>mb@w}3kM4t=#R<*=G%awxD{Y- zV(W>AnAf{n_^Dk)6I6Iw)lYY(nr{)0yb-slsp+rZ z%5C$?GsFMbc|USx8AW=CtK!?#M9(Sj(&dfwk=(Y~F@(@IF2XzRhv%V7_|TZZXZ@uaLrfI$v6ez(2wG3U8jJrXk9EiL z7%eL~^SR2!>5{u<{2mW#{LmT;>c;|=e-S{x2rG5UumtK&2cJF&luHhi|(MKP3 zRQP{(?~)6bE?P{B!S=Emuo|!$uo|!$uo|!$uo|!$uo`G!4YYQtwcye!AbaT?5e4?f zr`c|CPg3>=sAu){EL_+-&`P#aH5cT8KjaOa!S)W@J&-pkqcGUxkO%&d=Py_CkPgVB zy;)fs4|#=!N*>Yyd6TmU6Ev8kLH@uW@`gUm`s8##-js~;Fue_hpK;G1h^5h0HK^w?p{*dQ=fc44wguG)j%EKN?$OC`K>${ZYaXKJxT1I)8Z-qSY zhrAK42S^9xP0uI~%Mp+V{*dRf9+3{nJ1(O4|ziiIDeeaKwif7MSk~5 z$B+3#UV+EgNJk(qWBam?kw5Tl>@&AsvCdjO`2U5ugkG5%@#i&_pE<=?LUyY+nvh;sSZ# z4|#2^N*>Y?$jjKiyvvXW{*X6xsFH_t1oASrFNYfPz#sD3j$r&apO7~tWBbx($OC`K z8##>g$N3E8Wo%y#Gvt9kiz#sD5LzFzEBaoM|eL2#Q2mX-f@5T6WJ_C6f+n1vZdEgIu-n*4N zq$7}*v343bcNGRix)_Is$n}Xod1nZ6Oc* zA#W(h`Qv;B@{rI9<)PX_9{59^%i{^8BanxLRwxhE7V^L!^8ABT{*aD99uiuiJXBlA z1AoXH*-^Y?$jjKibeZy)KjgI?&HChY1oASrFWrVb@P|B~$MGJgBaoM| zec_G60e;{Qc_Umtk&ZxK#`a~lkw5TY?$jjKiyx)|^ z{2_0I=OvI1$eW(AeL2mL2mX-f9k23N;B*A?GPWC&5=?LUyY+ueXem zK{^6?8QYf+8S=m%^4fSl66t`v<1)4{^qv!4=-B8Iz#sDZczzJ+2;^mKUp{8Y1AoXH zak)L?d0 zBaoM|eL3Hd2mX*(;Q4u^BaoM|eVJ>>1AoYCdzZow>43aR6SFli^9*_5AIRhRI;10% zm#u;6HRUmX$Q$ALYosHTm#u-BZ^#3G$n$xfqm9!M%FEWkTwurpf5_`QP~nGkgz~aA zFnxwR@Q1viBe}d7>p7DqW@}*j4SC=XdEQRkzHmOn{AFukK5obZf5>aY`X=Wyl$Wi6 zxzLaY{*X7)uJVU;gz~aAFaw4>@Q1uUZeNg&P+qnMW`Q9O{2|Y0`xfa4AM)HQlsu#(l$Wi2S!~Dyf5;oUUdcl`LV4NRmroe- zz#sD3ZdLM-j!<5<_GQqJ2mX*Z^v_Bj(hKsrKs+1i(jO?k{8^7>Y) z{Ecur0(luX@Gdvxfj{K6UB~h`9ihBz?aL*GJn)A+_emuW=?LXzYhNxk9hjfJUvb8UtG~|IlgNE;vdP7|N@0^^&&6u-DUIbwSO z@*mBMCyEumNBSE<8&}HC?(e<0^^~4VvVL`K8^M>Z!WHWK7w&D!Rd>bFj2!lkY>{Oc z?(%(Ac#~)xIZrt^@--pFp>I?4yDi|W zS@=o6$S>r%m;7hsOewnmGx3iu@x^I5=Sh4(2R<@__yhjM-^D2AR*ENxS1Nrq{f^?#Rr)-3IeA68 zLHZCcDSec$dVUAc)Em-E>C1}%%Jr9oKQ8Vm{R?}~?9JXgQeC9?ZNZ!7B1sqK<^Pj9 zKb+FlDITI;Zxnve3U6=~e+PLt`3&s8%f@97(wT!zv0ABGhSSs>jub*}7$2zdg7|h&Cp}X!%`t=?SAIXZ zL-C2;k3TCiZ5-lg%EdkKP*P1f_0(H&IYnAjTDQF3kRDT4xDPk(IrM2=s{yM4tAWxQ zXmWO<2)TpApD{dgDQ+Ltmjr2_=KT5d={$0=r5|k&Tw;^<%IEezM4pH2aPK38~I zjWcLGCcnqQ;||PKA)iQ#q9n}dDt1*72EUs6f@XG7xfPIT!1}3(bS|0 zMVkhVRae4c1^ozio+OnC#o6PMbKYbyU6G7o>p!bbq_2$->G7`D0#i8HJY_* zL$X}^Keu*KI95+8SIPISU6ibw(t547_*#0a0jq(=(EyE*sl z){DHws(ghxX+&Qf#?Wn4AFzv{zqEFdd${^`ZWg;}*`Oo#(M^daBz;H~g_!sgV(wLO zOSZx8&s2G;=YFu_H0tRki$=PX zUF4rH7j_Z!m)0)wTeI3lvj%!Dsn2@RJN1lJ@0V?k|07kN?7Bx>yHWn0 z)mwQT#n;#B3$W#2HDEQ+C>o#XM zzds~ob?W2#z%DY=t%W1$+uU|hUDu0Vh}uOMcbMa4Lb(>}MV_^bw9=W>M{7~o3%dyV zOR|g7?xQO(?k3J8E-d$yKDvtCvUX9YvWp6*S-U8HoCTdK4dZWM7qR}*+C}czYV4xc zGnWqbFJ9tgI($JD1!O>T57PWK@XE62{=nKr$>k}jJqhnyyC~E}WqPbZ3AQY(2CN1e zL<5>#bg;^DIIap+Kw_UXA;!~wOq^F*Ck@tMmF2cCO94}K^)LIZJ6l1z#SH%g}5sKa~vUZVH zB9r=PEed;K7rCszYV9K6ZsJVh!g61XuS!n&^`bsrFLJxAU6efjVsJGFb`kWK)-LjQ z%x)JgTG(67f)Sc2Ef>nm-9gd)j5lPP=zm|8r^en7MjF)Xrs+o*@hGf*jjcE6p;#}_ zPqAKg8ltn8)qvH2)j&B7$WwB+y7TD5wT+_mM{FGNG_Z@znBn*^`lQ}=k&qU7zo?%3 z=nBt9?IMgj%<*z`@I!qCUB#|x7n%F$3YXaTio#6>nd7rYfyqM3#$REfdKY*7n$kS!W>_vWOLg^bzLv|UeqqaxWgPT6UzAJ zmg+A^7u3f@UDGZy*Nc2>7b%qmdW-9)wJ7X`T?G9l*+psh(Rqx!i8F}{%Y8AvDmmrt zBKQ6Le$gsx7pZ!Z9%r(PpueRIkDF-^Hra$g^T(0dss{yM4tASz-Y-7J) zRIHQ?cP0oL{DgP&trx9}+C><5nB(Q>;D`DVx{6)XE;84PJZl#z)dl)9`()@;izd9V zi=e+GyD0T~5#w&+Oj2uM7qzinRJg&~Mag5ZD8>}RK04?xtzG0^nAt8G?75iEqpSUQ zT@NkJUNv`Qo8zvnzg-lTe|eMY=Yu-)g{Wps_U&o=4}5apaK)#r?|W z`+m`A{;n&&S(NVkMdq2^aivszzi3#Er)WGz-}}V4!yGS@T@>z3#Mr&qHSHo=FFK09 zUo=SHFS2%#282G|(*0T#_QEcL{*vsX)aylzyNNT23+rz&zA8E8*NfUtQ|m?U<<>6B zWEZji(%MD-dDYlOt>Pm_b1&qgR;d%6GSR5pG=B}eYQ5+wl~21LOm8pJ`>o#6>nd7r zYfyqM3#$REfkx4Q7)IrDw}{-1DRCYh?W3c9lE(DndqFc_`s+W$nn*qew=!iV@1r9= z;kYiAE3b6~&&+V}Ac@ar*heSRfB}cvM5Pd?{A8nugj9(S1I zWlBq^kL2-koL!`U*R`-d6#4to>c9Q`-F16$Jo7ikt{F%GUXPQDAyfSuD6(=e`)c=c&c7_y0(4-BH`*7>V7Q> zdx0nPmljWqi|d7F+lOrZ4AIgt)cs)$_<$$$mljXhH}%5Pv-J}Y30KEZ_iItu3p}B} zw0Qb#r`8M4A&aL5mX4wB*P^{SByzRw2%h_t4BE$QNVqb3S$qTctK9_i3 z@v!3TRdYwq7Zn~>`Lye(dO1q(w|Yyjt7to1P0_WqRs&W84W!%3BN4gzp_vHe26KCQA%r(B?AGqb#p9?t0Wuskxt`uilmxKPw*+oeaV!CM0AP42V()DL`I!W?Ai{+oa-0rUklwVnUcCes>{-~Ur<;`30PSM`1;WOUA zTfNgueU-;$Co8sP_ZGa@&bOfRFk{s-MtbT0C9ulk0_N!Oo=vBH@}iBCJJWFYtu^(&Fjy z*s5N5j#xZ3uz;WGYEcY4p}(|v`aDLi7oM(dZ#A$?91+%{7@JLA`)-LI%|MzD6n;|`3M1-SC6)mpS?*P;QhaFhCIEed-H zA6kos{%E{h%~~{YH*qF$z+9z|?oc|TZn?GSA$|v~@Pb{7t`ujQCx-sY*+oz&VQ z&*$;l^T&AY`J>A9KElsGq}&K&Er9k6a!}4IU5hTaUO8glwbZm;S$jss2l`7>&gFU~ zmzR3s>G2*KEmIBbtsGD2FD;&^_w~Zlw|Hvhw{kq8zqELw{mzc3mfRSxZ|7Sztz^KH z^_Lb;pZno@;W=dStO`%)FD;%09#7Q^&w|CXDm+gs=_ zEuOG%>V>Co@eJXN*seOZS`_w@JxTTo^p_S-pY7Co;n`;K)WFg))csl%15fC$9G+Nz zcDvL(MaK2#Qy29w=`Bu8symntE{c43(VePp$hki6E|pKaj;fcV^nRJ{YFt2fD4kKa?D{jmcjm6O>(5Gm zaab#@>VIM94zd2q*+mqe07LywT7QNNoXuTsU6ta8~5f0b%?3U?^iy`94! zfu=asHMmwOl5v+qKOO0RG}hu(#T{-~Ur z??UJAoRGV1aP8iL%-GUg9rTx`4WqpUXanknr)S@Vt_n}+FD;%0 zZp*XdsU)E+iO)GKZ3yj;eXu^vz2K1K}PnY}Tdg0k;@zj8-98c&kEuJ2at?Gqm z!QxpJp3q-fJbfM`*9%W?j$Mlm0j<1#LVs!TgsoFAJV$K(4B@QMc9DB$S$}EqblGOD z7oM)IpH<-r{guNLYtjBxHD{4=E&8Ok9Au zN}uEl{()O=E!usLnz!&4+qLLQahB^itiN)05tR!94E4L*S~Q_aSDJUB(f%lk;=fgW z7y4G!?zp!o*SneB_wm~EdwK2oWL|srlBqGne1s2`bLcNkIhX5|Twdyhr*GGv0g-Sk#}oQXizn)Rz3?2d??P9FC-j#Z zPj0`nGHU^UU&}K`WeERVLSx=rNtBWO}+3eSUju36Z%Vwr_Xk3z3?2dcvgic^j8i~ ztUtSlt9gp->(6uh2ecGd;7y#4IkEyKDWb|bzQPSEpLQKJsShuG-|DTjekz$;NKs~O z6srNNfyUH;SpCT7_7-`!>(AK>zyv{xAGlz^QH%BG!WZM$p9@#1aYnFS!{ZK&mj$@; zqO`!T`VP8HeN5CEI%oql)fSbwkbf`-TZkM%-itz>B46f$DE`t8j+C~13neC#%o+Z75tzOUIJS`m+c*ADFAR;@yhL>z} z+{>+9lw6)ru997A7bWYav|ejik}WH%0jq)0t^qNON_SqaZWq1u*MA6GBcFp?nUYGc z7pZYwVt37B`aq%?4jzQ#!{C~FuLqv#pdZ;q@RJVNPAX{^`M1TtujoFZ#;r6S zljjlgxWgPTlQu(rP~SpVv0KhADqLjG%T>CA{^I)SFa~_Ei=e-1?IPfA;!N_=;H+v_ z@_LasTdfy)Zw$)!&5)1AbmK5a-HB=hb`kVf&Mu<(1Q_Z!-d9vyAvY?6LLtV*&v0jZ zc#7OYH@#8WDl0o=#OJ4oRNNrh-mq$S{HK&#cv89EWBh*Nqx^p2!~A~YL(26YB3YpqxX0Y05eEPbe?-!gFYrJ?}3>OUF?6 zhcVy-p3q-fJW=oKh3Cjc7EfTL#%nr`@P{$r1D?=dT0GHyXU7vMhKg#Oav3HzpAc=lO5HL!r6>1t67JfXj|c=~Lo)(g)O zi>C&bi6g>V6a!D_uN(9Pje^x3B^cL4oYf;!s_{j5eS${NM zR=TDL0S>9Z*9zQCoQYc+oK@9IUVnDaQuD(<$?f1{jS&g zbF}```g3d7Ayil*bEfh}**{di{6~4+`C;X{4{`WDUT?ma*PHL*_2#cDxA0Yd{uSjW z2}64ZIVk6~UVm=8z^;dc7{rxvv`t|w@)15%&Y{0F<(&Gb0+*M1;aM24c!o$-jwke& z7Ejdsdg19Wuy|I3C-j#V&jPpK+3^fh8|U-we5HSu3>2;N54zbEIZpp0%tOiDh2E?qeU4O0?$_au2e&D6n z_~=}!Q{=nic;`9G`_Jx*`1NQ1>uQ`4tk>|k1LI`@uDqh_Pt>>2RqU2qfA;M9vr=84 zzqo!{i^5*QN1m5kqxEOtZsJVhfO#CgDmmrPh4Oj*wD5vme~yl?bi5f{&4FD6{gtze zC_Vv(`d!QQXID!X+9Gv}*Pmr&FP$HaMc}3KRk%;JJMO*8_3mN!E?#e5#p}&?@Oty@ z%Jsg;&sQopU}|{Ko2p6^FFZ#qo*Gy>hPq#iV&Dn=rNy(rJ%u;(FmZWbxF%(lONiS`-6M=r1jvuy5*xr)Tlhz|t|){aO?QPv|c#o<7^D z^}@5y;;Dh9W2pPJCUGOZQSRjc)9*RDS+h04E4eY6&Zy@U^~KSO^sUasc-XW(w) zOyYpK8W+$VN@vtnWi$ExXa56gUCv!=*PoUC;;>d))&F9i82T$`7g2ly4E4K~>(BHp zIb4J{+9M&Y+o|&XXZI^uZ&t3qO1XtwdA<1-UT?mc*PCxrZs7)ge!X&$KGQ{e2019_ zwO)T7vFlr!>f_2d+72z6@S>bUe`(6OJfGt(RZi-K=a9uy1FCX7p}(|vqTbgF&w|CX zDmV>Cg@vI6@=r1jvF59W~!n4ofSrwkpUpYLn z{_M9Z`?K=(=M(z}2DGGB?hU734B9D1xXPfM>vL~c`Lye(n7>FGYsWvgdW+XfbS{)i zs;Sp1ORpuj8n7B@APwv$@|w>bB>s*WqgKSi^{Y?~w3eZN|GBz$C;9u&>mGk7NAkn9 zxiAgv=#>-xtN&2eh{b%ruy7`>kT7QQAXuKSj@c@VP=WjFaCe9==4bG};hB*Ie%+%HN`!oOAc>UR1Y}cPvJps15 zOWKZJSeJwT(%MDt*lO&e)~>fj^I|NvSho6_^5GR zlel8Jns$&G4v6B(H@{s}i+#EN7vlHj`Zq=GB8)rC@p7%~BH!9YLHSDVi?t~1g&_|2%6KRf;oxuLb%mXBSbqAiz++u`id}*ifzn zG4iL0QC9K7Ms(FfR`*M$--P^mkccC+J;v^&_ zy_>lHd~A9iX=2JbzrqbFpLQKpFGuP9R&VKb6&)v~DWW!x)qvH2)c|RLx<$1wciV&2 zPCu!or%Lm6-_cz17T$vsWII#XK+?hMP zc+l~*hEF;-!n4G^5c9H+6(sSkOMJEmRoL{jmq$fPlE}E{#s@^Z#?7~&nE=n#>N$p8^-`Yi?E-KSw z^##~+uo|!$XcP_5DndR-YeBfeaTV@3PN{QTuwE0!F6S$47j09T{B3I&O`g0Yx5JBf zw`0A?2$xtZ!g(8}V#IopAGM1x?l8y8wX%x}OYJ!>Aqq)-bQl9Z*hSD^wRRD3H*qF; zVg04_(N*lK!7$2IK38;(3)@BB8=rXah>)RT*KmI;!Y0u}BU1Ww!?V-c?)zn8m=U)-Ei!kmm$IG>{i+pPr zX&@x^(OMMt!Y+dTs<6IlIXHAfMy1(b`4HV=tv${V(Ax*Na$x zY3(9^RaU#`l%7j^2P?OR!bB(9MKi)UweZQZSon;!i;~MzQhO5Kw{}sei^}v^iy~}! zSPfVWG=>JYKD%h!(u%isQJ^2OUUYfXF2cCO952_(F7m8h6lg!WU)G|q7j_Z!SFK$H z+)bQGTrKP(pU3ru7pz@WDb8dUL4RrOBKMZec2WNYed7HhC-W)&iFQ#tm)#mU(k}8k zR6cP&XN`CVY?_+Gw~a$W%AX~%hK*JnHFnTkpG>B94&_%Ic%(O1t8f3%dyVOR|d& z{M?0HzB_5pr8xVYF5u)iz!|ukI8*Gf+=H&zRpkNekTLt{*e-I{TDu7Kw%8?YM=!!h z?xU-8z37^(cG3HKmRuN3LxrJe)1VO_ulZ}>mF2oHL*>)1dzfuTy0Fz-Iz6SSt2HRo zmW9=T)xfCKz#bx3Th~6iJw=L2ykkWDD~}zijf7tx4~o0u3de_G9J;?5>>?5)Px>QQ z%{g|8w5p%7gUoQYeRS?|QM(A^4s*OrgP(8!l-Knqo`hl3E-Ly?^b-4i5n-w>(p%gQ z&qEh>QGxZBWEZ`7_hQDTGk5*EWr=nXa5r%#iD9`9aaAb^!{zKEkH_`iVrv)0V+>t} z7?Tfn5%iaOz3B4Hc2WOwr!Sg_%0gkYV9;^lGwx;%%5v>ZQ~9jtx<_#e^*XvWtb0%j zQGSp%+|$G6J^F5GSi9+Itu9(;E9Q)}sI+eRbvC4jat(i+8~40cnzDSX2CN1~zXt4j zQ7tf@Aei7MypeOd>$+YvHEI`O++mKFqk|vS&qe!Y(n3CG+C}Dik#FrHQWRfteKK^5 zILd`x1pOu1MXA?|7|mT?GB5wTt{mm7Xg< zkM4cT2A1|O9_U%v+j{2Q!Jfsv#fd6?C)zYg{$;RyQnZ(wO4lCBdP5uec$S> zw0M z&V};icrTD`rrQjIZSB^J7I%Z^1mVFC_b}j4>qSN1D=PE-B5*0?iua3-iP}XNcbMa4 zN(<7X`VQhN^%rqXyU1KG@~mA%isLJ(kH+)Rg!8uv2>B|C4TT`yAl)9a?` zN0?%gKm6JCBIqX6iLM{zFV>sVx&3Q3U^QShP+kKq_Wh#rN=gv}HW9hv+I;IpM@8)- zj62Nna&+*c_brs%Fl^dI=6aE9?IKbbUrBv4o`){%BIqy4E=s*##JHO{lf+usMMHSM zsLR?##p5pE9^wlh>>}tdtzG1=%4!##x@_JBy>&T{t`nK6jVtYBR*hN?`GQL+N*1aO%Gs<-Cf_r$>F2cCO94`~f z>ii{2v(#IxYuZKTdQoA}zF$NdR2S(n?uX~03%dyVOR|enuNN`yCe9==EceMcD?7Qw zxeMp0bGp4Z7TfO^#bONIh8U9%c2OJaFRfkV-jdZWI<0@<+`gmV*K@(b{`r?=v5%rt zCR%p7G=B}evJLhQvvyH@n-SNJ6yLLUQL#?a>9YnU*s`!1uo`F(4QzdOQ8<2&g+SUu z>WLhiG_+mh9~!lbFzztN%eAtLd}|kJg)*s+)}pW%b`kVftz87%O`J(wE$pHKuNV0n ztzDEn?uy5m&ZC3=(%MD-HJRJSDX!;eBft zg}SIrk5!jn%fM>DYM@~>5ZXnQWjci+99M-(*j(#HBynsgHqKXM7aZsTQ8C<%KQ=U7abh6i!kmm$IFx!q(}89#G3k;s9SEm$g_5lQdyw4 zxPDrT!d}=#&|kH75pXwgCUIf8FUD6Tr~LaxBY3~)1#1^2kG~jP&B1yR^q1Byaxc$n z7oD+aY0pxzk#3Tfii*5pvtW>k1?*a0vaRvnY3-uq@)XyWMDJOLZw9+)v+bkvTcdUn z#vSH(nNSYb;ec1MTh1QuI9ilg8tIlMgG*Rc9A-S?&#BcdoEm(WgDF~U83cKyt9pPl|k7C7Y?v? zQF3`oYEQ!Z)-DQlQJEgAEWMW8YQSosfiw`>McW>%M#bT{DpbRE!kcs+oob)LxaEDN z?IK#6DsjDS?IN*mG;@a+4?3Q|<67oCI`ex*aT*cpMf*qXB8)rC@p7%~qQWQa`$ZuV zNquw}13uVA&|kH75pXwgCV64~rS#EN>{59U_jyU6_`9xsd``Ew*xE(O<1eYi$RF56 z&|g}+$UQu}U9@aq@zU9YJr_q3l0Kk{LQMP9LBEYe}sJtOgoP1EF0sW=yhOw7K@t(YXuZxGolot==wLb@8ck41fWL+D9kzBJSsN z$}Td)rX6I4<8+bF`Flt0B8)rC@iL`_&RSPj@i-ax%dHpr)-Hk)g`3nzYf;z>y9oNL z)-D3>Ce9=-tUt*(tFoDF7Y*@xk$;}Giz>yL-Y{gho~hSS(V z;0#x+7mbbDMHqLO<7G+<(xdtl;*r<&D8uFKBG1}Iq&U7ve{nxN4_(+r&|kH75pXwg zCW&F$F2+}-sQf-UpVy1rE^8OXV+`Gf7?Tg{MbKYbyT~1z*)Cep+cTJH10AP4Y!;$f zap)VfHN0e70$F;Ud$PWwVR&S>S}6kiQ$a2sI$9CC zPwmH6_mb+y|7Y(#0OP!>wBehCgjrfbU9$u*8;TKSV>^wO$d*ZzpE$|JAs7NX8A&5) zu%r=FWXqI@z=G%@3#<~#W|wBlh5$=1PGScPB!rIWrY!3PJ{Fep!Tv1zp68UeNwTDw zkz-FZ_UM-Pz3trRo_o%@Wnx=kGLKgIS&M_^HP>^-t$bD}utI?q3M_^KC-9}SPQo8s zE}DZsDla@#`{>B|=bfw*dJrp)Pjnxh2IIf;%^-(BFG`&+dhK(6fx94LKAu=FlFHh? zI_zo6>PE?M+^*P%^`d*F^&*!$vhs48o~#Q$v)!y-H0$?^R`enzT|~dQU*kSbykcJ0 zi*iQ4`ShYi|9+A6d$ylhT#WnK{j1ekSTEY;`{=G;(Tn1K&Gxl*Zo+ZB$mzFOy=d~T zjq63_a&52)#VE}h5^d>D_*>u=p0n?SmGz?e@s!2a?C{EZ(QKM5j-D+*@Jbdd6j-6a z9fSg$7Ja=)n;gqkF$uT_Yd*bb{rdGLXUFd!+&BMv5%HG+>l@ z$8WgL^`d=7zxni{MgM-0^?SCTSze6$`TJSN81-&`W}h$GXL`}(wJUnj{BoC4FZtK? zBB$SC^`hKS4eLcCg>A*v-R1JO26dx(+3mT>hBfaOp3{Bz6}@PFJk7;R(s3(#QJNl$ zr)Bd1UddsF0xJ}_BT#@mhSQ?!MJg4?a#c)&gP$*AK1=x=Trax&!LwpnR-Tl5#OOtt z-*Iip@48OjHLVx9+>w=+TcsCGuINPxbP|7aeG{*k*YzT&U$c6V_j|UVSzJ7RWqcrJ zw!4sC)NSXB_FcK67tP(rPE+e(b6hWS`Yl#3n*3c^em|Fc*W&(5=RH?w<3(G_{k7uA za@^DX|IJlk<8i{X_x*+Pt2_37FcQ)6LpSU9u)pW;tn8zkn^rM>=6~Kea92{UP+)}u zcQgttZXeyD)Qcz=97HxSoha(3NH2? zt`}wZ(RHurMG2T?=VK2iUNNuhMNYr@^rFS?qw{{x_A`r%aX-6%wK@y$qdVaDbiZ&# zFH%{{`!?xX=O!H2i=2Lo)r)dJZ&)v?_tYwd-dds(OY^2#Ln1Br2ej}be7pO&<=2a5 z*E!v1xBodA)-X9XvwjczdvX2m*!rZ?VK!aTV|1?Pj9dAvP+)}uD->9m0!Pa)xh{OZ z=uqoL2dVjrUK9}r=Zo%?){9*3$jZxU;m7CQ+3rGmQEo*qim0ESi+#>^nkHU@*YzT& zU$c6V_j|UVS!yfvqCwM(x=&uwi=2P+)}ucXSG*){Ana#_V||gcZH$ z;5c~*9{U&G1@B$B&vlDT@{AsKVLNI_jIpb(Tf_%{*}>ZySJ#VPpzT~aCzIqCi}$b4ZSk~9kK5LOcqNw=3an7zjz9rU zixX!h_e{icRowoA>qQ~Eh7%{8(|7RmMR%JsO%%zptUU6__&&O9y%XV7{SNg$I=^>e zS`}};-2`hThQu~?fMNYqk^rCYv3gJR@Sgv-3oG*HMs{asJ;B(GJUFD&n zVsEWf9tmp~czKDB&&T_de|Y_u9vuT}i8+L@rej>v=hmpZk{~>tVXdxTHUXU%%kg-EV#QDQBJcU)P<&H{W>m)TbXY zc=e~hc+8;G@#1SwI_ne9f9P4yC`^l;J>7UcUCi4*MY&0fs^KYVA&JXA&Kl8FM{+L?UiCBJCTc_xH>_X!(hETaiA>?oLOk ze6YgDBa$4M9@j^lUmi>!x#;{2`D(3F@2%xaeWR7~XcPKL?sLkY-F50^`K#B@-#b|S zZZCa_n@pUKu^q`au>Q0|I)6((@7fT;m5_W~2ERCymhT?y)3sswbfK5EKo>eHu;pU6 z#qV_N+ytxT##7h-#(3cg&;)M)1rCSL>J7M4xKnE~7e7tc@uLs=`zKv}^&im={akgI zmz;IltKJ!fJ-%Js%})kSVK8wS%aQ+KAF>r}5B>b!lYz`9*Q4!DwwSLZ!$*>Tu6ECL*IjPShebAB zO4}Nxi`Qu>Wi9nxOf`$^?`O~M@1>T-_ZLfxL(z7x4{5;feOS3Ae!7pFrK8N11Lpzx z9&=Rq@>O>^4t};UgfkxXZQ&n(*uPnHo!q7|!Pn%)>dH$ie-284TW`4qvP1|!eEeVi zk5V;$ow62XG3VcW!gq3a(sAt1y&wHK-ytUWe3b7SpMMqn*)kB-@{i-dGmh$E_;@%f z!MO;Z-Tt+Dc^JRzDIbP`x3D`C_UG~}@!Z47=TZ8o9>)Kj9`U|gDYNB&vg3jE7(yu? z{|GdW^D_HPKPQQ!Z9Yl*OzU`(%-#ClNw8^qnv+B_)it1#0)J~lB-Nn#xS#Q!C+n~u z;hgbC(t`QQZgaTveV%&B<^nO*=x5*0((7`6g^UiiZ{qxO$|I}KF7y^3b4jsMEACio zxz3wB9ou~+0$z_(K#=(n-RS7M%FvVxD1`{SA7NM4)uC+eqJPZk&9bBVvr zetIYZ9=`?+(J|olI9lqXbvjzw>i(x)5W-fxKUvnb=?5br>9E+((=K>gxiZwZ*w>fz z-ib?kducCMdM|vEe?x@XiL+LxvbnYs=-;4GtKlZG=PxR?4!ln3mmAx0eRfUF-=xdF z$^lsZ-c?AhIV9hj-+%taTLue##megQ%YDUKrC6N*)%l)i*U$0vfpf$yHOg@``N#(g z9kjNr?8OaL_P|junvBr37a6_6+ z?*$(dHlY`f3b|jel6G+9ZYlR}E7lZY{$9Jfjq;DJySAtM-SF=H?(}jzc=(d?P<^=Z zZV%0-_j()?I`7c^?sQnHyXu{Bzmsn}pLV|O`8hxL@ctKmI>Wa&7J9c0RLb>{B?(iF zrSe*YaBuYb75L6%`S#hyw_mP%k8>L>Y;K&xVttgK4r9Lklnmdt_WStFZJK-j!p_Wc zWw9ifFP%7w;PQYJUGH$JA` zmd1y-_jQI3rLXCQ53QXW;luFY`0##k+<5qD#nQlFXN* zLRf`DCZDf%tozPW&shmA;=C>Al}3tNYU4vqC^hqBKG*v6Jg+V?ntZ#n)97~A(QA~3 z3?Cyo(7urPbe?#2b{{<7)%0`O1~skapUCLQ^{yH@P2@p$i9-rE9zG6@ovXcD3d5sA zrICR~TYju0oH$GSg#qdlA#BFqQ4~1&$p0hjt0%#a|BL=?-q1f(h6G<2dhE$3uU&Mq zMZNg)MZLA~OE_D~-|v>M+MtOw|3>K%7XsEuNVUbAQa zeO|e#`z8M_{gXTXBl5iam%o*8_h;3euYS$OAA8?sbC^5*s{830Ch;Nm5o@2@M{;k| z_GVLe&SdS{n@dYx&_t$l)C*BmoqfqY6U7_=gP1u)tV+bcAoc^r(*lz@f?8p5WWI1cU z%dfd#%-wD*N7u@ge8Wnnrcc+Uu}~*^@u)C)k2&?<28sdQCVtbkYh5#EUK?x4p-eS> z#h;^o7yF2{*Yz8i4f)aeB55BnJVf~TnT-#_jV24h@No%FH#cNQYoO!2=#+a@=vI9E zIcq;_xN4IBu1B;tKBit6A{AJ2qy;&CmDXSp>Y->{jfp) zw&Kt7*hzNf+8ZaU9{AsBK4R^4J`(jz=ZT*Q(6gd>#_$l~e#d+eDsXzjL1P_AZDI!$X9RpBO$2H{OFA^3Cz_R`CzaGvJGmLs~u0 z{7rk~W7SZM51D7Iy_tE&yL-01)o>BvTkTcwPcw>|JXcZq>7JN*6sjK_(?afD4ZOP!n+Pfe=%ns!E$bDDm8OMdY7Zt1Z zp<3fYtETtdnsK{zU9ZLKzWeUg=J+@_jSp{cR$iT@?;0K=e3-p3XSng6G<-BHw6+Q# zltZ}g+iH0=cWQg%W2zM6gYv4kchPc);lgp^xXAJo!%4%!NULzdc}DgJv~2$wZPJh0 zwCmyRjgzfUj&UOPRcmizp7E|1m-;c!D*1ZCUmmS?Hm+0f!{>5-z}8iKon`W!y3XS3 zOY$=QT=~W9I*XnG*j%U;E2RRD-Yx3m?>BmcU?b}PJbeE*yAO5R^t7+cJ&!J`a;cYY zuk!;x1fCy;^_3UFg@57z)>|HeaQZX#q0Bmqwcq(b?i+K{q1pZa&8GKOwEp}6Y4yQb z>rr0b;pR5FPP?in<^%gNl)ay$e*c<{1H*&2yV?E!&8GKywEp}6$IHJQG5zjzSgJd9 zcBbF0pQC;^zUO%G{5O@VqeD0_G+Z2MX!mHd>AfCDg&V-NTWQbac)zl1a(#Qx`_mia zey4os?On8dXt;2kcz@&!KOVk#u++P4q*!hAx~^u^dp&SgPjHfJv7R}3kM_pNs!cIY zC?9%z52j~Q{&RURlK-xxTRoiB~t+2l>9k2CCS@i+(F!p}DD zYU*zJxORuDZ|cJIIQRA@$N7P`9Y1@Yjo~4}$LDQ)7;e11@!0g3rMby2z+;!h0V;;!uYiu9qs%K^JVeL)gL-zy=r8}ej6NZNfANvd+89uUPh8=gP_?k^0 zXSW>qc;d#~la360_$38pvHsm&&J}y+} zm)7m$B>(8#FnIcrfsdFDOY=>ADvb|suj51aaq>(7KZ$d$h6l%oK8Jn6aO2^I4bYZB zv+2DatzYjXKCZuU(thK{%V{@v>nGFr@bF^l7$#n@#WaIoi)L zwrZV1Wcmkm9XO%A`NyhHr19bHb$mE|-Su^dNz*%B56!(s^-$MO{rwi6_h{-M*SzOF zZZf&)zjx`mk6)Sl3w6C6?&eNNpZAzFJ=ERreeMN&W`2Hu=zoX$yvOc6Av_x2H8+JY z09)Hf;C}+X>Ce>F89mh6@8dl8=egT$`}y1Oya(xa@y0cGYVUDA{ju~o_x7gcggMT8 z7#<>g{K>|L;l|Nr_-J@vgQoY~8h4lLyvN`v?TwGAkH+|*-P+rmv0F!b=VtAHFK4R$H~@@#5j?C4c1=gBhkpsp7$_3MELlF;lps_=?Ohjerq@}{CL84Kku=o-S=63FpU#$@1k*HxQKA#`kmp&adP;a_ptMYt+HQO z1J|_qd5^6hh;c%B#M_&&Uqmu%%D!cIaC|r(a)uiZA3o88g9<(K`>zX{az40;qzBE3(y;*s6mcDCv zi113)^1 zv)KBJyo^7sU1wS6*)G|gcb(;Drl)<^+;5xvjk)L59rsGFvvk`!i@WzYe`3#5H~sm> zL%z;3aRcVXzu6Ne{t0d;)>=LW_e^}#pQ%q~)>*9m&IfV_=5BY}uCoZ;3VS9`Zf`!Y z>b)60VC{8zV%~eT3=a`Lern^xaO3D|_jQ);2emgorr#aogYty8HzQ9(I_oUC({K^t zOhjzHc}&{CL84UuPNo>-NUU^xM-o@%AnnCx(j%C$8TaejF$5zRt31 zReR%P>TNMjWZ#FiH(}q1WY*L=i{ZiX;dsayZam!X>nz=W+1~ir`qnf)yuAzJ!|Xtg zkKDA*GmZ;)%|6nyM(h0otK*yi|H^)G3Bczd(*>MVWN@DSm{?1eeQO$HxryUsG% z=J=StHpU0#Rd4U2zeSKVG!^)Js-3k}S%f>nxwZH~agp(F^9ueaX(Z zn45k6(O(_>cY}%_x@_;spLizD${pf+6()A=4VS%gZwOOZV|mBMaP2$xhR#H* zn^|YEc4qlNI#mcyy(Am6v~aWOy|kl(-mllPdl-dgzuJ&%^L@r!EAhE`&KKU^#mpC- zc;9jVO)R(SimUfXV{WuwYrpU0e8h8e{^H@*uGg;jY?tibd-XiOgY=U3>3O&-&He5F z%I`enexYu*psks6aoOkle|P`AUpVIn;3J3nT-?CkFtHANTqL$+OQyIvbWxA2ae)?9V0&?VdMb_s92`A4bWFY)%eUNY}` zt>GcU$E1xD!;RCY?bmBR-ro3_zA46stk+t5GxJPj`LuP@=OfM!X06v69wK~PVfZlIc)G)9y>@xez$ z)A;cAE{G4qLxhj33?GIY$49%b*Y0~$d*fr(_hNi-p7Hi(<{9tq+3U52iwGyLHk=rK zJYl=9*LJ_Y&2h3fjT3M0qH$ulh;ZWio#DrEa`>#*+Bxi&?MJ6x^Rt`Qyr;c!GX2{a zCzMCLy$L&MB(tX0YYh*M5645!aO2^_XT5eg@bQ;>x?kGf_}KdSG(Nn&3*y7$P0(0 z&+iGe`v_hrsIsJ;H^J81&aF`z?|?=#<(PeT;wM5?*I4P~~-L@aMNzB3DaC-A%z-&TGW zDs!&q6R1FEla*i|bLG91xJK*Hin6_)ORWRn_q45Ymr{pTm6^}v| zz~`+lGnwD+1-@;eVv`MXihT%S%vChNJJ z{@b(s+q?bql~0=R*>cjIc~9Uw-{(-2-#K65h@ZfBx!Zq`;^onD(tzz-=re&AmWu`p zmF>@$g9e`WV%=8O;qt{6nt87kb!fC~uL|qXXgxfSX5CJ!!(!{vY?}{O-(+2!HguP> z%!Acu5wH&3|! zmHtY4iM#cdTh3(YC?mpTb%)SJ-oT$TUaxfWu)6+xJL%X(6B1>K9qefIgOzs`;;agp zOy>@sfAN;VLSM16`uuWVu~sQA=|>wiOS2Ts&i}&MQA>_-d`&*`!9q{1Eh~F*LzT@P z^`cb292W{*A^d5>zMW->&yB0wUEV2Scb8o8MJ71*zKeWuiLb4CoVJ7b;O~KY`+UE- zw=mj*M&P|7Af6u+y^xlfr{~kPPH9Ctsjce*d_Q z1H*&2yV>7!Z#KQxqxHWB|Awyh>z{GN^t;nxsqU(O$n?AQbJXv~_p;-@+23<-HodpA z^}h$te&5u!@2RK0k8)(`&uwXeW~ zaY=d}wRQ81=A3JDePe1Qt#5dHlk?2_^&dRzx^rH5*826;&WL{)9wK~PZ}>3Wc)uk2 zcf&}jTCP>fqvQT?5pJ{T<06&^ACqm4kFCRLe0Y14_=stokV6a)5k9Und>C##&E{gQ zFf>%^_3%>NX4A*zwO&r`@4so|t~Q@vre2YrU%b5w;=}L|;p4rA55tZ3%a+k%Z+)mx z@$gdJX4A*zEeAf{uyOJk?XBl-eR&!m-rfv8u*S|$f`1quB7D5t@L{;o_o1g6K2~2` z8mW2Y65M9f$3?XsAGh9e@y6Vx?TwFBFH7UY+nd3MkvV}6!$X9RU4{?CjrYq%<@&(j zNU^$9{kB!QIQeNnkqsFlhi z{%kRBv+3h9mScX|bmL%~<74aJr}5$KT@W9JhX^14V)!uJc)x5JEEbmp2v2Bkv+3jP zT91$Q>yN$ZK%3)Z)r-^k@b)f<55q%*k82Gdh8yn}>EA|$Q)JR?dauXw%r8&BX-%8U zr_)!Y@!{>w;A6J_Vt9z~@fO2};l}%=Yp7VL^p*>a=q}8o+4NqI<-o@^SHIz=U2Tq! zsTZd4;qA@f!^oVFKMW5MKHg;bFx+^*MEwitqY+J}+4NqI<-x}dH+8o;KBixgwx4=? zllXYeO}~4h(m8<-!$X9RYYZQT8}FBf7F|3s+-B3qS*iTdy8T|OH(Yb|&u$uQb9_ua zKaCG>uj7OCz2@qV+5EC|Q?&kKc!=;ZW%w}Mc)wg+DU4KyOV#QU^_Etp_k5Ovf82WB zUG_}2IX<>NFO3gxuj6CheISO12p?}Sd>C%LUy}G}WQu7vz1L%T@bTb12iko7Wz}=j z`0(~Fh!4XABG$6mnS#DJD5ka>AfDU-~XXGuX$#B+t;VZ)A;cA`u-26ueu5O z!|)K{<8_7)!;Po&{$0HVhYLprYJ>h@k#4i;<6>GZe`wD8+I)R~`q^oGczd0HC>87+ z>qBejM)HT@A;QO1h7ZGy_sfQ%(ZPZT8gQFU9~aqb`u9wK~PY4|YQc)y%qt_&B3JhXJT+4Npw>(4c<-f-)Ar|enN=J?pUJ;n$3p?Z7M z`%s#;mI`MW*)+FakAuBCC}?OilZ3>S_Q?~k0}$HPx6 zmIekJHM%2{X488;aHFf7k7=FVOmpt;Z*RS3subfy&QDo;T`qC@&bpV}&Jkwu;qSZK z(4j_847b_zo)zy`m^uFFko$G^e2kvp^!FC;;!NVii*YWoy+2dc7$0&^wB7fTy@%PU z;OA%kyliyt)b3xv6+H5x{?iGc-}iH{dzeoTr-$6nwH}&!{=dvu?qN>6kK>>#pCgC@ z4|~|dI>*Mwc27-B&Ak5gub+9@%U(8v@XTeGT{iRh$3K2Io=@E8KKJQtb(0?Rn8$Q} z^rIi0`NStaG4t_{e|+Y{AO7&n```cmnRmbY-7{}_%Ufn%{pwfG)a&(`OD?%&=A3iR znK|vW({?}N5s#Q?MLk~o+Sl&>>}Nkaa>Y0g&%V(bX%xBI#`N>b7>FVm5S-W;E^pT~1 zzVxLp&3yH%U!D2d*S;n${a^m_m!&SZ+;Ypz=RWtjnVWCEIaP;uzVn@ei}CUCnJrtk z%xv7aai+7gbLO#+ee9CzfIi**jcdU|^1+u#0n^!?VizBR)* z*5T`4|N5*turEIS=}!wT-}9dL%)IGMZ<=}0i>{b?#xtHFWk2BwPY^mhNKV)fe%{AA^c)0lDi)UCq_`?kQ;)D}U z=$sFY=)2u4_Xj`tLF&u6Y#m4gVlqhsj!W_fj>&)i=YO7g`O9BE)6>%8GDAH752ke8-fBy3_9vz3|jpRu?cI=or_uOvs#2M1y z(T{%gykn7V^s;~SqaV%q_a{I3$;`k1`@hfp_{Tq;qKkLWgd9fyWS;pA$c(8pM_}eO7GwKgs*N?LPJo*7uZ$y1Toj?f1CHJvtXE z*Pnw|c6vW}zdPNNZKo`d<$WLe(1%18;{4a&-!DAxth3IVcg%a4S(kNq&U2nK!Ldgi zWce=n-h6H6{QunNK3C+Qb?es6JKnQp&K0gyDih9oNdq7AbIm)P`#JBu^rbJAF%P+U zVy--w8uz%zJ+71WU|+ER*>=YqG0!=Vbf+9l%z@uG)#mx?2maAHI5;=~Id?baw%soC zyyPV>+5NodJ#RPU?aukquEZan6!3ePb9egimm0f=c{|QmS3_awu_vFrwzX}XYLS5= zd)E=yhvIKkXTnP_`H374`27i+RkkK50`tGH{wEb-oyL{EadUGjDUsE?*|D}d6>-heS)9<|wcIUg{yL|WX&92v)o3JX4GI!y7%%%{gZw%r7@TWhk zZhMoom;Jb(c(nHWIzeuFZaOr3k8-o=y%nv$hZ%I6n4U!^FYj=3bN|*D|CsvEKSkqL z`rZ0D>i2KhI50eTyPLg7x!LqykJjJA%<=NsBc|V-4oh{n9?0~&^>ftk#`m)0zS(<} zn@#WSZ2kTMj`t6Bb)PVvnzy~Yc5$bE5cfOxD|vh8+g~ti{n&8fIB~k<3_l)j_8#SC z(|bLZ>mKIGwQY`*Z^t;1d-JTl2ispD@}J3jF8@tFPvt+C3%y@1JMPn&dzd$ub~Lc0 zk5sE0NaocD{5|CfeE*o0|Gr}L-WSciCGAET%;VYj4>}*ayX8*fgPnqj?HRN>cXaOhV7Q59rk)oj=)}TE1a(rj~uEyt0e%oj6XVUJH>GZ7| z_mqygZQ~)A=O*sCH-wL4?zs=jen3~a;qH6G#El52KdWxb$aB_yAHO-HLpl}iQz;LX z8@_B5nKhf<>(lb{u0pqoje{p18SX^z0KPjpLb2JEhWyY86F%TUT)5C z#=O-299lSe60G9G(Nhmy)N%NeP`Kyj9+H);qSZK?7shI(|bKyKmU{G z-U6Q6-k<6HF+Mo|`+V>7e|O2&EzPdu)HtE~sf<0xPqzCTi*p-G73aW8|Sb%-z&cI^*s}3eiZWHA-3mquEu=-O<-|fSLnRUrZ90L8v^PFh{XUHkZ*O}3xBCo|_gxqs z93S2fSw7ONeAjGxuh+84hxcl8e0(*Hk8W#k8XqCdk`HY@&Eg~O5A`)Wzc-uS>(g@i zQ0Cq{_NMg;=$+a5#^f-^M{XW@tXV$LYfIw>Lhfz7XSs za*4NhK0Y!_|1?}UPP{*Ih93`KQW&Z?!$+D;@AV)bk$a8qz3;s|XA=MJ_t`^@{~88P zdfdOgakBNtF;2)wyuE2YlD1FCI)>?`k)FEI_A!|L>iu%zmlolAs?|$N7UfJn@=1JW*}EQXe8b%i3qM@lD1&)C`~KDT zw_mUBBl7Rx`-u9@;30>2Jv1D9bGYHZpp)N%vhM>va;KZawZEQa@0$8bhL2dg7vdw$ z%7x9Qck;L1-UYf%e6y?j9!G|}kb7H$`&j=^nvZyU=i?)@^bZ>sj+5M)xyQ#=@2Cdd zX4A*T+I^X=k}Em3Za8k!n$wO9oWyikns54NF-}CTwDulMuH27_#qki~!}w>;aO1Uj zx_cUQn@t}VyA1dUf9g7LpZ3Pb)F0CL@b)f<55t4w!~4N;hhz@laqCmCr!@ue!1{BS-rHeCD6uFc`bsR zm#jjb{NL>Q=|fF^Ty5@y(r%o?+`h?===|>PX>0yr?{lyG`m)QD56f2PhVKHeH{ttv z?5|tBDeV9D-f-F8+4AJxj67-W_i>*4>$&BGX6;AKrjMJe>!+<7=cL(*tQ zg14``>K7S&SbNj>m^gEmo?&=!e0VFee8ln5;BsjT@v%(5+tB86w;3KvFl5(WW zjgcJr6qO@gzI5ueQjWxpit9b!rMk3B&M8NpXmaB!bMKdS7b<7phjdjgM|?ZyFzS$dNXmX7Q1wOS9v@ z+4OOJS}aF$PM&y0*P6d-Z+vY1RvI7P-V8ow%aJBGx*VC?pmL|!2GSP`Bdv8~m zcsKm7nW>*Y(%SFi zJa?Bnm>fyEg>u*ABae)6-u%aMkM2p>L93^xu#mlGNtpskw+ zT5OLLe4N+SecvMkAKo8Jb*GDOi|&Kv{zz|c5+Cc=uirRpUEA>B`0#SGe8ln5u5#qQ zRqc(BspqHh;q6W01ARZ6f7pDQ#fRg>-Dbysv+2DaEnnA`Ir-nZ4&1N3@iG1EG(Nn& z8GOu=BVBHEInw*VU1Cj|#hwJ}UfAR*t-r$&F{e z!tNhTyKxS4`}V!T*0s$&@%3NX^VD4bvdfXPYx5w;Ltot!R$tu}F1sGGS*MELM= zVz_a1Z7z+tw;{LL^l_mrmLmhl)!VyrN67s{F&&oXn|ewbAKurjj(b9$sPkzSAKB6` zDpu=5i~TWzY+{6)O&@33VtXX#Wblt&ZEmlgs;2Sb?M>q&egDH5AiCd|_FC+f#Q%`p z|L^w}`u*0?y$W`(ncLHN4qWoH^8=orpHKC2{G7P&zx4BfIot1_&Ck<&e*SwsUQe%I zl&{@`kh62l+59}c=jXo<>gD+TjQ;z)IlI5pf5#`ApQrcy{C8!%9M8vp=Pl>Imu9~k zn$6GCdwwo&csc&NBc5;0f3L%Se<+)ur}zB)_prPi*B4x$$hjTY7)E?z1Pc`^!>WF-y-Mx70iw{D?dvgKj(YhR*vJ?^Y!zPIorpW%&!xP{ddjp2|vg0 zYJB-wEr0okw?STURPH_39Q5})pL{1xz;PU4$V$G?d{;hAP~h&M=<3kXL=KH7y-T=9 zxGLO$YnA@4@l2XX`G9azcuTk~6vKGC3g5-1`Zom9;!uB1!GVwK z!cXvH>Td-K?t7?<4^Ijgh7W}Q41XWqhteN#$jg6nct-fQ@Qd)O@UgHeJou27e>9da z&kKFw!{L|Vb>YTvVtD8wE&rHsOt>g)3m*x;3RB^;%fbjKAU6J;|;gNy( z-V~l5{x&>2TpG5A-tfXu!cUEjgeyZOygpnJ-i-^peiyC{U%>^_Q(-jxLl_FL4g=vO zAs?O}E(y;GPeCf;^ikn)sNI85x8r5nny)@Ak9oWVn4SvzFtWZL-W9$V{x$qKyg&Sh ze6NSgareIP@3`pa_u*6F|Aya3A#Q@j^$^nB`v^z8oGA)`!2q6ZZW*!~JADa$b5&csy1hE(%*w>KG{T zkKyL<@$j3uiVuGs9t}=Svy-L1OS!g(4`5wd!eCjUNvGCH(jHnEyJ%#oxW&fz|6+DCkT1OuqgK$w$&KDZX>_Tdes7ep~a& z-Q)RMe|Oit=vMOkoVmO1a*N!{K5cHcRN9X_5$W|mm|dD_bKe+=+sTM|K9OBI@&4TO zm?wjk=oJdAP+)}uD->9vzzPLcD6m3-6$-3SV1)uJ6j-6a3I&c>3eYZaA#5XVpZGFb zvSJrFV!Lyt;wuz5EGa-f!_A9s|J;C$xJf?4=aYEu_S3J!pM1VhqyCe-uMTSGT_}fx zKWF{;TS{)9{wn;eU$@z-J@uBMa&7fm4>j(3d7i(Qci<+qi+eq!yt#K;EvaxX&-3^4 zCapZLhm^N|NmwJf zwNv|!3Zs9MJLmV8Yi1mOTkzog4V#}ldhZZA`b(8+Ep!YO__wcE?X8qXYo+o?sEicy z)p}2De6$$yqlLapPkC18DO8JmtG|GQTlqqzQWy`ZP=2U55{7XubQovtYe>fyo_y)LXN3-A zSE?0<^Oa(Mu~HoAE#{eL96waX&!21D>{m$g-ZEYSK|HFj1iIIvAnK?QmEtgdDi<`~Ug`rXNYP^}hgN;9SI zY0nDCywqDB$>SyDQyA(sCtpV!%K7?8UpYTouMT>2p&vb&h^`i^?DKrR&SH|SERPi{ zl~P|ZkFwF+@z7DOlm=K($LM&zG=ip~Po!#moZnt7Y>WFUI4wF@)jZq6l>MclqI9}6 zVC~84KqJPnKk|j1>ZKahfnNnJju2dh-fhJ`zM(`t`BJnQpcq93*ukhHt1mC0vy(xq zcHXnMtUbEaC(ya{G-SA)cpKy&LYHpuU}0nc9meiNLx)ENk?bk)42;%m0Drwwl$Xnw zt_NOam{bY!0e}msDz$ung^nVqhoD6$dmmHy7 z8P50BF|eLfFVsR$PZ{;B76*pW9jK;;E1(IwG>#Z8SJ}sLgk_Y9@=V!UjHUqCR%?|4 zcndJRP3u=F*Fag2UxU#U8pcq+kKp){0; z6N;5a#*!Hf7BP3AG|7$cGcZ&dF4ZtK#Qwq#`m424e?OtgKxrE~st8cRhbnF_mIelE z@B^aeuat*|)}s1Dm=}8Mm9gTvBU|0`boE?NuUQm)PY*@RU~A>?x*!xk7t<3dkCB}p zS1(?)xN^Pbk$5f@ly?!&fZ>CJYZQf9iNR z6vnDyqz%wv@v-6%Ceh(h&gXQCD3O&NDGtFSSneth zgPmd0|yS{a{rbo<%Yc%xvX)g*+ZSl+y-AWT+N7 z^?@ofx$6TNe6e>OCL8kibA?6gpgbKYFAjiJ3&pG8>qTKMaSI z6#Q`5W2_C0=gSrBeGKTcp;E1mW{d)BV3&~GN{GTEjZlbDwyHbcr0t=9ZRlS|!3lV( zL8$Aa2-ZVZjaTYt>87cot!wx1gX^B+5#u z0CH9X`T+9_XZ1_hoyuNhM83Dofh^u?36v~i>hG1gxUYgySF!pM=^Z`Ao#>JN(VByP zTX9^`twIB4Zo-`K$ybLV!VQ*2tiLh7rSnX@km*`!OvL3!I6L+On`LwwBaCEZ5*Uv} zZRof0CvhS`s4PG!fECiw1`orP;%E`mtWG(CCLx{HFrwmGX=oL%VkqZ9={}Q()^T=F zo+iZwkr37cvlNbm6Ui_YpJWG8ps@mQ^}3iRG5{vxJ{2vIZ$byTbloW@qobjLU^J8^ zOEiGoPUcd9Z0M+AW(5I8h6;ILqIa7TMEyb=t)q5WHF(K*4IG-CXp9$`Nw{>z_sbT^boIR!TLU=573-S3Ok$eosk%JPR`< z1%T1if=zroAf98i>{O=$xt|#YdYDT9LjpJ>0ZQd3fsPCc4RfwijYGrIJ&9KnVR==< z5OH++Vgq&h64r-d(nm2;$S87P+DXl9y|Nf^$OQYjVQq!4zm zc%&WqdKH?BdLhD5R71G1qgc&Ds3-}??oeHe8L^+hd%;_W%Az>9S4VKnK{_xL2Qj3e z|3woObwRK^P!Vw^ctx_iz*~U2o=O`8E{uX6D0HksHS}YS@?lyn4U9mVMMHoD1m_htw;f{){WetOw2Og%AFM+a(!Qy}eqz=ki_ytOh-1_sB>GBF$S@t1c2FLMat9e0dg}xD%*QY>s(u@H+;I6$@cBogVgN=4SkQbATbnNgzd$!Ki=q(-p;u=2vTWh%#PgUJc< zP>1{S;4=_|`|^chT8%;_IlD2QZHE(q9;)U~u`;a8JFTGPWWGS_4&itCYNfJ`%%@ zSzahSft$F-2wBFlBa?k7_b5;W(PVjoAE|_lPHJP6qw`C?I#>W}@Q^Y{E4NmL3JFe<4C=tvcFJ?@z(j=b>_k|) zOkN!r1+ie5NCk{jq~ekVdLk`H(+aAqse0+4LAyi^)LymjtU+B(a5r#13KwN4W90#K zK>~Tb71|-dHb!7^v($!D#fwM69$7F5c@&|ctxmtH-3EdrAdU&xOep zYm_~Jj1du}sKAPF0htpzzMA4xO_DZ}tUfFtCP91gm>zIb74PTAAQkFdrw=X^e;#=q{0~v=n`SS*)%GJg6M#Sjkl%fu`sq z%@9*7M_QByHd)wh5@E~QV$hS|HNp_M(9%=x8;?-vd?#7SYP|x;`IM~VGSdO+H!un? zW7qOJg0Q-gN}r&%*%rV=Fk~ti?s*&L!<39ub)aD-72}w7o~ja z2+;Rz#j|rBiP`#3eA`8ovGi8YqZpDcSS~n*{$XSt>Oc#6vpb;U&f_<>g zV}_@t;8{9b33rZPlv;aS>g^8aGLS`T=wz1}A|4aPCkI+@fwBaYQ?x)w)*MfbjZmqH zMyMqeHinxoc+-a179w>=LNMy=q+Owko-K;@V}^fHMRj^AWS5?D^5b9_$xN^0hnPc2 zdw6SuSc#zS36&mgDnhrR2@4})JLb210hZ^0B_eUzgJR?qBZ(RU%^YY040<1ycBm4O zx%3wMAd?N5*n%mOk5TP@F%i2buU0uxr%d*g3Yi>?WYDe3y`*P-1fwbnMFxltvvjDF zUC6Pgs8<2?LNmEWS%&0Egu6UiN$B5_<1$%}-1FA6FF=8;VDjX)X{bO7(5|dGZF%}dHp9iE~v6X|b>kOEP!{W#& zS07=)hB8l$jLpVuB`ytF5IAAcsX&cap+{6DH8SD}*aU?j@xZn}Sn5ZP;*5up2+2*;_X8^!dzB*kK_Es6c%TeCb|I6{HCCc%#BnM{ z4H!(x(ZQJOy;LQj&O>*vV5o^+ji)>E^ovK|Qm?!v`Yo1VF$)yNpoEWB;FYy?u}aHI z5q;KZye>6?Em-whi)R>_^jQ$oj~E8?R13k4B@O>*{(;{9N(r{KXi%8i=AycZ1s+7b zRsRJWAw!4tgUP9cA_pve=tNc>MD-!kb0>g#A!%Urfi0LCEa(%BT#Lw?al*Nh3qPt+pIN+6Ael%bBv17U|59^{fQ%WL8aLM?m>{T* zu!psJ`jWCiX5$r#5+`fL%%cbXW-2oIHdZ$PD;5R`2;@Vbm7peKL=FMxj_)&a;5HSq?6uQJptKWQLUQLoe%{Ylz% zl(&N$(lR$-Y*>uN7%ic%j>Q*fSF*xMRh;Yf^(xIz9#TAdNazJkju}HluWpOpOFaRL zfLIHQ11bkd(T-`evU00r0DYh$KUeiB+CdEA%3EG%${n~$3$~*QGJ`2pWC1d}8W&0k zI`zA+R)5wt} z3QJyP;i&_KVMrZjNWy*^QB7pMXC2i@E`5R@kCl3L+m%|!v1)@6paxtpboF$I<8p}x z(sD@=hI?9NX)M=e3}|T(5}`&z7BL?nhLw$!JOh!TQrbbmgYr8UdHMbYvi{jY#R4Y& z3ReHbQUg1%Mu0XDjTaLfL%31MHWOiT^M6jt-*N1*fGNrCVv;PfIbvvGBg&; zOCy|0cI@23d3!VQFdln*>O&a%@>ga^^8se+vl*CE25gIc*zI7xb==tjCZo%|FcvA) zM$6kFA1Y47k|hfxp$PMzt_sQ;nLp9}FDhZW9c3Ndf>>rNBoXR-8YoWIL_lJ9$1(|Ft%fIwarpK1jZhMqV1wn z)d)bPhGgSibVIYqatU1*MImn%xM)cWDK|_Et__1&kXR4ahEW0=86{xZiUJj-glbW_ zwUuZS%8(ZZJte=jm#!t;YWWciTg+*7`8&n|gDF6rF(?bDR9&4h*?3o%zp$xq8!0oD zx(N4-!nO*Np3JkOR!pbA^|P%l?<1U0gJPVRzSq@A3xDW2%U zFp9)xryVtJjUf>*9b&r#y;!z^7Yg!7oqr+J)x?}F)@Na%GUy9KSjWT`D6OPKNsHVj zDKV5(p!Vahs8jvAlnFvBE5@Qs8Cw64O<_BrU5(?*_qCRM3_%Ja2`a&@cBOXl{#Q|lo)ddfT4L^{k%SBLBBqFvy6YuP@y z>&#sXhM*Ij#YbV{Y#rH}G!it#G6}cd+Wbf^8(3Ay)(|#?pQy3X>=x@U#m*GlU8!=} z;99aUna}Bvg95}-DcSh~ zs|2?mX{>aY;8K;Nfnpg<06T9BHBhSu3mw(GSyI#(R2?7I7uZ)Smts8=(;Vugw!z4g zhgF}Oa5cR-`3gGNa)gl+a+(E1x|JQlg^}{eI98sjl%Yuqkt>Y9n1PAbQ%)MELKH$b ztLzF+t{%il!!VO$IA9A8ba&_{0W%LoXe7kne))^NZ4|2IFF4!C7_8+TxIE+G=+{+kyjyh zsIf0jKvvfwK{JjOKZoVRySTEaS_ZN*Sx5F0pP0Rv)1 zX<*$WPqo)5vmzfcXTsE9$2v@G`p3wX1%|5pC5Kly(`bm5OzwRE6NdGa3=G4Cm8Z+O zsmiSP>la2Qk%b;T>XH-;Wgmg`1}12Xhbt~D}0X6ky> z`D(6UFh3?e3)J{VXHLho)qw!Wn$9>H0t{@YC@YOdx#Xo(JjO7Iu*US7Ed*~Ryom>7 z!&nL_f(Gg>g4@xTPw3m^Z)^uP&kmJ)AQM6K$JR31a{!C@Fn~pK&()=CF$Z<@1HK(% z5;I3Edm4j{o}`ZjjFCxS*;e7${A3UDdSqFn)}%ZNvnagVDRIJuJ*y8?5kIQTnUB%p z#rr*w5;dj!`b$L^8zs_#3Y%;?3{i-QqUSp`tR#^~z#fooc@1EOpgB}c7hzO(k&Uhu z2x=fQ^f69pDpMPS;B8|v&%!UJU`C23-%l@OHMA0zVh_eJ5lYVvf;fN!5y5?Nn0|G0 zJ!Vb7f>|QQH4J-*Q3iF4)r9pNnx3ygsyt+NWV9C(7BeXH6|k=bd#JH$X#r$pzL=k5 zurZp_9pr9$dqmo0N2(nyVsb)C8XUm`#$f5e7GhxD=VU)EGKdZL$VS(ZMHH`BMo4o*cb{uHtrl+p4V+Vd~m8V z9bJ5N<7xb&Y|4y-)T2irpah&G8U&R`uohUv@hqq#=_siC9u?KlR5D4bQ(19@tDu@P ziXq?CcZBld#iAEi-yue@&ZkkZjk1-AC<>`SP#x)uVrqpt#|#AjFe@6^nJb#h_=P#& zo+}z}!(^8&5m~2Ww`GgkmMvskz9*Frb}FR8I241Snk|Bk&dL z#KZb1B=ktGNG5Y;k;)5GMGy5a9wopa49+9fis&I3?jE50fvJr>#i)pJ`Ey%MWG|k< zUKF`k#a)b2$_mLC>TJpIXo0Jjcmo(T)$uVRGU^L~hiJC0K1!PgLpg zDDDQ-bb?P4$2CT^G?m>o#>?WG0R;|?Rpyh5(*2g@%D5Rd^hH6qdP{O*mgIXJ$tRpk z_`Z5E4qbN;y9>l5p5N>3$GqVCc;!ve0SG7I8O+9FoQvRC5Z1wwovd<&s)Y441*Lij z%ZU?z129?`N5z5#W@(CQSTE4Q$fu&sV}=A*$P3o6uIaJDK4!)0>?|4vSrqC8rY*HF zwx$8bW2P-{hX7(3%5rTq@q1llW2OnMQaPRYlQjh+hGvWfT1+rJ+hWgjOJ|`MB!vBy z><@+qphQ~AKPE${MaOMjQFZVh*G{y!L@d`0A*4ZrR6~} z@E`-M!=_TG#N6)zMWlkAom>;fq$mN96YPoyg~X@p^5WI0RJ^@q2p61u`|Ehywuy3j zC;Ml{wzbO_YfaCJ_d`TOO*91v%`&dBpHVBO#t0ACn6^bn&$9;y5XP|X6{Qi>u)*tD zGiPODrHYId;W;JzHy#ELSr>P(iQ>+KK)pkNlx%9VD4c_a^iUgzeGbsJK?MZ~`O3z7 zXlcFN=?2NDFf_nrxWQqlC^+yDaqzxvmAo%X+U`GHM3AhxCefh_9mo?0pn$ZrQV(7v zJ9HEJOyuPr3792$_Qn{xIufV3XSVn zm2I<-I9N$Y?zDXQDxC6DzzR#&cxd~cosLlHx#$Z^RN_b_LuUG?SE?ccc+mQo&94j2HvaIvDRD=M&pE1AS%pC7})5WAzGpO3Iy@8xJ8j& zaoW)bWs{qR!vOSitF%P;R^il!M+|NXH_=gGQbd@AEbtLfZtO6E$HW%CvqsnwI+^Q3zOmva9Rgz~PRR-fkDtiWt({!Kf;kpj z#fm4Y{`D^0Tw*oJz~aX{4h}@FLuDT!9@}r0^Ok=3`;U6I2e+2 zI088mi9%)k6f{LvAyeU!j(}3$qalVzah12}yl69+L_xv;_X7ulKI{=VUM7&2oYteaYF$rkK>Zc@fG7hU?xlUTx!5xu28YXq{!g<^9$qbtqGtTPw~CVLbqNT6^@BvZV~W5n`AB8E5DMq$~< z>y*0FB9H4g)lnM_VZp#}L!g$5jBk`lz~24#h-Ot?IV zi#vS5ryKg^J-p(UL=C6*Npw_k@CjEEf%U5=I{ccK6Hy+kl}bg)Z7y*-z>$Z1cp{r- zcXXMjtqO%b1ZI+WM038_#1-c}P_IF7tvDdK#~YNDqsJDOVK?eyuA?{hdWm+>%Qwl3 z?!&8obeEh3%`~DR6&nC#9d@$z3e%wP`AWnl__Pdtc~!S9n}{fCKqKhefJ3r~{8v z>JzG{Sc&GQZEUB8A<2vgx@8$FHh`MkdSDy1MN7m*FqrCKbFV^jg5)D(6I<(Wb%QcJ zS(zw~V?zmLf9!z3G7e=juZ~>NAvRb(%bO(?msZKyWyYc7VRR4-OL`5*0#*DDg#&Um zSPii(z`x`2S4hF20FoZnn(2#%@KB+rI3%OpL}Od>;Mk{G&24D700cu911IwU_(o*n zQYGD1$EHD@z@|fOQWd9ixMx?+Auxc^iq6SKXa^%VrzpK-Slm`Fq@+iQLLbQt68l9{ zJU3CrrCk_uh3Y7FEL2BrFQ@FHfFxrFM#QA2Y>J&E)&p1d4IR=i3K4n%Xqp(%ww9q6rg&IR|=+7&z6(J&_z! zkQK_?xqQfu;C`M8R3+k-83NbCwkRikCb?9iTwra(41m)dOwV`8$$89P{A4~-TP$et zcm!vvb+|eOYWs+6^VtrSME3lMgKa(xZI`hf1Fi`&jM4(#jc$EF|b{@D(VBJ_=g zJ}hZrjZ$YfRGHgyxRxWc7`jZB86gZ{R~pdg!ZUjpLvDa)-pSa9bWsmvLT5@iez}TM zc%tNwU=2r24Pg!1QY=!h(Xp7!+wL(_{0nFjPa`xER_I=p@AIs(^s?TFRj8t^avuXZ zMqCw6HUdnhjy$^IG_j!3TNxkaNg|5T@>Vj!bQ3>6%AHKHEQ-roxSt7nuv}cnM#_YM zI^fE*fl!37i`k(JR{h?swI8fA-Ui$Ed2Njph3sR@f<9Q!(y-63_N;x7}QzIeKNRy ziZ}7Fg%G9HkBP#3V7Yx5J=j-*RJbK0^1)hjt`1df z&alI@a)?lB6#1}-C(kipcA8gqb6~X^7fi{m1kmFM4C)etxFSQO8eDZ_mNv1Mj(`{F z?S!XMR)TS>m!=9@14LKwh-i_?BWSybURM(fbyAdP2`!*UQZ1mHOF%1qF;?MW3ByKz z$hc-oq!-&odL<52*F0g*?8AN~LJC+N!98abM1@vh_}Ef`T1$vkhk(zm0^IBXuEWi$ z&`A|npNTWvraM)30MM!%t=o&~T)Ht~+lOeRr()_o zw3m!%C(tvDb7B(1-bCv$74?hD?5QzO=wOe@1$;;i7fK>LOle+aH}aZ#MfA{JJF+e- zlc4!9eMti(&U}cYO^B1D+Qd165+SZ$x+#@&Z!&~uI$p_J(G6ft;1FOcV4$)kp8J4I z;4Amm5TK%^CoZvJ6M_YIvb#vgYXR(Kxhnt!kyUYDd$f=l(=6nf*ll=NzT`SDL^L_M zn8yZNo?ygiP?yI=+(?3%6Bv@An6MT*xD!z-q*A-((GO_9XrCBkyt*Wy)tnSEui9pm z!|3a@O|G@1Fj2af(d*)9_cD5Y91Sn^P|6-0_1J+CGZCOrc%&8}S!<^7;tYgj1n+tX zb2wX!D>RVc(zQ^J6fI(|hCv@%AdhrVauWkTL~HN*M4WHFOhi%U5($8}>WPkx?W)z|D^9F;lkdIT}UVShB< z6*C5yo!$_lr@Z9>6&E2f?DOvG%%u~&7~QxZiqnJUqB^+DcNny=irB53dO;5_f`Y_e zAEWsI$$WpR=xDsb4(gCqtOJ`XurI*w;27vBa2^wr4X(nF01pkg-`-QT)eJk-VEK;e zc7TLlO{0S{3fkIz6pj?qIEK54NOYTF<7rq9-9`!gaY9eBl`ywJ_#040W!8_15WdLD zT0^YPv58s~O)`>lFIKmNX9}U*kQ$vH1I;kOg4f_L2V$|e2b^LYOig$WBeiE z38OwRD6216>BUT2!is{OmV_(a=;|`*FaD^gF^ms8=A?!ez<`pfs8|{!yMr}?NK;Kr zhf%D~gR^r#j%kQy#nDp?8c?gL&7Lp;b4!`zFC{@217Ym?n*m&&BDM=wm8Lxu%e6te zIQSz6;&@OnjLC$cmkM*M2DVb_6d`5>U1uFu;~LYNkJUuq=&m`;W8Ck+1VSk_oC5Z| zHJOie!1db2kh+qEo>6nFbU7}SWu-cXrA{5PdW(?M7<|ZzQfCUi%x0B zr^eQXF$mF+U2(%WTcZY45WZ5Ppd#(;52XMj=&87UMJIO@#nT=g(BIG)%SuNi6QO7h zyK)Tz)+p31I#toPaJ~d<$YE@K7&`?$Ai6JZaOLTv9sC0objRpUAYbm(TL-%Fs0O!; zsy?ckqNt`gYJonZf@{veO7 z1wcO*b$I(FRwcxv#x%;Z%A$A_NG~p+01NJF_srQ-K`81M^Rpi8@WTj*ivd3el5o2S zVs)z`&$#j~1X}$q9~_4qXFql*4`Qi0<0~hoa0k{e+y-_!JvpJ%A?{7FXjNf3fuaMs z*3S{gB}C%6W>=MAvn^JsxkayIvACu6lAx)O}BPTLr=$ObwFL-ARVpo zLa5}0&(Q%NTsELbCFG<{v%Yq-sp5dZ$Sl_bRN#kxE$FmHv?x57J#l_zpd?2Z*9F$7v!$*DT@) z^lthM*)%JljrX|X@ zrK!1{iJ~ls(la(Fnq~)VM&&xDmjsx77E!j{qtG*3veM00)u~Yk$LPSOJ!*Zr+799| ztgyF0FIXscf-*h5B2snqjgK%3@V#-!%W-N1`2zM7>kCo_{G^$Y+CsXIm8GH9O^FB}E0k?8*VNUtzX849v78RL<26isB@rz~t%Cm=FhorAP&5%TJFn*Nk~Q_`5A^E z%&?Co?Tu+1G%+xT9*EquKn_3i#$@pk?5Q)$h!L`oAVE2U^TCBN~HDPrD*T*0Prx#euy(UnA{p<^*Pbds|D#dzQCK7%%81q*- zKO@W%8YixQFpTw=3$*TH08wfY-dX`yklTgw&xT!ZJ0?Sjk`-+9!*)yj>V!w~cMeos zp%2rgZo*5PL}K0o%m^>Ac@@UfQ9=XU7-7fnwC{9p6xfk@V2Wlv#m?q7PqB!504J$M z?`NdQ6%wl1`nVo2a;0BnRh zbQWMG5&{jCH(7~QC7G|9K11nBL$EN4aHKggQA$Kgz9zy^6yd^CMBl_v$`^zV$(SCG zNwhhS`(9H-z!q;&sc^~~Y{QOd$*_roN<2=~_8icMYQ^DGIv7E#t)zHVn7w_#2^yS+mLo>PWD|$xPpihlbqRRiRAy-56so1;hsKdCE`HJ zl*H$|;Y@2|HJPZ;+1x^raDkVvC4;5H*QNEj;q! z?5M@zEUEd#1A7%AAlrr*K~35W1||HI$B1riv)?}wZU9jO`U1pZQVDuaI+7GZBw?un zogCB@?NRs3p?|>{0;7!*cvvRJ7^aZ{#v z4Jxdag3|eTuG%N<@47P#+e!RdJ72((e!&HpU|`PNm3#_)XTIEjXLmk{VFH_avAzSe zsiXo!2rmFg#ZiqH3koT#Ad5^aQ8z^Iz(gieHKj?9w5_6%2?y^)48&)xSg})k2Ii4V zPd^RTy&B*w$MiTI=`#)2us*>B8#PwJykNz#)BYaEUK5#_JJ=-vB)0VR=ELNSdor0@KxjiDj_xG?J`$)ckmCCF05^Bvz-y3 zk({=Gkbs>>v31fkKIK4CD_(xAKa0V=Kynn>=bktjp~!1=EO%UdA!4uWhuIcz?_D3b zsoboOQ$H}vab;s2w>Vl6+H4A1E~3lGLMwSi5q>5C^F>LAmkO0TOq|3TBDO%FG${1a zQVBw4!=-IrO_B=-SG~D7KKF zfY@bq8B;}%F-Bb9j3U>NOIJE!q(*4qRCxPFidfr&+!s2ob@^ z4JyV7JlH!}2l2=jNOied$`~3(p`4k~dxxy8(UD8m29LSKfZGU)y`l=Qq39qt=yQ5q zL$eEvYdprHVeCf2k5YikZ_}`CY4I0u!vzNKXoa^y^x;n(e?$!;uTc*aJN4M*2@=4x zf~_+uNW*@nzb%A~^{69OnJ^}0`9oG@ag{rclksu~9N88Tk0uc{sS-kcylAV63tI*T zWwD-v6xv$!0OD}uM;Ai{ko6~$o4?D@SHdP!nEB-%&3HntcAjWa4lAY+?{EB$Ei&N4Qd zh>;RM<}h3Uqkzd1HKAW`HX(N=eQYwe&16ygcriPuiQ=OTH{4W`&-lRbYXf#8)Yfxf7~bQVGAuLm?o1$$ zYJPk|#kgnKu{P{j7sl6yaooKFy%J$@G=zAD@NB%dSCQVMn}&S4z!hN-rNF{>Vvhnl zi5Jy|9=Cz^@kU8mq!(!9PdSy`8;}Njm%F7X&!7wpdNHAicF17@fs8k=bMkvOt&2F? zhD{}ewp?;Wk7>i$`GU!U0F_CWSC*+q?MvX|Y%6Te=vZ9ThOp{?bqQBMdd73grrU!_ zRCoH&#LY{TR4gqk1QJn%C2%7S)&^8x5zR!vLhVJ3xZ6~Wg{1-8Fd|iyb>swym4Ddp zXrX|>QyA(g_Lb#D<|`gDHa&U6^q2tsrfh^FqY1B~k;8B_ulWh7K5*tJP~J)T+aGCF zs=c;OsgNeEA%4;h8GH3-acpU=teb~r%G5h4trNKT9I9ZGunHyouxw&G0hgR$Z^f?; z@o1P(hb&;YPMH&^7Pd=)kLRo063Xj8l3~%@Q7x>{F%L0z$*!y@0PR<8jwZNAh9sBp zb=gAI8_4(CP4==$!|54Vx*o!rYUfFF1V^VW#&F7!tEo(s6qyN?H?dUm&}$$d)I#q6 zf1I5Mm|jKo|L1-8-A(8b6ATC%5HN&d3P?auve`7rCQEixXfE5byJQ<%(m=(I1&AFx z_J9pV5Ic$mJ9bb}YzS8D*#DpJIWxEHZg_srfA=}>ojFtQ+?g|H&YU@eK#aLaeJyP- zCT|KhA$8eWbrF?IUd-f=Swu7&iS1?+kv8Wm@u}0|Lk-Wmb1EXKpqOoG1~eKK1rH(^W-016s->vFq)@{3VU7f%)wv4+oM@Vvu`t#rp>i3x zcL%lr7(mu|M&AmPf_*?S&ZcPUWR7DanQCCQ77M%w%%{DcPFrUD%7OBmx-iI{DWi%4bIi<-{+F~;wOOpbk8ifhT zpkS`RVawVmOn~OIE8x{J@nY(!?qM}2sk?8mT7{6s89ieUN!RDuLEjGUjH9!6ryG*; zH6T}8Cf*RFd?Xt%g2$=NCg-G^`JUi4*%dLU^tf?^O?HED=C$aVZ$&bIAOfSBiHmI& zZ(^<$i0LG*bBtWV*ivS_AJjq9pA8SDvkg56ygB+g)W4}`U}t5I8)Hfpx_5ZE+eJJ2a2sd> z$>$^+ag#%SO54|3HuI{puzcQTKTfH3)BnR(?eIlT$V^XxvCX$=9NT-kqN#PZLj24i zM#oN3PqCWzjQDoz5s-NW=rKa2JXdqE!^QrsIblMvC!%0Cs_o$gw_QYK+7r4_X4J3U zwpe3`?ou{mo#MMATBaXS;LyT}Jvw($Uw>trbkzb;J=C1ahXBqq^U>5!ZlQ_msC#)= zTZo%#Eq&3hj@Hrww;e%`Zp!kjEO=#g^!;l&w|c~7#u^fT5CQze8nO@7^w%sE0)FGn^v_qUck9|!!_~cx*Hr5##=?-i|nz9BhYf29yH|{`! ziCvwz$GN6S2X@q7m=9D-W)V0l!TO>K2yC^*~-~#Pcuqd((S+}C?dtd`7 zp6D|Uz&Y|Xr%P#~M=Kg1d2|Gh_E-!dO$=(HCp!V5OZ_qym)DJY@t}rSZ`tey?O~D= z+hLLsmqaMPbnXm=EjT=hS4~hTyV)EigA-%Mx5>v3tf3UGJy^t=b~es#PPMH^*2g31UdgFYZw?1|>3939h(dVofVho2ms|CoG|1kjKURHy37eo1r`W0N+AURkIz07=q<#ygCV1AZjUMBvJcz zXp8d=wrV2^*-yFxR@{^wY=HjL141{WR-f0lx7{JF1ao`wB-yL$*-a|w`r$q z*yyB$Gg6YHa+lak{1q^Lr3Pi;{b7qQH60VPQ!M{R*B-i79P44@N;_m_mN<>M>Z6tr_ z(3;xh%zk}{zlzI2CbwcS%nw}^G4>XzbGYpFb-T=37^V|dw#_5GVFV{ zg&o7AX5CJ`LY+cF31`yH@ILdD--Bu45`aZ!l|JrT0hY5~N13MMDU1BdwTCEyZ68##!f` zd~(>9sI!#&t>LvBx=TD2TsXxR!_x}SgOe{~TiekJ>_uN8@wnU)B1=~3UUXTi%H4L5 zUIQ{))OSt{?}Fu)2ut&X({Z|rUJr>e91SZSCPwSgv^t+sEH+&i-4ZIT-fln64atkd z$;~uc8tAzY7Qmp1ab=)UA*Lv8&15bnMd{8E=qBu3MatbMvHCiS7DBE@M=%-%1-3;A zjI)f#nx>cBVgmJa)vx$f@pHh9Wk@IOjO-+NJXk$MDa3!f6JFCV$^|XA8JXuTL`*nV z!sKgeKWGwmY(u6%pJDRy1)C&M;< zl{ScnYvWiYFhgYtqMs?O7p;DOc$YNyVn;15qFgP(1B~0Xm}UgU2rw1&RX}{k_(oMU zQh0^rh8@e@bc(nkZxT)K@zM`gr;-fTITLppmkrsK6)zc>VFbV=IZMZ-O>pj!L$J!Z z+u3+Gy<@GMuCIxWU{{7@idmW%q=QKp)?-BHAdv-RG#LU1<1C_lnks0o?jPH1ic6;z znIVS;g_pU&#PGIR?i(cVZrx`>UW{&BG?3(O?7)5`@5tZ%qS z&0{Vk!;y;QUfM$jOe0l|{FM3Fx{6*OeDdeff@g3(-dJaM(m|s`dgnU)P3}w}ekjnKu*uAYHbrLAKR;m&)ldq)q%~gj zX?|t_hW3;zdZvY!&GYmPXHIB~oF_q8pC(%P7s(kkOZfe=zKSyOTwMNWf6!)c3QkNM zA}^^Q#ucaLK!o2m!$&)*j8I#bOCrL&levW-mK_@sr|kA)Q(0%;W5y5@&ZQj$W%= zmSc+f2vaW0F_Ytz%TkVz!g-n^aT1)z3bpj~&IwVF_Zpp`Cr+_6TQS%|T>$3#eDBslM~vF^5JU#JX0${r6){>$pQOxNw_->T8_x`Ebm>On z&1hRIKyH#Jw~j|kI3U^GAa;$+q5|qu$#3yeYle(mQ1cx;_FZa)p<6NkC;OV`$CkwB8%jR{AdIup-l% zIQ4Iv61Q@d8y(uG$7pgA2A*jRr2+lgqF5(sJ6OuIohS#ZOm0|VV@y1vA$(Eet!EX< zH#uXM-8qI-ZkK*!f@`u&`F=AhlZW#L4;lUiAl^h0%{u*2u$NQ?S+?+IE86nJAo?Jx0JQ_;jL*d zi{vghd1~%t>A@qT;VSYdYNQ&SVRtSn8=djihc%wbj%!BCvLpRnQB{Gjj!4>m(Q9DX z!)v&2LSMy|>$aM~$iWfK!(7E_;c|1MtY`vetW>sqtUBZ2SNW{+s?urJj?&%l-^z6u zu8Ge^eHoE7DKdPtJd{VY6@f9nS)2?tCAE_|(=lSlu6Vka^Gv2q#VE-G*prko5Yvcb zvp~BNvhzSYTB|Ni8)V!QuInp}r18Pk&;7`ZHG;3U-OS~nZ@>ChYgFG()s=~0#K+ze zw$x>5&@H~4@4-P9#w9j6;w5XkB|R=`rFVtL&tB$EWBOqG&14d+oyqiLk)Y{b^02MJ z_|82d5c-bU-fMmZeNu5&Bfuy#T+*tV5n*|-22{L3nvsXlJvOj$ruz;~ zx&+1aMwIuNycn8N@ACp{ETtmi_syf!E~28c^2_+bH8vD?!8sDlyrx;)HEz>y664cmw)sWZkzxdgU)UE#^yj65w9|buc4TX(QAV$p-I*tz_BKDu zH<^R<^%ZlNPMSd549U$o6ZSDD;Z2Mny$PnzzFgU-?Dbi~wBC_6SU>PR6x$~>wnl1w@KToFM+yNSn6_y;QCEgPtvugRH=)qQ|i_x7mXOz zQdiaOZZXtJSwwp0Dx=hKHHET?Z7qcko$v0(T*Zs&;7DGMTXyvYI^QTM6S)C48(?um zyxkZpx1Ld0!+B-IU`iB1WsMX@r{8vF6-}XzlAtH$8>Nv^|If*g+8=!lEJ90S^efV#Q&Gw$a}yI{!J$)Oqf0nOgU{>cvdJa&3U3xgP%h$kqY6me zh~4f4cLC;)Al@>$5Ona|gi)StDFs1F< z8u73bz&<>EQuNc}5GYRWv8$6b?6C~0DN}M{c(kr@doB486m3Ia-s47rfO78Vv=S4h zUC5cN4EltVeA_XP<^t#pvp8W;V3&zt`eha=LuFqM9ew2W7P30vt%8fT z?HT1@g%(=OJ>Gcs>mM`C=WqwZIw8k^WYjtBw?>Fv+vqg2k~fiFULI>99L~kstYiAr zj5u*g*vfX+%9PD4a^`|8Ft*~k{d_`l)gkXUIo7_5^*d8w&HI~_y5PXQcPf@?xE8#y zaqc5#o{KNIJs>YdITa#_i$(~?o-+b1=6EE`rA9K*r7*E2iw)?#ZBhbT_?5DN|DwUm z*>1I-t@8j}a&BiIwC+V;-{(E8M134N!=D+&vxfKY>%#yNhfbO8+lo(P;X6%V@-b(9 z-uevvEH27uFy#$t^z&fI-?sT$%-A;j*xt{42PW z#;c|lF|l^qK8$;;{>W!i-xQ!R@_FmE5VC5$Er4FJ()a_3t@nveQXT=Jfqh|B;w&@ z_^cE;O&j8~>@;l$*R3*hvHruQG9(@C?I+i*Tg5?X^IN9}LCF{v*exyx zU>`wxa*OD5@bbb(Hw zJrZC18A7~4!E0+7d$9sCRdfogl2Rp4X_=V*HV#*crF$Bh(_;=S*7j>dx*h0xDJl?aeOmN8eJ^4%a{@)Avw^<96`LxO$LKSoZ$5qcm`k<{4Ttez zGpz4B8wHOqLW>BU3^U1}Bl9=s&R$NBX0}yKfq>zqg#*{LOBn*C4~RV!QR`&{I(#r~ zwE5CIqnLK<`l(32wxaqgWgTW2M6{E2j^Q?Q<7>Wd*OFV-{}GkA)!zkD$C7!sc$sn@ zI*Q#e*E0sEWA2O~_iHnYEgCxjm!B3T5M?uoTuxUhB7txTa2QY)y;SDnCLR&iB>XPF z)zWx$OcEfv7l#|YeKNmhY_zv>LO-3Pv7r9q&1&mQ-c*XSI%F<#K*Fe~Ci6UY$4PFh z)SCDVvDRuJHuI$th2%uSgM7Nw6l^L};*HFq&tVSC5+KiFJ;Zuz%vlo_RPt$IB~w+h z?-Wt(y)Y^|50U&f8^&&saHK`07t{Iv8APxVaZGe_g10xbpE`<-W2nUfX-R16LZO94 zKD;^o$R~d`dKX|Gg?lj(C@#R<(gN%)c{1cI9PLXptTnk^6A%sV8sDI%(iupzahL51 z>b)|L<-!kg5V1lUKJ1()Cnj=r=8SMNwdd#2qxn}A$)OyWHZIMQ?A!};PSo(cF`v!j zpkXoME?<=*o3%LR^ZwT_wR(N*&RtpcBF0$C=r)^whL9x{AJI&!P%jZM$r+_Q7VE@k zVFK6*%M*`{=>>hsWe&)buN$nvrPi2u5t@1la4O?xLJFZ1=)J+4-1B_6FA$DCTTV&*BZRi74wtQYZJCfVxe?& zDjoX7s^bFtSa!;SF*u?c$O^PRvRQ+cx*Y;GuZY#jUf2{pQNV9wme8pZp4h{ob#46Q#LvDv4@<#qda4-Q++uoJER9(%efC>!|5e9$Ja7DK=x zNsl5~!&om^d>S_^N(D8Z12kv+nC6DOA4S_o5JvCS4G+$xjA zpmx5<#|Y6xMWXFwwnf-35mn_nZ8wS~MhR_4>084i$t}(tQp4|J`=vTPml>h-=_uvq zTXFOb)n&L-D!G=WeLN90XR`Em=8JFY$4`=ZL`_1c>dTlixTHNk#n~2Ms+F-q3owzR zTJSb9mXqyPO524B8vSQwgE?Admn|xe+uAs+9J`ws zt)V+@Hg~QYUaq_7TFnC48lszPms~sbV=na8%}5oyjAbAv#XeT1{u) zznLa7sDIq@$vaoJt}CEitikSrE_c=Rcj0M^^abU#-6lh_ZLn3_PKGV9ei~e2V|1U2 zjWl_f!qg4y09-VjrR!E~6O0LtzS!=)RqID>=;>s-(MR*Em3^GRLHIf~dZ|KrIJ?i~ z0@!u*B{HPg4v}^`DY6Y;KNO*sGi6FwZH<=nj1kBs;Qdg>uwZu&I&AIk)-g)gLbo7> zftgJzpnTmOqxbQxIin6qu;den+uQkIx(qr#u$1Heb~cz`qS?!mF=WY7)o>`f^&*2i&zBG>6M`<57duT8w%=gV-53aP64627+D83<-2WOPax@Xn^@3{SN!X zWIJ$BlC7hzfKfIxNP+4F6FC{v7t~8Fnk#_mSA$>soU+mZ36v6*m2^8OfT9_?MZy5Gcd-!vK8*ERI)ML+Xn^kjXrn_ z9rbgDwN2gJotxm)J5X?B8Ej11cpn3DNLy6mlf`zELryufU|H$bOr2;cLrJCvHGtG? zE<}hcCnwM7p>p1mTPHZKuO4nB5xVcg&lm%117{t~=~Yy}2HF8xMd68U zXf1rOpN~yPq+%SXp`(pDF}s-Seu;58c90HWMbV>M(J3E8Fzrk&E{C-0xu!?^j`F~lu(~6_gOX`)NaJe5BiF6a#2()LLU~EBr%vazNPrA zO;1i~aJYAy#J4)IVfLUh2ZEqH$i}{Hky&A8G{Q|s$SyjfVc)FJHeZZlpvXCa$w>V& zwlo*JqBsVyL&qokmBRq+ZxK<6x5}_0|FBXxlMl+hwzr zT(Je>8X~Um%Sa~*kP`-)PoQat^6b@-9Ib{F4EJiwF*3G$hlNWh7!=T|*#eCg*9eVO zLT1+)r)HMtT(-e;1yv_G(q=SK;9ZJTFfxc-N3wUh*(lBbt4(f&$iPRtoX#~ zi9jRVK~)tkYFE6rnL$6$i!2NIZNM>qnEq3ogLUK!?nKALhgtI%K9jwi4;4Bs@kTIE zt_I{}fA0jl(x#IlI^wcTxnOe=g|fn&6d6_G$YR&F;jLj;e40|c%hk2~=+v6pZH&VF zl@YQuKn3E4UbfwVrpf}nU&dft`-ofOL&}5xK+FfUPGMb% z6Sk~ppevYYNL(-GTHq?tZnIyht=k-CZN=BP6oUZ#TGzOAcT_zfYMky&6Tz!!w4JUh zP7MAYrccK>qRxZ0gks3P7Zn`wu>%7OGegZ~^oDbD@Uk2@86GgHdWY%Q5nVow4Lw+5b} z=meN>ELyauU>U|}npNcY{w|jev7{%ng+hNMByw81hV7V$!Vo>bi|u!b{hq4dLG&E-R8s7A+BJ06zV>Q`x6YA; z*EQ#2?dLlWid1K(v^6B&pLDPYFqdKlQU;|oG^&v!|Q7Y#AOEj!y*9w~8UVJh){Pd&o zC)&4dQP{RPY+FKR+7g6q1e3cLqv4096F7hB@C2Pyi0H^LOA_YP=-}!WkHhUMI)g!J z5O+qM+I*DN)0id`uyJ9dmoNA*M&KzVo6Ve#jl1)jnWGaKomA;%@kT93K~bN;mt6L> zOsK$x18!9yRz=&deQ86=iCkt`%d?>P{y`3s+OiyV4z0_5pI@_TCse2SG900bBMioa z#x8iMt|=jA+@Y^YWS`j#O)&8>2~U_L+glXMEbi3ryw+r$dNks$4%FhMs?BoL zJT3u`9=|XtDiozjSBjf%#mMAy(0VvN;h4w@_}s@RjC3i-x%!(I9o5sVFVO{EWktD3 zf&?wp>@gq%lA0>@uADWVm1A5fjHmQzw!@NsOLn?IQM=zb?Sg4sITdCzW7zVnY)i#ZJi;{pC1X zkYcVCekac;$Ccx%uk`llVm5zi>pUP*EZ@~pu$C@*B57k6#N9ji3@o#HxqN*zm`7W0 zxvP_tDXbc?=BS8jFLy~qIqXiiP?eJON|D?mvtG>{eq(8j0|B=PTIsoqGhYMZ#B#h< zu;4A!f%@s}tz|)1afdIf-3nXtVs=~ z_%aM+YDKV5MZd`j*aYZDXee$dbwJ~PCAeB2X_i< zwY2T7%tr@`+5;KNiPa>pZ6;?pW@Wc2oMp;)kSsU(U3!&yqv8=WT-1;;`oL5Ivtnad zJs!UC%Rji4?{HJ*bc{B1}25kOC1@ItXF!rCmM+n@Vq< zUG#R?eBd6blV>qmY;g+H<{k_6yFs$xtW?DlR?k9h&6|3Qc@R=@OJR!H?IvLO!t794 z0W760_Z(x=h8$^&?^P~ngP1dApQG)?6PoG5)qrg9 zt1}JROIT5nvCAmAiZ9?|9ev6j!Uk>oM2c#@?a62R)Vg-m-onD@3zK3Clcg{zDG1}# zRDBmW!(;YX9?>fe=|ZTs91GrzG8ba|{*PBVqE+yue1)2nw^gs4zr{jP&pKKmGt=8P z9ZN3xs4Gje#CA&ArP9l3e1eKKinCRo))g^WPHPE=Y%L1=6g^>V zle~;Td$JJ0mTW~t&I_bG@&e-^w>4{=i;|8{XEf@KZn6<3sEr?%7{b0JRg-8*f?nHK zv8^uzBr;(f7Z0`zVYJUTBy37?r|}5xv`^?xU3bU^k~S~8a;JSg$J#WdE)%y|EJroj zlO=)B?g0G{Rc2lU38}KkaZ%`A!bK(d5>8V;iiPFGHsx9Ad4|4kQH~c^?Pa8^G-cjNVHrJ1yX=V(NPbk_q?x3N%_-N0x|w>MeQRv|JH27`Z7tGnB+>1tP5o~9h9*7A|Up?9HH(+*UJ&wy~0?# zE*x>b6YXBvagV0(r0tg1##ZLs;5HeR+RwGrs31~`b~9WS*AK)M<{OA!6# zNJ^Z=u$)8(Gfa0+=R}@^`O4{8b92;ZJPJOjY(kWyDUxi~Wf><#7R^sJ*{%hL_qiio zQHw|?6T+%JwUg3mHA}u!?`$NGPxOazt!TnHhY!Oz69ETfb?jJ@M1JWpVVn;XxNG<_ z>9FA9MZ#hII4AFbjk>8^q!FxI~;m|<#DASmcSE?OKo|J@VJ==FT{3CsF1z1B1leR zy1>~Mw@FUm+Y7ti!y_>X+nPmu#M%Pfsg(&x^(l$w6)?By7P*3m?HbzG(BWm?T2=*i zbPQlgd6H9vicjxL#Hc%bsq8UF_c#R4HDF-4&9<#6Ss{SXFpGoOHa0UYCDTH;HsG~U z#e)`zkEO=zZ9FluF3Q1-@Z9Fw-9 z)-00V-h)Pm>Oh|_=8Rt__iXT4fGKggg&U-xU7Y44So5fb(%sE6S+SxJP}`=^$cei{ zXi#dE1y`f8deGQ}l(#R5x+L0GGkK4xV4o^BL)Dc+m|YzvS$5_@F9gjfR~B1L=Xbwv zzGjg$G&t)M(~s=pzCX;8M{lM^Y?=2jTABy>zL5*|lH#%E7n+WfFHuC*W1IC2xQj1W}GY_ z**pV?v6U@q;m73U!a|=8f#@S-qdXl4gneU3^l5L?&E13Onc%~Av$3WPGx2-OdODzt zD?7ZU54mWGD5oEKGkv|>i+)8vN;q2AL_w@RXYNG*#^iT)qTbId=zc@>S2~)R4zqmO zKpWmh2Y?)7yNFJxY_@YugFXibEoYpRtY>Jn#1Ndt5Zb`B8sc8A#A9%uaeIt+vAsVN zECrT@Rt(RmdF`5lreldD(XLytmgElZUbIHo9+262Uy5MwNc(C=){Qu4hhjx0rIcY$ zKAE!u+;|1rn$yISCW5qjc^(CSV$EIkDbiJ!aF4~UEI+6W>o~?mY{S$gYD-D1i@~1u z4n_i<%#CbhotZ8m#h-}IR72=G*h(`&G<*H1Rv2QKYm2n)WDI1EroVLS*pWxF(LdbF z`i09vxIs{A)9l^ZbzDw|eFL#~z*gJFi-_TMiLRyAw`67$jl}eSChgTew@W4+b9Ht^Ngk_ESyksFNYy3Ef5p>S|BZ(LHOU$Av;aCj?@_P_ z3F&7rPeyvPPr_PU#P!vMw!y6b^SP* zK|wU*#EQbY#wooVIh~HMYl_ktm3(5HHdrm89xLUFX(MP1N7apK!>yC$A+U*@!GXSU z%qJ2K@oi|%xS}4@I5xhGFj&vD^19%&@M=XG*>9MQv@(^XB)qtj7 z6Rn6`+jNN|bEhG}nFN^7k*y|2$7sE-KjE|kyn$3}AQ<-OAM&m8j-B?5`X z(b?Cxr^OJ$&N2;j!|!qi(Vkp9sVzY>D-)e65iM?Xzazm_4`q|LpR3Nbs#1m3*kvEH zdkmuqIewjD%9n>J9a0UOB4FzaS)qXMS;yp=UlfXlQ&c&{H|i=8LttC1f25P6FoxWY z5@ziEZsrXozPuviNC0H^TJ6Z>n0fssmE4QCN-AAOfMCT`V|!iZZCg`PeeUknuawXI zl1+Tp#$>PRa~qBQT9>p!?I^9h#`UI)dNV|F$W#J%aiE8+c>2!cJ4Cdt%a7Xcfa0dQ z40t()3-;^pXvJxWEEOXx)tNZK%B5Xh8o-FS2AVQMFoIKUoS}PIJC2qjPD$yRYV0|g z$y{)M>B>0|ah5`YDBm-2Y_wo!gH|a--`Ne1gE;3!qM~$UNGz|Y^KDi--0cD+sVKz_ zT-mLYv^nYHId|hslbQspwZ`u_Ca+rwUC6E&Sq2ikhR#*~XC|yQvr%+@9-Q8>?r1$+zU_Y;L4I zd6|GjdSNx#cGL|6V*OMj`$p-!giqO)Pc*e-ZjtD0^=HbCnGT(ca^qYtZXyebI(nXE zDJ1d-hO>hgsrX`mB$=8(5a6TQBYsJPL>f0qE}jJxzKHe}eJ|49%ja*i2k1^=BRiNe z3^^{9l=SCEI8}loM=U>KZHr?v(#kM~2FBj)9F&?S#gBsRIFq9f?6NY}9dPP?me~m( zUhcL@dX@gUSbos?MLcXc#m*{sGO3#_Nm5TP1-kH6R=CZC4#PQisCeWx!y{v7qtZ+W zq2BH|rsZk*Y^jjyZ^|h=lIka7bRUTaIKcZ%vwzbpOk!6dXVXTr_ zip&?Y%Kdg{CRfPOMdRSk!s3Yn?`|2E;YWAy6dtsI?r*{kKayb=ySh`nt1L9yO(Rh) zHM5%Pr^=EkZOoP{o_(ndrz?vl<96q`yt`W`%DvHwrKqDDPSZq!B(;y5!xLv`gFo8t z&F+GU%(6<29g244JL?*ktSVADG2LfWX9$HLvzy{hCV0upoFy4^E>uHbmr291z@(_l z+d@PhFHXD*o4%PzMCh3vtbu4CRbs*&Vf7~CAFxb)A?^?PiBa;__N>kYK~|E~S=O>J z>CBbo%$_}H`SVGNe$|NAQ%%PLC%|fksJSZV*Pc%6Dfo{C8aQz zy|4~{BAQU8y#W?jwHf`0(%?Ppa~Lty$kCaUsIZEVYz%F7dWuex(i*r%Q1itEi56FG zphh$^uCp>BFa@xW#sy)?h)r(bOt&ee`{i`Tl%=dr|JjrwC34oa!x$!sO>ZZFq^1zZ zH$57gfu& zx4;U^b2QRt!W*BoSG@v?nmFT_?~qGYqnuGAY!LwI;9VQBZ@*?9SK`J*;)u zfg!yc!UY}lQTBW#5qUq=ltfAy2C1^{CF~mKoDm|^Fg!+5iJ%gBU~#}wi32DL%$SVd z#Oi98>QwHM-7#|^P>~|64MbV(z=2`}T{;qkZG+eZ>*n4Ov@HhwE_AsxtRd^#9wL>m9j;A#v(HJTRRxI?OyXG8lZT0W#@l0ew<2BRGC}mwG3o zliuLE{TOD56_)xl@@!sQqI~oZdeWpu^>F+B@ZQ9>WyFAFJ{#x#G##}s6zdyJqlEX_B8395PkiyX6EY{ z)}^DpyP`yng`(P$SJ4?6bDCC^f8E?4C%_y*A2vyj8223OjJhbJ%~mo{ZU!D?gx|0( zVz%`amE@$dIS~Q*&e5VXn6BxES}!}dZJP>q+D}suC)RabuOL~mRgcY#R_|i943U-@ zs;6yEiGeVgtmilEmk`q}(vG0Stz~b!dN~oS7*TrWAairV7i*;Mw$h5&tbfK>+0>L$ zai_K{8)6B_vylkRZ~0~}zyy>T@QY8z5a;CSBO!#g^IKNDy)Ni{JUcD+3zI@yVntXF zs52~)Hht{Z4>+0?R(7q1#-Ds@yOi#uW4rD~!?P4*6W^_g{mQkP7LzBeX=-tPaM#8) z3Y~&jLwS=Ut+Z2>ld-L=d8yZAG*@8eC@VrWXrx~j@Hp4Qt}vhXto76Csaoq{i2dZj zKBG)c3}z~F1J^o1VhL!E&h`y5lvqH7Sa?{bWAe@H@Wu+;0s74tRojsCGE>x)SJ*hz z&lg_@56BItBVi?-+!D7it@d=w8(Lhxv_@jou&QNao3C<0XEXEu@K6h5U=!QeMZcm6 z*N80;oQ-yPsdE{k)=mApTNB69*@Cj;a%Kq*Nn{mjb-rdJA2u|xIdUE=^w8Wf&T*|m z^w6jU%z8U#TMEd!h{X=8R?A#m=bCd{WLf|RwsJVevCIa{DpVLpLYp>?H?!VZuElZ+ z3!P0cJgv49O@R0y-Zivp*6`GMjdPrbuhp%K?2TZT34ps#FB+jT3K+d)Gi z+E@9-%hbTUD>00z5)0_%&Fw4Tjt8iheIkpOFrI3;Eb)xFyTx|xg1(j`)@r|iV?A6O z>4XPUT&8H_;@nJDqjg_ z1H-xn!%=En>ifuLk;!^m^_a0%+d0VMTS#c;Q-?K!8gtf_8xhrp7V+NR&Bj}pPuX#^ zz}2mr2+RG+Q%K<7bh%FD`arYqXMWLzwc64Rnb%#*FdRbfOv zn7yyz(akb4%BbL*jQ8pL=LKY=K9U)QIvH55+Xyi_#gNUBHsWKW=p*s{VhcuJSj`M# zmW@ctAf`}JoMyYexLfq1SaufBdVssxNtyA#%ONojUQW)9T5Xq_!lXaX5&_pw8sb&0z+}oD77a^A(6+51T2r{ z1A7^ok*qi;#+D`FDqKn)X$6WzXjww4*RDY#gz6E;0`uVZW|r~I>}QRICK(Ft?=tG# zY8O5AlyJ-KGMH>o7vou_?WL>{HNlsXCho%G70a<%qL8KEZrp0Nhax=;jWQt#ZDbl4 z9Zyf)#2Gdtco9ORf0Y{o?QC^{hY`ZCQkP0L4a*2wR*2zB6>46#u5Fpeg&)r;o|?wd zi8AMzAc(g$8pOK?Y*<-Bn1WD-qA(_UdK!u?dBo_C^K7X?#sp0l&}x#;=|b9chsLx4 zFzS==t?^)V+teWMeVPI$rLlnZaFqxKrw#HGlhXw)!z}u=Y3##JmtrmZ1Z?U*LQs=g z@3Ts+wat?0;x@r&Pr|2HpC-sMYsRL^^zXhJ@!F)!ECn;UU9dG>*m^U$F(eD`%|hE4OqL+6B&LhrxvHh9%f%)m#_0<&X3mTQ%TYm2xPOhcv4RS0JaUUSA_ z>IqUHQ;TMjG&c|!G0vo?O{is(U%>#7jDs;|Aiib#W-&Jg6evn2%Twp@Z_Z_#be*`%&TZNAlsbzRz zSFbMYN_KGeo!=_Bcf?JK0Fh0^Z_2AQVqr~|xe6U?N}R<_&Wt@mHjA6GrQsvQiu_)` zZ|md{r*@>}Y@7A7m&$b?ipKoKk4;ZD@9T&8QXgGT7iHKQYUAd~n1bJ&Hlek$AJUV1 zwdnFSnI`bmiz9zv^GJqNQ!Ya~-q@_Asx5PxTWw!GH0$$(=CvG|3eCpK5C<81bK6R~ zXz<@=MqOw&gc6t6rz(%k2Km2<0J zsa8%4dL=h*@~72ENz9=kH5N3_T8mhFXUj2rpUmfPv7v`6Z)i34DOO+z5wR*e`C97> z)5wQJ_U9@e@dKJ1R8oNwp7oh7L@ePV*HXe6T_|B}EmXp&(j|ngqe>WGt&~uDId!G+ z61I_j@PQI05uFlp<0e{AWgAR5~3A@ zjGYLrN=TE1PQ|#4bahAnsC^fU&vFYfG_>Jw|Zh@{MVO)q>w2bn5hFeD<>lBqa?MrKWhCO59VN{AuE~vPm|(xo z`EJJaN-2$x{Y|TkTongm3TW=%Ic);^^GV@<~CZJU4Ed^2X{xma-HvD}0)7qU}OkV3tyB?iuJV zAv8{Wp6eMc1*|iBFjHDs6>06cY-(LvsY4^X??DcmDdHfSrfyB#LuoT%@DJA0?WIks zKzH7qv(BnjRk@R^Ms33`doN|+sA(yi2el0=Z7EH*rK6t0B$f7h(vr5Yw>%{*kL};s zBA9&I_Q+CJabu+v+H5ulJ%g0Oo@sM7RWkWCU&b{iYfcu!w%lx$S{b|CY9x2tde{8M zauh znVx3S_oxM&>qivc!eT~BJSFxf2A{Ue^X9PV8&);ON)~c<24qUyxbT~tg$X>SMAqcu zyKhs1(t3V!Mu{&As^dyoMlx3}qan}X(L^nVhiqC?+(~##)d(H$^QtKd{EBfpa<5?8 z8yRE64+)d}7R%U1Pl>{OmUR;hbna{HA7HW{?HDLwm!a{oY@i?O#Y4SW!qpQ!t!P4I z`I~BnmE_W#ETZ9T#YKRE!@R37k?5`7#=1soD~f~|St~J*V)?-KNzh@T zhT!rO=5|F!cF!V*jVy{Y^$Wu`S<9^$C4%|0#)p`hXvDDe43k^&Oxx*}Px2^#!dkh! zIXd$%41T%WJWgSa+*0CSm@vep{n!<=g=9>-N(LUa#)y7jK1R=`VPX&Wr?BP;5or`X zW3c$QS@(>c(PQyaYgV)tqWSAd=xEtb3^j5E85@P8yErKZ$35P&0jrA)N+Gf$O3S2Ale9|`ImKdO$-{pv$7wi!EkGIkCUiv?h}<( zUKSJyZsxa+oCil5tKhUCXFGCqf|@W7TfBsKx@+64#pu>m#?owGN3x%ney`lYv>?5OSx_6*kxjfDQ%#dmSTFBou)z=U0#l| zEc1XFMC_rCggPkVisen5%U}Vla|1 z^SDB)&r7Zb+9~0brWU7;8J*AikyP3AEqwjqu{7Ew+)Mg+elUDPQUzMy88SC)3|UJ6 z6U5eA_b~BS<_i+7i1$yT3%R8|nEY8ld=t|mni3e*x)R=cM3m=cIH^pxwyZW0Mz;D( zp*f<>-c`%gtZ2iiV&@{k#Q+Y*p;Ed`^|mFZlRecCJ#6L$`xJQS-gO-?bt>xnzoo8so-R7 zx>cL4QrL(qN0V!29m0HV%+t1cYreF*+dnj(H=YzPSy@VB#Sdtub}~I2D=}fz!dW!$ zOWxO_qG#B+Jz`glc!8@%yzeKU2PdC@Zuf19$d-1G(=I{3^th8hD;N|EM?BNk)1Ki` z5$Cj*OLsG5fgSgWL9d&IiezfLb`$rnAkff}0=U`y`d3;NT2vPrnKSECQOJ?`Luad91F4+^Zz_79J|7%n zI%$wT@Z_{?!|1ZzB!APf2ea1mbS|;o%Y5KRIBEu3p}JR^;rhDR zBJgA#6HE;qLt5s0a=yDcED@8mvKak!Fp7Ean#EvX@^Dz~dG8XF^ zTNYQAFQWu2n5dw$6tsj9;hv2gQV?N?K{}}5RT~~@ZGW&?`BsdCEEMg4T?CAbZOUOoqj=^)VjXsc8Or7d`&ANppbFI zvO2_hfcQGb4q?nfa+hkeA)a>+TiB_Ix2zgz+%oDXd#I?kM#Zvk8CB<+9BQ>L#OAni zXj{8tT_sdPmH%4&pBeNYz|P`#R#+NZ!d#y7fUCkn{+EW^Eo4@BXy#R+sixS9d5-_b4fQV%&e%)+5BG; zw$v@GDF4;r?$8%T!hMx_JntlzK;0BF(`tDR*wxfxHo5gFuZB>$sxneJHmu%nDRF1k z-4%|cW_O3RVb-jvV;{BJ!+UM95|89LH(U?yx-g68Iu^g)a--QR#qEkp?Y2-KzCdkWZ41x2d|~Zaell=U#}Jil@mV}V}7b0@LxBS-z+Wxv<+tCDAik;-1avr=Ehm%fq|ck4aC_AxT} ztKP;}xs9)^x2I>5``*fZwY$mbcKZ1PVb4r`74_oV$?rO9a6P5XtXl{+Q1(^4(OvY@ zy|njz#99j0zbS8*{pl-KXdgyQP4)7Bf8##dLp{EU_BxU_(tk_+>dKb-k@}Os&!mJZ zHJb&@!`EWtOs!U7jPD5x>(rFD*WFe3aNV8!-xXmsDekQ8CGZ^n7xIQx-m#^&nv#U6 z)y|0o6{vyL$kum|-~Xq&M7vUG{!G+N7>`kl@n*(njS%&ShiD!3nfqup z{cYjjzn@xm3vC^&_a94}E+ytU)N>x+s43i5X9@V3N56ZZGLP7Sp4Wo^HqwpM-#tq- zNv*7Wl=>z@RKg|kzmMERJ+8N0=F_HE)u}e~!rgT@)y=D@Pj0E46RtmSZ-pq}MMD?z zjDLo0p+5TCNzQ7uy_BNbYvk!~Za9#4m>1f@+}c%jlvY_-zmT{Z75Y2SN>mJLTBW~| z-@}!MD?Y0Dw!DS%+Dq@dj@G$}@cZyV83=or5cSNtxS%im(-M?7e`d=}^~38eB|n6W zoUl4vSAD4d9MaapC8T0TsEkmf`M72ReOB{5wEBEB^(k| znC4bJHO}@2SC6X!m|?gYHLQjGfc=33fCGVp!lQ62?#x2m85VXhIQ{Pj><=6O925?v z9K{d(KHAEzr#C6bSz!*j)re6e~}2hZBATA&=#GBybctJ&w@* zf!V+u;yvEtiVlQuG;j>?1mYeG97irQ0MQZUeLTNUB>p_y^9etJ5@t}s0`MpDJPA0N z(oP{vx{0nP*F^Y;3N8-P~gv;iA&Z=w}8^E?y(Jd39mnL!WIJI|*!PYmbd zz5uuoxQMWec|HS}ON+JBT9*JF#O$Qi4j}bYfG(gL=mE9@z2vP?& zQ)%G={DZXc5HJjkfV+&AUJlF#=Fmo?{Em^&;p8_L7zZYR?cjC*JAqxmZs2mh&=sV6 zI`B-=Jqvg??{g(z?K$9{OUcgzo)5eLcp>m2;40w7z)OIa0xu)~tI2OZ?rVT+fjz*> zfmZ;p1YQNa8h8!xTHre1b-?R^Hvn%0-UPfEcnfen@K)e$z}ta$0Ph6e1-u*39C-t9 zBdz%!df0n$zmK+RqL&@ad%U0C^#O32jnpqyw+{jz0&XV!!?fisz^$bJ2yh$U<)idQ z%~`k7BQ<_+CGNqb{}}Lb+@AnGiThKQ=Zx@a{I>(20X|C)o&n4T=J0<9>E{BUqwK@! z(VwUHc>kOezF_s66TZmv&KU1YJikm_QIxL$U!^a8jb1X7KK*rze^&Sg;dc?^o4~gK z<^OF)M-{x@@jLXAM=?rf1FFM!!F><-K5#c9=N{_t1M>VK`Txk?A%uGg`!Vnn;6C7| zGo*DtJ0r!l{z*BuPZ2Ii3Be%kO?q*uQ?%<`^6-9vbgvVINx z2KX)TJK*<>)_R_QAisxzKLYn!-YcP#Gs2&MKLdXO{tEsu3tMGrW`f%f_#0*X9e9|L zUeAdC6>fB!b>J0r}=>f1z~4*rccgu^Ox z!ec5M;r`pg;l%p~X^*gcoEAKm{Eq~VBJ6QIk0<=`gkMa)^LQRz*~HsyBHvBaW==S! za%Ok}c^+%|s_y^hrTFTX7w}ClAk77&N%_qPXICy{9@aQHGdz{|ej4>WhdM9A-3TnN zV9P1Ki~83J@?FV$uc};xPxH@(tTq=xc@~lPBJvhZUPRBE6V_OrW`w5`|6E`#&>Zt( z2~VGy5!M4OzYwN?A^Y>>)9I9we?+C->F#=o$REaT49%H~b{u(o89a!-=r;y)7rHk*@MP6OR zOk*R2?Uio&Q&-qgSqv{)3%e+HH_yv~JD6eXEX?s&RJJnSQe2g=6MVCy%qQfa2xPZKs?;Xi1%^ap8!6I`%}QDf!pzm)BFtX&jNP< zp9A-K;0wSPDdSGyOW?l@dUB?L zB>VvQVdXO8?S4f1djXaGW8(Y-xDWqNkskbv_y<--;g2+q2g1+E=YF`UUsT4zFTvkm z84tgzXkMORrMNvjNc>-u{x`sHsq626-{Wp0zlxRNZSn`)4}t$9&p!cw2L1y472Mx| zzXJ~g?*#q<{1f;W@NeKh#QQJL{{f*oBUFHDm9+>^!(Gp_0R~_OFca7h*dP1>JP!m8 z0uBZa0cHV@0uBWp4a^4S0EYpOAMw`b=_8^TwR;jOv`wPW~4GmjE3=C(uPX zCsNODo;~qsONL2=U~`TeQwxUeO}mAeSX+YxM+szaXIdH(%-elxT5+3#@6ES zOv-r{_-6xG0?z@SOMRY~mGMGr^XG$q0q{cLMZi_n7cu9jHt=_Pv87>+LHJ97mjPD; z*8tZ7dw`b%uK->Nys9c5b#Zug^{Vh1>hoIK@jB}BI^e*{mErZ(7ltNQEh|~Iyn*;{ z1l|O^8F&kDJ@8iGZNS@scL47M-UYlH*bCeM+z7k}crWli;Qhb{fSZ610v`fy20jej z0^ACG1h@_ODDW}hSY6R0oCKa=lYfWHEN18$+u z{hjy^1OEX2S-qD0_Jn^`UruUhCguMprvGpC74+q5_@DWGt_*e1P@oFb)x8}0PCB0T z_#1#3b;Iy~J~n3}liCm1zpgnP032BNif|Bd4h9YZW&w|?tA;}nB0R_{SLw9wdo=jj zz#QN(;4#2l;Bepw;@?Z1H1%g!F+88Z^H`q8 z)x9zt&-01EeBcCN0bwUnhZ-yFnf&^CCWMo~olN*Cz>|2dg>?snMRl)XjT^$^y4TwC za8}~=z-#~xmoen@U`gHUkU6~${OiDLZI{+{@Z#iq8t?G{-}cG4PY1L%SV}o()F}_> zH}0p@y`Ge>Cy&=#KRAncXM=w#X`cq@8*2@84!AZdRWpwE+_0=pJhOj;#yWWJy2ZiM zz7c7b+||$&)34yU((+v#R`Fa-UTZ8r=s(YM30E2SQm59+o9GWpKLa{l4QmN&j`^(P zxgI!*Z_+~edB6ssweHRI-PES4%Uj?xl}9bK0ULok=$AFx^z6FpvoL*wP1I*IFyF%7 zN*qt8Z{KEhep>;@JYu-FTV6`9wYlULS6AM_H%e(e&!cFsv+LfOm3KaMttm0r`stl7tkYUgY2TgI=_2K0b=q6NrFXp{maTGUgl7=9 zow6W7CH#)q<&pMxouV6d*GE7m-&NI4&!~hiPr;c1$u!#pdYvt*ai$x zhTii*q&{_pa&E1>uhdSW2ZL7s_Y+@bsJvRKpN^`0=>L%4&E%He*Vm9Qj%BG&)sTMa`@DqmTA`;e<$YfUT#f%4p4S3u z-#t7puHHgkn*T$1Ip0xz_Z8q@$@h8{@ano-`34G4W8@?Bi=tm~^&J%dHc~$`yoU6z zt@~)Wj_2#@HX^fP-9nr<09xz55&WC#D&ft*TY$eqsU8Nd2me;!ZNz&!AUV-H@V^r{ zs`4>wmv?3PC|wBe=DptvPI=al;vELe1|$a;UD#{kPLFTk{cf~))$kth?*-lmydU@g za1;4_5cp8t$HL9jdoFGFVeq$*?$)|bkbgCN1pi+0Q2QNK`6ReBC&2@dc0=`3NSzX| zGbens?$hC8w9UtXPXM1Z`gwc!RNYAUblqpzl}j>L)i;FCkoR{e=d^iUkue6>nfmD zyA#R!=b)eIJAEP4gIC&yn!bzLnl%?mXDT^kX+NI(*Hq6hG3U8l!s#))H>5ofI0!fx zIE1)|+noC4Fst?zn};7o`7^_p!l8se+VW&?8}}UGu-aF{V~97m_BCkC{^9W2{^1DV zvA~hQQMIqL8}N1dwIs#g2#>4X#e98MczjL#;Wxv)+MIAS`5a^AR>Ko&-uI8?c^q&& z@I=DJch2W|0>Y2w*!{|9pE~FE}$Fe z0k#6YKp)T#TncOh2B?qCWw?jPa~K!_E(1n^F<=~+0Ja0SLYH>%+zIR=?QY<5(q2)! zCp@zzp8W^#c0ZurKbWT8%x%PdHX!~~wB$F+yQ(J_&o3h;ETYWz?Xn86Ynd)S81oO z;r}}BZvb}z-^8yz{w?6!z;}S}g8v@nexLYvVO(h4>YjpJp-5t><8=*9005&j{|ug1RPxN z>-0l-&H^3VXZJO%%gfQ9%M@myU0&9J0?PB<0+X~2`goenIm|0z4r z>gPWTXVi;+eJh+v`lk@*ES_frPX(R^oJ0J3=rjKTv>&q!TqCd?XaZJ{W+l&6z-r<; z4XuVX^-jy5&hrZ9?{j&sCGH*7pR?9UW#?r!S(x(jzWP(jIvaO0dEQ+41!MXb(1l+> z7c@sXyLGga%g|PTKXcRlq`jZC+G9gHM)-rwMB3ZhM11Y> zZRV-*vW53JAGm;ayRiNN=Jp3j`v5V0?i8PTIeCcYY9H}%;G+5m(S3LzTwE`iZV1mH z|90RK^6UU4)9obBE}$Fe0lrFK+)Dj>i8r6TwXfT!__g8CU;k@n)5-azylTk&71!J8 zL8$$KQ{tA(zLb2o0ezHJV~2jA{#gCR`c=6TRX(cmjc^Gx7b)=v5s$}fG- zT6nhQ^(X5O`M35s?G~2yV40i9=YrTzBv*M3-|M+nSIQ&o`Q-Zoo-YJmRR8B`-eqb# zRIT0q60WNM>!kjczjq(`y%=~2@KWGqz}3Jt)KBlDe&%Z}i;=SKT73k{JDI~$C>Y0Hj&S>EB``v^T_>A zx+QORGS~~b0@_4_EkLv$14QpS@ zdKz`VslKkVz}~qoe2{WK1l&y6hk;wj^H$&^z-{$sg^yB?j}iWH;1j?ll>15GQ^2Qz z+kwvjuO{s_!aob#0elYlJn#kJi-g|^dZM&!~;i1Umd!+q7a5w&Yc>VzRAt3#gA5rGL^)o70 z)z7SyX?C&yIlbOXJBcQWhWyytob@sJuoA6kzsc9H@M6UIDdCmM{*^j3Mt+9-=ft}l z_d%25ZQ*^j-}^UmQ9u6$X@3da5B!QW3(2F2T@L9kJy3rTF%RP14`R2M`dc~hbiAX> z%nq(Jpn)#i`VTV-q4;#a8NaJNP>rk)h%0v&6H$0^BJHme-)6YkPMe6u} zApJw&{|NjE_%rYq;IH+ER{mBm8T6r*qtJAj4Io>V_D1FJgg*@YgS@s`epTk{nc<(j z-@m|1PV;Z_P5Z_F!Tn$0e?VwJrVqS}cvU9cx`szrY7NpcVSlt?R;8igQMAtiVMfDj z+8SNW2EInaoXY-yG-_r8YKsHFAIN5^c#eaJ+k{3<1Av~TwdKJv>=4SRp=qPM&g6Y* zp~|d=xs_viqBm)IJ&L>!jqx8%-F`|vf6cqh2B-JypkF?nZ#jp&4g>cX#U>l#^4s#2;8I^gN{6u@2>PPUs#5)^!Y6H48z&W^= z0gb?N%4z~0O})zR4FB7Zs z$$w!*?=dIrXz=~Io%^f{a&zjmyWzFra?)J^Tnw#wCeLS)=Gl~eCGebv1(oMg=O$L# z+E>|0+dhx7o)5Tw9P7S@lX&-&XahZy?6o<(prJXuka#Kni*WygcX$)D;ws#tcSrO6 zUFKd3FQz^(0bUBss+?FktkOuR+Gb988F|elo$|UGxQ2SpM}zGUU^XCKfP10sv#6iz zB2~k+Rz{7JJCf-o{YT{FeJrz`=#1ibcsuMIcHJU&c1wZkoz z2XwJ|Dy@qwopQcde;WCnM!M5T=W9{XkHgUTs{>{Ojz67wPvLuz$?ajyu!kJw_x5}c z-$xpjR&HxpSotXLc?9`JqOZr~o^2fz>Qn~5*48qa<+ zag)Bpj|hJ;efD0SKc-$k0qz5S3jB<8KL>t+`6HIh*ja#A zaeNPZCE@x_a&LmWH8=!!cXtgE+}#@7-KDrofda*$xD<*Och?jt?()57_ujzQ{^&o? z^JZp8&YYQ@ot+)KmLh0UggRX$wyw)OsI2{!@$ScJQJ#w;YGhz{C5xpPwN&h~HckBH z87hum-?}kAkG|PRk2k6>yu{_b4%saw{}tYJm++-jc8Pr>-yi2&nWcF)C5-f~zm;3Y z$cO3f*UukcWS6CX=#TeL|8M1&^XbRD0{Z5(e49r3f9Y<95&0GVp}V;GWiU5^tlXB0 zzWAoh#XKL+3Dtz5N!Eb>OZHd)%Dz0)ruyQ@y=f9q<^L<+Jftx(uF?K3W@V+EL`PXo`0f1=88!bU-^`o%)%r#sv;O3@)c%JI*4SC<{7Z&eXT+~AZLFlp z{EbnE>cw8?H?Hf)mPG;bEAy~S(~@?BSa)%gvGan|`+ZuZuU^pa^MzkaUtRo~Q0^Di zhX03uquBHd8|gQWy)I&0H;KJA^^1zV4lwj;>XUDlM^SVrOxneuIFzt7i`Cn|uHF1j zYwoMB0j!gw4@$gJu$FQujadfD8tJ!)O|P7B-7;35d0&{PGTiU-PV~PA{ym2!V}})x zSrMJ29jSy_8ARVI;IAW9u~);rI*UGQKuxFxwV@8w#jhUJCtL%rr5*RT-!Z%wBXMg8 zji50!fhfW^wRBaQS){EFQ<_`ClorquT3KXecPrYbR4DsymiByCN|^R)FH1+#>qxkcgyTJj zjNXK4M%yX+IN3q%W(9gEn0<#Zmd-p!<~qp% z_zi?X$Q}$sEM3$D_zmSf)sy-&3^~JL1m!J#?d+85NbI9<8x3P%Eb--=ZXEXUFaajQ zB$y0SkSpWDQ?XCOJ{@yqU{}h=d>6}otJ(2#ad_a02;%!b$9>;56|ss{Pc=>^zWN zooDbni~Bj;rM{deJsGE#@5u|um-ka0+{N!AVZvGYFZc2iaV~RxMdT4DK061*{VML8 zxc09xiPU;hi}-fsU387Si#%EWvCJFE_&Bcy*!6Q)qBXe5Als14)hTXyX?&{bfM^hd57sdHx%nE}5#%JXBIlO?E@Csx- zxXkmv#{R}K5n2BF$@)|3T`$%vyt7DMm9Yu)9?IOi)F-KHvhJ@1IWg;>Ikx>CSs&md z`hBuY;u~ucawnmq*=PLU^Io$5)4`e)*#R<%_wp&_vn4|LLcV1}p)m6eDhp9G>l8zt zjCbp71_*#a*vB`0983#X2`@WVZB~b3x5^F@ktg4G^35dUjAlC_VWL#oUGl9zEj==% zuF9U|D0Mn*pZJ^m!Tj?uPIPG!AUjgbxGvJ1NPRcgz0w~i?I?8yZLaAbWR2iNjR++K zJXUj_NXD{czmpePp%4br--^aPu61T?K4olLp67CuX*^ye@kvY8?!`q;0^}t$(waqm zGxbbl$eGQ!n0Z~08kE>qMoBP}LNZ7WDIg`Jg4B=((jxx{h@@_WlSVp_@!j;eT~zld z8AvaiejSZO{xv0|^`?@^dRxg%I2pf{y>LwMZ7 zU)c~Yz0WVbx`21BxSR7Q1&J^78S=pT!xCE7humuLjkLQv+&J7PY{su2O4TfAQtbj9jqZi9>Cr-Su?PPu@rya zWGsa_bmAr>e>dIQR5ot9h zP7_SoKN@BIRc&frt2X1hIkbRC{98gRXbo+kE%m;gb-hnNS?h31X^;LL2;UJpL1$k& zX4|qM_CCp&Ul(6J^UpPPMP@gUvD)sKJ)kG~>IJ=xXL_Trtk{X;iyKXtKKS=F{5SdT zAN>v7`ojPi2!lYro5QK|(k4pVDDC2Ub+fOX*rE-3_8we+BxP zuv=iORi1CN{gk=0Z9e&CJ0N-6r+N%|<}>gcX-=mMwi9m$y6%Ksu$y>$&}Fanl(G-` z`?)^A_3xMmf%!UQ?T?jp2)93ocNmUfm-Y2WF)z^e{9m#p|1Grxv9i(|y0B)K^p2DE ziT_g{`l5t6Y2<~mDb`JzZMm!oJ)@jLj*KtM4okC6%DdRy&vhCd1AX5#QZJ=Gox$&{ z^M^awl?0_}vafI@kG~U2lcn9y{13G<#Pw<(zGRFM{cUiDp8(l}!hfL4)K26@O zr()a8^pwvi-QQz9IWo6zUvz!(IdRWryGI$^JrlmH?Ju<)etAi^JUUdMOr^{!;#LVNgY-A5V76o|r7C7M!p!2{ zRHwXZ*dD4iZ4cC1wnvN|JXSM8D46L!@yV97&3C(e%ho2{LEQB^$f`@4k=*}!o* zv09EgU?sPikkJ6QNc>H`%r)Jzw!0y2ji50!fhhE7YUszgM96NATMKB3+eNja(#lp* zX^nduXbbIde??$CocGMCcRh|8LG(yfiXH|CEJjlB=_MYn#0H}n5O?QeUk z4nRNIX+My19t494Hw1Gi41?h?0!G?o4C@tnc+GRg^9%A_D0?Yw?9MmcShql#nf2x^ zdF9(eeHZID3Y|^A_xRCH+CI>(i7e59KAi0%?h(pZ+b7KSn4d8VV1B_og(*!~Crp*E zb@O{{oK4fD-IO&6GJZOq{7is}5Y7nzvi4#U=}v|$)afahQ;j?a;HPWTY`Qibeu5eJ z&4gLDKuy*m$@u$huIIp9m}iS)$n%ejNWa@$|1bJm{=?6TzVy|o2lH(svevKcjON#(7t8e^%P;ygnE&#QztNb$8M?U5$MW z;pO{vE#V_I2Tu<3+>0`thOn8m%rWU; z@uOaIo_CxRo9LE0$9ED?W+!jh%`p2fivc}~M=2?*TFB)Ahs_}{MP|i`$&)X7cvS-6W8A|`gC`;ssEGnUL z0eKgRdkHRsl;ai5tKzOEM1SUyxt4y4)VG_wcdv6TW5_os&$yf#A$#C%qSGz7O+3z_ zG~(SMoUH%7i+K-sxp()mKY$op67B)}$cgii>qqbyp1|L>r0CDSFycJ3B{RxooSGar z`c$O(!j{5tyUK|ajMQi^ZK<_awlrE0*D_xE8X0fkExd#G@Bu!;C-@9sfC*%Umz1J` zQ%#G`5?{_fpxjy(NShQyb+>8Y1y zoT(%&CuR7r9fp6#SeXuFIzi5*aoID)hI8ZQuxE}9SDm^Wgj+C#*t5ijlRiBwySO&r z9cKSuPT(-xRS)64Mw$`S4bh49Rb0zj`7rV_o9A*K{SFy_ieu;94e{`gZ_lnJAZ|kJ zS=AiIJxYWVzq=Zylr-n3;7Jh(mdmckxV&tW> z=QZrPSR3+Xofk8Rq~UiL zw~}1Tnm77B#LI1$(@CT*aZ(p?A|NmN)x6WP#Q`(QJhFRQR&5#jd zQOQv`hFQ{H(=bD9wR~o6BWxVTRjt-KhFd9nU7uOcXVy2&tCX0(ohogQG~CMA z8yaR=dm~?%#zvTOHUqoAC2^?+M^Bg zs?mdYGR|kFC-w7SU|&PVRZd)q<8!Y@8r7i&)TAyYQTr2y{f_o6Y7S0j8b=#Z8~2NR zYfJyJ4$pF3s7D>GkJ$hsu{XqQ1dXvbfhcH7eQgHKp#?HzjsfCK>P<` z4z>@_vQu{QeJ})Fhr&4WH4Jk&jDV3aim;<$42*?w=n{?m@z^K8M3@AVVG2xzX)qmr zf*CLqX2EQj19M>>%!dWA5Ej8=SOQD!r&uF2P+Nw5Ir_>t*3Z~iz)I4WeO{|DW!(A~ z+*V^}ha_V)vX_c}4sL7F-Tb~EWZc80x}2ip-^)8h%SDVY)V5#kL$!7GVcL3R$ok3+ z#M?;xOQno7A&Xb;KD zTKZXS(f1L$K1SXXs1P_7y)@--{ADcS5bqe7dv|hg+r@t4xL7kdPJ2oidG6xTpMOT& z=kNlZ;>MA+;1Nm=zJFiZBa~P0+IW7W1Zt@G)ay|fy`ecnS@;bI#8=aQ*6+ke3@r|a0VvJK^)-3-34w4f?$wyVngurU?0i7 zm=9j$he8-`vL+%fW<0{k+JSS-+s5a*KV|~l62cnhUb~X-M2;{eF(h#e(2_caXvrJ} zl;n;rY6{#^I>u|M922zE$W7xIq@*R@4-gLNIG{5?mRvOZt(E8|P%9w@U9WzHF~7ZknZemW?ZnSqmxdLY;1>BmaDl-VKQ;FFF1hpZ9K z;+Uk}S5g~eJaRrW}nkvN@(A)AXxFor@qZ+2J)OT*-Mb zIUKDyd6~9|dY6;@M04tuJPWxT0ZMMj0Xm#pFe)661n38q`i@L$80P7??E{arY7-Zom4H%vFK47vko%r zBD)^;`p^JfvvPkTvCID1hM0|@F*JcFXbR0h_TI~$eCabc=eh;>-$^a8w}RHt2KjBF z9khoI&=ER8XZU({0An$fV;9`ILO19RJ)kFg%DDm!pcnSu$o&yrWzHdrIQ}yRq6ymv z`a(bG4+CHz41&QRXP~J(cSEob!yGCm42KcO9*Nvzq%#WpXx!ctehlVV7zg8F0!##X zANt>olenHtoGHZD`A(h6^)#3cKfw%`3A11}%z?Qu59Y%HSO|+?F)V?l$Xf=>9UYaQ zVFhk0VUMNLw*LTT2=JiZ1Ig*JIwLoHk%)ApS7) z-iWygHY0Zn&*@gjStXZpma)~dv^;aPZH`&mZ#-XpIYDka{yQA=w4JbvG-vTyVcOsU6mKM!L+0!QH($eH2C z(f0)WiLUZ~K8gJloQ5;F#l-dzIRDu(T{}m-^Kij2U%Tj#Ipzi0CDOYLSJ3;lepb2a zSV()akY{`$&$#(rc+F^=7STQ|!hMn9-i-c+^f_dXLFOy3lg}HDljP+j<#LjonYzo~ z&zq!q3vR<5xC{5tPo8y=A?^Gr+Wr*eLB65x;}^<6%nrWu9}qT%YuV@Ym&0GaieLn5_HaJJj-Ze^3SXn7!7Q&=&gp8V=>5V<~OB`TWN)2lR+~@z~IZaR$ed zZ~7&ibwF7@g=gp?{nM4&cuh{`m$rSF(RT3e#{UKJrJa^%M$XE6NtwQa*YF13!aH~m zAB_AgBb^Ca8rtrU)P+yT5SjjTJ`?5($lM8w*cBBt&>;WZi$j^eA6|#Y>;fcV^4mltvYD4<(#h6cK*UUPsSu=+=2cy@1u!4AFrq% zQvd28uP*x4BVK)I0FlrT8bM=_Gl-fvS8Fv5Im@*u?n_hR$hr}8Op`MaxNZ(Dpe3|2 z%9M80`xR$l2(WKJ{`a(a#_s1N7oPjV12Ez~-O88;;4TllfM-p}v?xV50c|6A8 zJ{HDtJsu{&M3_XF$uI?`;y(?h!%xJYfjN^fvtT{tJDYN!gZo^<&Vz*XD+kgCn2)_5 z<^s%x&W_3==Njtd8tTg$qrUVY?qd9wz*1yMzLsHTRpk`WwS-&iYcI|IqMSW9#W>UO zSEDbuoOn+t+i*?`RjHAutm~=EVy@GEMxPbPx~TS6R+9EAo{3+$UX8mvJ8Ll4l8;}F z^nTS!aMGxo6Gzt}Q~FQqF*m?Q*hJW?%r$IwwpX@5QrJ!on5%^hCPJei;nw{yB|5xNIii3-=Puh z=Rv3Jt(5t?2<4D-3uPm^ntAyHGc_6??NMqZ$960hQ?v%ZW$I< z&f#_*E`YQJG6o`Z6Bp4*&P+>1J})`<@XnO-FyB#^@w-BJIS=G2dS64&e zF3scG!gumE&FlJ23w3SR!dyGFxUQXAJl9q&K5`O3LgXic#BczelVB!=WRM(E5GEyQ zq{5yW(m+~qN9P}K567Mk(!2I(8C-j{jIIq@Cf7zSvum@Kg}7NE8{r}dlO1wEPRIqh zArIt*Wwg^y%zTjF$P;tam<6E_ZiS%;_M(`@pg5F(l28gtLm4Ow<)A!NfQnEFDnk{h zii~QMQ+4#J0X1Fwv|6qMT5aw{9j@yV=I=OrlzNnLeb*nxd{hJ6BB3F1<^DFp-WZxd z6i}s>jO4anPw>`jE-OdZ(blCdH=hx4$ul`z{}$CTe-yX$MeV~1<0w$mkJAYsZb zR{>?WOU_b`tL!2CUhexo?&tnEC$#;pE9fbGZ}!3>|93bDd^ymzo9}}|)U!X(;V$_p zOds|z_9Ji6~_*)6N(=oTq&_&->v#@6PkuS=R;aoKbgq z&sZ+fnq0*1qTzSmbzZxGUvJ(&QhuyDqYeL7x66FbupW)H0yyRRlIyB=*>z1j?YfQ( z6TV!uE3TW`Rl;0@>y+IMxCslGS6m3Uu-_(~J3c*Rzj6!`@1plT^8ZY`td*eWuR|zE zKQym;n|I`Gu5WXFhcx)MLCyn^=OPC4FL(%#@OzB;1pao(x}>|>Q`bFozeoIghCKOB zd`38SVBmfq_xpzXb99JsK^P}LP++FRdjY!} zf$N&6TC;~wJvo3z6e59xU!6v7}bhz{}GPqp~&vswc8Gc6&yCUWzA<9>o( z&nb`Rr2m}sWewB|o@L%2=$90d(XmYKerY`4DY#AvsUWrc74?sCMEui&e;=T%k@x}s zaQAEDo24pk$=5k;-q*}4n{(cB@8ur8G5Qfn=;x<%OZs2eb)_d?86YG2W`d3CTi&y8 z(N#`5=X(fb-AiWD%i?~aWhG1!+Z*kr{iT)-{UXpIyIa<4y`yg-`$gVsLMX^Sy&RN5 zE_5*051H+k>}`=XR5{60F31d7ke?g!Kwij)o2;G4k173v0_a!}_d-zE{Z=dDmN6Bx zk5|+!?M)lC826wZ-&@6rQ^NgTD+#5bH11_!AE&jKMW1r`%Ws~@u9ouHE8tcUxs{+Y z$oVH#+)hf(JU^l;ZgE(*@{oM=qn@_Zl=E%*BVgn zk1gd%qw@qp3{)|r2{%efD^Daftwe*8pQV>zYuW)h zFL>SaCD1maF4{kWXY zABSJTo8<1MOok~i6{f*-cP3__?aEJXFS5Mk!5fehkJS2n>6Q;rnK`i-XQC5*uA8e;1>X_hQEA|Fs@0sc@RZiHroXW6Q8;vGR*k% z-Qt!&clzWmApAmb`__(%oJE8+(~)PzpH@P2H06vSb@_%^Y{*Ka+kNhfjrz{_2J)67 zL*|K0S$-Lbb$)*mUF5qhfVI=6oMnVJ&)k;%-kcXgS(|IB{~14tuRP57QXVp%FKf=0 z8@eR*mB&BjNjiQVl93Ppc%SSw_4|F``4o3q$CaA1XdL{$NowV1qa8BalNIPGZOTf_ zRUkVdWe;2z-j}kf?-!oyS&SyG#*}{48pv#g^&j=m>|FS@RwyzB9-{E4n7;gq2> zV+wBRFMISe?r{CAJDq+G_gaKI?@mvCGSY^~d(Dh<0rMiPq5fZTXVMd*_hqiHKsvZe zde<=JH-oNo4P5DCH4uk8z7pvnJiWz?NPxpY@h9@;?09dHW@dZYzI2zC#6Nh1_9jQFz8Fm6!Z z|0pl{jS|MjxqqohQ|1w+eThf>_{2{D2_X?Ah9r;_l0jYSUvkV8{2*6KOt&Qu^(!BG zNt@YZ$`TS-ju7kOaulFiU$rhAPjew6l zJK51a2l>nixr}>Tkb5BCy@jbGMX1X~(5r~?&dp6&`Tc-AnDUJrmlAe=x8AU>+Q>tg zk~fH4Vb2c*h+7Z}1r_6c{{6aMgE6DRgf9|QTu=TV;bfhTKW{~;AH`seP4-(zKliqJ zhWjsVt+a)5u3K^ZrOza5Z)I<-lyM2%N}^LK%Aho9uBUEFU6nb|({8DkC3t4Pr)$ao z@GHeV;~f}OnlPm)$I_HzDc#DTeF}!^&fr%zs0@$F_hg4Dp5-+1!+$zn6X<69+;;($0g-Nc56A5^cQoeSH9)k%qYM zqpu~uw01_Xs5c6#s5i!~2}A|)OOHWam15kZX5543&;nWpN#8`)X~}+3emf2Mt)UH6 zV6e9>W;>Aiz4quf0Jx@c<@al?NH|P#MpeOW#-tZ$tLm%i1 z{h&V#fPpXw2Ez~-3d3MHjDV3a3S@p==Ho|W9|L1y9E^tvFcBufWS9a|VH!+_pMok; zuK&NzvVL2B%fqU7$466UkS{Bpf|=xX79^vh9Sp3b&koAY+A8xMC3_|3Aa6D;eILqR z%EVk7Ea!Nd{XSVsG8b9%kP)9nLGuZ}02bo52o}Q<{BDx!Qrwrpa;~$ga&9|w`a!3d z^Y+&v|6U>gcL90i-LQfGVf*CZWA?h)#7FEuse58aicT$|ra zQa17o(_g~v677_%2j#1pzMkwaquy2SKumKgX?euZU)_9A0BfT*l)ufxJ!A-8JqEyySzp2vPbAHy%YIX zWKIMB`?2BpW}%&YK=?@Qjv9meUy$D-aryqo{SnuZ+?U7bDf6UHF#pE=DeljF{Ro*$ zc^*`iJoCL9n|3w6sjAVgR5RL@1nBS*zgOgy6G8PD?2;~L4$(JZt_1s=pz4I>x28$s z9rE8}et?hg2|mLY;D9;y4>LZ-4{Z)ce=V530}ud#_*KU(4t5K6E7*c-(APHeWXIhB zPH^Gp4!)}fVGqV00v?d}iWf5!!te{{z9!ad@|`3y%<_qgd%WPvdi>y8^eJkQ&sx5G znqe|f;t7KJRpsECdLqJ_I@UJcC5eOU(6^{VoI1pj{d-a-=C^zj!pgbhNimZ_a!3(e zm$sxX;p-Bf@iN@Z-;9xX^?1LFe$2yA9`R|LQ|#k`B4)gKO(s;?&bK1lQ;OF;0dIshgRQof)z~R>%es_{o~j?37au z>{-825JEL|LQ7bcY_Wj62yA|6bU8!;cUReV{M&gZ?l82Erg13`1Zj41?h?0!AWd6pY56Rc)f* zCZCeWF)$WdlK*j-Ql8^M%3uOagh?*{NP6V0?dW5 z2o}QD{O<`U_0!9ov;gb!yecR z`(QsDfZyRD9D+aKFdTuSa14&a3HTFE!YMcnXW%THgY$3!F2W_a3|HVPT!ZUy18%}C zxD9vUF5H9r@Bm`qFL(%#;4wUbzu_r7gXi!9UcxJQ4R7Eryo2}f0Y1Vf_zYiwAB2#D z?iCifC^}4HE;#@*5aNIZtY8B>IKT-ma6=FTLkM`l3!xAOaUmYWhXjxi5i2GNCRm@7{kX5hjfr0GC)Si1eqZVWQA-H0ofr3OIKL1`!hWuY9DhYC;;DnVta0#%_JREHW+6KX+ir~`GO9@K{h z5D5*T5j2J-5Cu)488n9$&=OjOf+S6u6 zd)h;7thWhiqPK;1&>lKKN9Y8dp$l|{ZqOZiKu_oez2QfQhCb-fHzZ2$7t&Pk9}=Jp z2x(4R*qpSQ8)*#;k@5Z(yz^S%*1~WbgubHZ8GbD?4upcV^Mi4h@1P+eVam{uR>WzA zUn|3J7=E&b;S9elDel9$9%0C)jWXi3;mu{bj|yoU8)h_qW6*0XW;DM<$ti}&Y-h-n zb&NY~?FnnXYsXQy&e6{rkGWLuz;`(PbLDW!b&V17{Z%x|wdGF7_j4{4Hbk-W-?<^I$$KfQ7IK7Q+%) z3d>+Q{0u8#C9DFc+Et%Tnw6lkktfb>!F@HXfwk}}tb_Hi0XD)W*bG}>D{O<`&}}>U z+yOgb7wpDe+WS5D?Zv(i_QL`A9S#!a5PpBaVK@Ru;TUCh9PaV}Iw6aUL#keUW-_2`PH$27v89av<@Dg6ZYxH^pZ;A7ca1qS^yvO|me1uQ%8NT4hMrB0> z7M&_`GXp&IA3dzC!M=;J9Sc9;ZUq}|c5r|bT;L{b5PrcB!nFr8J7aVa;3a-2gy9|+ z;(59&@i7xXLfjKUVn_l>AsHk`UJCqEA}1B3hBS~Cet>YeOJ^k=VP!2yddv)vk$9OP zGyYj1D>Aa-9s$`Q2juj0&~te@>A5|f^*o*~dR|XQy@b+)F?i-3`BCNkP{6}JNGJq_ zJr~G5<;DL`S~s2*|6b|tMjzk5cl}1-BV!JCC{Jes7zPRTib&;~n!e1}-&oCTxXGTE zBIs8Xih1_Y2KvSp_$64`uTdU~d*lS~ihLW(Iq{FQ5}rp|Nhn2}(ohD1-GgoV?os@i|P==FV5ERoYiW2dg+8(e3e)Pp>%|Cw4pgFXFme2~S(`Rmt*#_EzjBT_d?e@?CWNf@6W+&(j zUEmGT<+>mAhXKT6%MJNE#{9q8?hYj0 zAQ+6Fv~NSO4~1bc9KvxMfjJUJA!9U*fw3?S#=``d2$NtkOo6E|4W`3S)V~?TpXrfz zhKyxL>+=05%));*%z?Rtn+Nl;FMx%x2o}Q5AfOP+j<7Z+E01P(#Hr>PJ0INE+4F)A^)=P;4JB#gY$5~ zGsJk8eo|TEga0MC3`rS#Nk(0bi+M0mo_@yqL4Lc)Lf$h64%6esjwi`lYk9Wm7xB`) zf?R3uj}ZPUvYL~ol-V`U*L?+TwW0cT(w4cI8-%YwxwWG_GaCC129f9C=*)YB__yFT zNF6|X?T%*zc^t`@>L~rLXEgmMX=BFd_dH|t`<^i60c9Kmf5Ag|gxh0y;_0va?HR5= z#r_PQ!wY!n8LPj7*YJkxx3nAYJmd8D==A}d>UjMlX?{X}I?@RRSqJjj<50ffR*q5Y z>T#Im_VTXrGJ@@$pzGdCT7dV8637qG%6hIiURqc%*Wl1cC#-`#O-u3PI19xN4&0pJ z0yhLfF!W+=RtWwc??l}Tq25V)n3pv=V64%>jPI588+`kF+1cTptb6&T=unWpTV759 zDvX&38Hph-tC2mBh4GanTqlKOkQ`Egw67^KOA#lWHD9T`Q@H^*YLwVn)Mc2HH+xi(Ia^Le2UY?Wu=q>Y11-LHgmH9<^j^|LH=OAYea#*heV;NU0M3}?^H}TOIN&&$!vf&ZW%elGnLLnbq~qWBhC$*YkX3$h(m;t53WJ=oE<@882+k;oPNOHP literal 0 HcmV?d00001 diff --git a/materialsrc/editor/comp_cubemap_parallax.pdn b/materialsrc/editor/comp_cubemap_parallax.pdn new file mode 100644 index 0000000000000000000000000000000000000000..5530c4dbb733fcc746ae5fbe312793eb862cac03 GIT binary patch literal 17108 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 X+;LRic}^;` z6`vc)eV4Ga_oW(Y@aLS>-4+{m(TS$*?rF!bab_)U=*^=zn*+$&<6HA@eSKP!@}!Tc z?$BVa}?^;k(w$CSAJ#ot*x$yHIPoGCS%GN!M_IO{{ zdgJOyzBns)h@=>Lt!9(5n)3VgxVyJ4meD=u*;a3mDL)N(ysC|^AO2&Dd+5YNB|j~y z;$DzVz1Q=@qMyqH!$)7yIJuo?hF1G8vSv6e{{B1Lx9};^Y4^4;M`xd`=y3Nc=Eaxk zBXPO=(^v4yHQQ+m7d8@C?rs-1t~yel+5Gz7R=5)Jn2KI>**7DNoC=O^J(Wletl6#3q)>A%sh5y z-s#G&iyD8p^?C12MBU7-l%A-k!mX#m(<7^wen9tuO%S!wjcRpg|3)-)`ZiAjcYXGp z$JprnjFQtO^JZh;=Dh7u{d!dO+di2gN1s%xZBua+d+GA!AD(^ketFXF#|}^X!)ufE z-8){#1LL1Yh5~P{aJojkmzq*YbB7*E-I+Iu+2!o*>m7`}dM(OW^z-IlI;K4u=Q#Q1 zyDmcaejBONQ_a}htB&TvCgV{lea|iq&k=RO&9)|h+LnmJxE~H*l)T zpZhheB9uO`NIFE*u5CR1bNlJbv$Mt|TYB7EIK2;f*ZBc8zisJxK(MZ4>xJKbnQzMB zZ!De>I-`bVX>JqSeCa{jzH9Wh(Kpe--~@g4y&rF}7={f)zcgN*dgtdRoq}k1pL{*5 z{gX#qMukp{eOfn^!dmlAaRIh}@vgPw`v21C?Vo{4)s0&sb|$VgWxpLK7#uDfN1$IM zwn#jdgcswJ+mz#ola6-mJ+EcJ@;5U+f8XoA{H=NDfd!(X6PItTx{B)XVdeFm-wlP= z?On2RHqp{AWkF)UPQ8yU!46x3ymy#sy9xU;?ZL{%$4mMV);kH2AvnKzP& zq}!R{HrFr&ZF<`|?pA{ Date: Tue, 1 Aug 2023 09:37:38 +1000 Subject: [PATCH 189/243] Use the config that we matched --- transforms/comp_cubemap_parallax.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 2df7c2e5e..33c71dc6e 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -122,14 +122,14 @@ def helper(a, b, x, y): ) continue - config['used'] += 1 + best_match['used'] += 1 material.blocks[0].name = 'insert' with material.blocks[0].build() as builder: builder['$envmapparallax']('1') - builder['$envmapparallaxobb1'](config['obb1']) - builder['$envmapparallaxobb2'](config['obb2']) - builder['$envmapparallaxobb3'](config['obb3']) + builder['$envmapparallaxobb1'](best_match['obb1']) + builder['$envmapparallaxobb2'](best_match['obb2']) + builder['$envmapparallaxobb3'](best_match['obb3']) builder['$envmaporigin']('[%s]' % str(cubemap_origin)) encoded = io.StringIO() From bda1f10988574e7b3453274a1e227fca21e349bb Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:37:58 +1000 Subject: [PATCH 190/243] Swap to f-strings --- transforms/comp_cubemap_parallax.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 33c71dc6e..3c3d101b8 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -80,9 +80,9 @@ def helper(a, b, x, y): 'radius': radius, 'radius_sqr': radius**2, 'used': 0, - 'obb1': "[%f %f %f %f]" % (scale_matrix[0], scale_matrix[1], scale_matrix[2], scale_matrix[3]), - 'obb2': "[%f %f %f %f]" % (scale_matrix[4], scale_matrix[5], scale_matrix[6], scale_matrix[7]), - 'obb3': "[%f %f %f %f]" % (scale_matrix[8], scale_matrix[9], scale_matrix[10], scale_matrix[11]), + 'obb1': f"[{scale_matrix[0]:f} {scale_matrix[1]:f} {scale_matrix[2]:f} {scale_matrix[3]:f}]", + 'obb2': f"[{scale_matrix[4]:f} {scale_matrix[5]:f} {scale_matrix[6]:f} {scale_matrix[7]:f}]", + 'obb3': f"[{scale_matrix[8]:f} {scale_matrix[9]:f} {scale_matrix[10]:f} {scale_matrix[11]:f}]", }) cubemap_material_name_pattern = re.compile(r"materials/maps/.*_(-?[0-9]+)_(-?[0-9]+)_(-?[0-9]+)\.vmt") @@ -130,7 +130,7 @@ def helper(a, b, x, y): builder['$envmapparallaxobb1'](best_match['obb1']) builder['$envmapparallaxobb2'](best_match['obb2']) builder['$envmapparallaxobb3'](best_match['obb3']) - builder['$envmaporigin']('[%s]' % str(cubemap_origin)) + builder['$envmaporigin'](f'[{cubemap_origin}]') encoded = io.StringIO() material.export(encoded) From 84c30f1f7e05f9763bedb0e1bd21fc8f9ac216dd Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:38:35 +1000 Subject: [PATCH 191/243] Handle parsing exceptions gracefully --- transforms/comp_cubemap_parallax.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 3c3d101b8..299f9532e 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -106,7 +106,16 @@ def helper(a, b, x, y): if best_match is None: continue - material = Material.parse(ctx.bsp.pakfile.read(name).decode('utf-8'), filename = name) + try: + material = Material.parse(ctx.bsp.pakfile.read(name).decode('utf-8'), filename = name) + except Exception as exc: + LOGGER.exception( + "Could not parse packed cubemap patch material {}!", + name, + exc_info=exc, + ) + continue + if material.shader != 'patch': LOGGER.error( 'Expected cubemap material to have the "patch" shader, but shader is "{}" for {}', From f5f70aebdddb1760fad4dec5063ddbb555471f80 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:41:48 +1000 Subject: [PATCH 192/243] Simplify this a little --- transforms/comp_cubemap_parallax.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 299f9532e..9ac607dbf 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -28,12 +28,7 @@ def comp_cubemap_parallax(ctx: Context): diff = maxs - mins # ensure bounding box has volume - if diff[0] == 0.0: - diff[0] = 1.0 - if diff[1] == 0.0: - diff[1] = 1.0 - if diff[2] == 0.0: - diff[2] = 1.0 + diff.max((1.0, 1.0, 1.0)) # we need a 4-component matrix here because we need to translate def matmul(a, b): @@ -55,23 +50,23 @@ def helper(a, b, x, y): ) rotation_matrix = matmul(translate1_matrix, ( - angles[(0, 0)], angles[(0, 1)], angles[(0, 2)], 0.0, - angles[(1, 0)], angles[(1, 1)], angles[(1, 2)], 0.0, - angles[(2, 0)], angles[(2, 1)], angles[(2, 2)], 0.0, + angles[0, 0], angles[0, 1], angles[0, 2], 0.0, + angles[1, 0], angles[1, 1], angles[1, 2], 0.0, + angles[2, 0], angles[2, 1], angles[2, 2], 0.0, 0.0, 0.0, 0.0, 1.0, )) translate2_matrix = matmul(rotation_matrix, ( - 1.0, 0.0, 0.0, -mins[0], - 0.0, 1.0, 0.0, -mins[1], - 0.0, 0.0, 1.0, -mins[2], + 1.0, 0.0, 0.0, -mins.x, + 0.0, 1.0, 0.0, -mins.y, + 0.0, 0.0, 1.0, -mins.z, 0.0, 0.0, 0.0, 1.0, )) scale_matrix = matmul(translate2_matrix, ( - 1.0 / diff[0], 0.0, 0.0, 0.0, - 0.0, 1.0 / diff[1], 0.0, 0.0, - 0.0, 0.0, 1.0 / diff[2], 0.0, + 1.0 / diff.x, 0.0, 0.0, 0.0, + 0.0, 1.0 / diff.y, 0.0, 0.0, + 0.0, 0.0, 1.0 / diff.z, 0.0, 0.0, 0.0, 0.0, 1.0, )) From 88fb9b7a90e688393863c12d09a9477367da2398 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:46:50 +1000 Subject: [PATCH 193/243] Handle case where points are swapped --- transforms/comp_cubemap_parallax.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 9ac607dbf..731d429a1 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -23,12 +23,14 @@ def comp_cubemap_parallax(ctx: Context): origin = Vec.from_str(parallax['origin']) angles = Matrix.from_angstr(parallax['angles']) radius = conv_float(parallax['radius']) - mins = Vec.from_str(parallax['mins']) - maxs = Vec.from_str(parallax['maxs']) - diff = maxs - mins + mins, maxs = Vec.bbox( + Vec.from_str(parallax['mins']), + Vec.from_str(parallax['maxs']) + ) + size = maxs - mins # ensure bounding box has volume - diff.max((1.0, 1.0, 1.0)) + size.max((1.0, 1.0, 1.0)) # we need a 4-component matrix here because we need to translate def matmul(a, b): @@ -64,9 +66,9 @@ def helper(a, b, x, y): )) scale_matrix = matmul(translate2_matrix, ( - 1.0 / diff.x, 0.0, 0.0, 0.0, - 0.0, 1.0 / diff.y, 0.0, 0.0, - 0.0, 0.0, 1.0 / diff.z, 0.0, + 1.0 / size.x, 0.0, 0.0, 0.0, + 0.0, 1.0 / size.y, 0.0, 0.0, + 0.0, 0.0, 1.0 / size.z, 0.0, 0.0, 0.0, 0.0, 1.0, )) From 3c135c82675581fdcf88a35918924b4194dabaff Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 1 Aug 2023 09:52:47 +1000 Subject: [PATCH 194/243] Automatically apply gold colour when generating comp_ sprite text --- sprite_font.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sprite_font.py b/sprite_font.py index 1e6de273a..6b483b84e 100644 --- a/sprite_font.py +++ b/sprite_font.py @@ -1,11 +1,20 @@ """Generate entity sprite text images.""" from pathlib import Path -from PIL import Image +from PIL import Image, ImageChops +from srctools import conv_bool from collections import namedtuple import sys +try: + text = sys.argv[1] +except IndexError: + text = input('Enter text to produce: ') + +golden = text.startswith("comp_") +print('Gold' if golden else 'White', 'text selected') + Char = namedtuple('Char', 'img width') LETTERS = {} @@ -14,18 +23,16 @@ letter = file.name[0] img = Image.open(file) img.load() + if golden: + img = ImageChops.multiply(img, Image.new('RGBA', img.size, (224, 174, 0, 255))) LETTERS[letter] = Char(img, img.width-1) -try: - text = sys.argv[1] -except IndexError: - text = input('Enter text to produce: ') - chars = list(map(LETTERS.__getitem__, text.lower())) width = sum(c.width for c in chars) + 1 +height = max(c.img.height for c in chars) -img = Image.new('RGBA', (width, 11), (0, 0, 0, 0)) +img = Image.new('RGBA', (width, height), (0, 0, 0, 0)) offset = 0 for ch in chars: From 9249bf07d32aa42212d71e440593f3f5c1459a29 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Fri, 4 Aug 2023 14:04:36 -0700 Subject: [PATCH 195/243] Switch paint bomb dummy model to futbol error.mdl is not always precached, but paint bombs do load this model for their collisions --- fgd/point/prop/prop_paint_bomb.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/prop/prop_paint_bomb.fgd b/fgd/point/prop/prop_paint_bomb.fgd index 8af438002..fd5e482ac 100644 --- a/fgd/point/prop/prop_paint_bomb.fgd +++ b/fgd/point/prop/prop_paint_bomb.fgd @@ -15,7 +15,7 @@ model[engine](string) : "" // Not actually a model it really loads. - model(studio) readonly : "Model" : "models/error.mdl" : "Paint bombs need a model set to suppress a warning message. This model would be loaded already." + model(studio) readonly : "Model" : "models/props/futbol.mdl" : "Paint bombs need a model set to suppress a warning message. This model gets loaded already." // Inputs input Dissolve(void) : "Dissolves the paint bomb." From b736681c3da77229e08599eba13201d37909bdc2 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Tue, 8 Aug 2023 01:54:54 -0700 Subject: [PATCH 196/243] Fix these not being tagged as engine --- fgd/point/prop/prop_contraption_cube.fgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fgd/point/prop/prop_contraption_cube.fgd b/fgd/point/prop/prop_contraption_cube.fgd index 93fba9daa..3e17b76b4 100644 --- a/fgd/point/prop/prop_contraption_cube.fgd +++ b/fgd/point/prop/prop_contraption_cube.fgd @@ -4,7 +4,7 @@ = prop_contraption_cube : "Contraption Cube" [ cube_mass(float) : "Mass" : 40 : "Mass of the cube (kg)" - ContraptionFrictionIndex(integer) : "Friction" : 0 + ContraptionFrictionIndex[engine](integer) : "Friction" : 0 ContraptionFrictionIndex(choices) : "Friction" : 0 : "Friction index of the cube" = [ 0 : "Frictionless" @@ -13,7 +13,7 @@ 3 : "Sticky" 4 : "Very sticky" ] - ContraptionElasticityIndex(integer) : "Elasticity" : 0 + ContraptionElasticityIndex[engine](integer) : "Elasticity" : 0 ContraptionElasticityIndex(choices) : "Elasticity" : 0 : "Elasticity index of the cube" = [ 0 : "No rebound" From 9112f3a0999cc48bc1b57859df8dea20d9a174b4 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Tue, 8 Aug 2023 02:02:58 -0700 Subject: [PATCH 197/243] Fix incorrect uses of complete tag --- fgd/point/logic/logic_playerproxy.fgd | 2 +- fgd/worldspawn.fgd | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/point/logic/logic_playerproxy.fgd b/fgd/point/logic/logic_playerproxy.fgd index f1b8cd874..0e4172791 100644 --- a/fgd/point/logic/logic_playerproxy.fgd +++ b/fgd/point/logic/logic_playerproxy.fgd @@ -81,7 +81,7 @@ output OnPrimaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the primary portal." output OnSecondaryPortalPlaced[P2](void) : "Fired when a Portal player successfully places the secondary portal." // Only responds to the old unused +coop_ping command, not the normal pings - output OnCoopPing[P2, +complete](void) : "Fired in response to the unused +coop_ping command." + output OnCoopPing[+P2, +complete](void) : "Fired in response to the unused +coop_ping command." output OnDuck[since_P2](void) : "Fired when a player starts to duck." output OnUnDuck[since_P2](void) : "Fired when a player releases the duck button." output OnJump[since_P2](void) : "Fired when a player jumps." diff --git a/fgd/worldspawn.fgd b/fgd/worldspawn.fgd index 0a273a1cc..ca86e0853 100644 --- a/fgd/worldspawn.fgd +++ b/fgd/worldspawn.fgd @@ -29,8 +29,8 @@ newunit[engine](boolean) : "New Level Unit" : 0 // Portal 2 force enables this - // TODO: we can probably hide it in multiplayer games too - newunit[!P2, complete](choices) : "New Level Unit" : 0 : "Used to clear out savegame data of previous levels to keep the savegame size as small as possible. " + + // TODO: we can probably hide it in multiplayer-only games too + newunit[!P2](choices) : "New Level Unit" : 0 : "Used to clear out savegame data of previous levels to keep the savegame size as small as possible. " + "Only set it to Yes if the player cannot return to any previous levels." = [ 0: "No, keep current" From 19bafc542f22a4575cbe5f622e50078aaffb139b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sat, 19 Aug 2023 17:45:51 +1000 Subject: [PATCH 198/243] Implement #234: Add offset-type for comp_kv_setter and comp_adv_output --- fgd/point/comp/comp_adv_output.fgd | 5 +++++ fgd/point/comp/comp_kv_setter.fgd | 1 + transforms/comp_adv_output.py | 6 +++++- transforms/comp_kv_setter.py | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fgd/point/comp/comp_adv_output.fgd b/fgd/point/comp/comp_adv_output.fgd index 7f64d452f..c24cd3be7 100644 --- a/fgd/point/comp/comp_adv_output.fgd +++ b/fgd/point/comp/comp_adv_output.fgd @@ -25,16 +25,21 @@ params_global1(target_destination) : "Parameter 1 - String" : : "A value which will replace {1} in the parameter." params_local1(target_destination) : "Parameter 1 - Ent Name" : : "If set, this is a fixed up entity name which will be used in the parameter, replacing {1}." + params_pos1(vector) : "Parameter 1 - Position" : : "If set, this is a XYZ position which will be used in the parameter, replacing {1}. This will be offset by instancing." params_global2(target_destination) : "Parameter 2 - String" : : "A value which will replace {2} in the parameter." params_local2(target_destination) : "Parameter 2 - Ent Name" : : "If set, this is a fixed up entity name which will be used in the parameter, replacing {1}." + params_pos2(vector) : "Parameter 2 - Position" : : "If set, this is a XYZ position which will be used in the parameter, replacing {2}. This will be offset by instancing." params_global3(target_destination) : "Parameter 3 - String" : : "A value which will replace {3} in the parameter." params_local3(target_destination) : "Parameter 3 - Ent Name" : : "If set, this is a fixed up entity name which will be used in the parameter, replacing {3}." + params_pos3(vector) : "Parameter 3 - Position" : : "If set, this is a XYZ position which will be used in the parameter, replacing {3}. This will be offset by instancing." params_global4(target_destination) : "Parameter 4 - Ent Name" : : "A value which will replace {4} in the parameter." params_local4(target_destination) : "Parameter 4 - Ent Name" : : "If set, this is a fixed up entity name which will be used in the parameter, replacing {4}." + params_pos4(vector) : "Parameter 4 - Position" : : "If set, this is a XYZ position which will be used in the parameter, replacing {4}. This will be offset by instancing." params_global5(target_destination) : "Parameter 5 - String" : : "A value which will replace {5} in the parameter." params_local5(target_destination) : "Parameter 5 - Ent Name" : : "If set, this is a fixed up entity name which will be used in the parameter, replacing {5}." + params_pos5(vector) : "Parameter 5 - Position" : : "If set, this is a XYZ position which will be used in the parameter, replacing {5}. This will be offset by instancing." ] diff --git a/fgd/point/comp/comp_kv_setter.fgd b/fgd/point/comp/comp_kv_setter.fgd index 2ca2be928..a03a07863 100644 --- a/fgd/point/comp/comp_kv_setter.fgd +++ b/fgd/point/comp/comp_kv_setter.fgd @@ -17,6 +17,7 @@ kv_value_global(string) : "Value - String" : : "The value to apply." kv_value_local(target_destination) : "Value - Ent Name" : : "If set, use this fixed-up entity name." + kv_value_pos(vector) : "Value - Position" : : "If set, overrides the regular value. This will be offset by instancing." invert(boolean) : "Invert Value" : 0 : "If enabled, invert the value so 0 and 1 are swapped." rotate(boolean) : "Rotate Value" : 0 : "If enabled, treat the value as a vector and rotate it by the angles set on this entity first." diff --git a/transforms/comp_adv_output.py b/transforms/comp_adv_output.py index 1ce893e82..c7b9f93d8 100644 --- a/transforms/comp_adv_output.py +++ b/transforms/comp_adv_output.py @@ -59,7 +59,11 @@ def advanced_output(ctx: Context) -> None: param_args: List[str] = [] for ind in itertools.count(1): - val = adv_out[f'params_local{ind}'] or adv_out[f'params_global{ind}'] + val = ( + adv_out[f'params_pos{ind}'] + or adv_out[f'params_local{ind}'] + or adv_out[f'params_global{ind}'] + ) if not val: break param_args.append(val) diff --git a/transforms/comp_kv_setter.py b/transforms/comp_kv_setter.py index 922caa6e2..679be83ac 100644 --- a/transforms/comp_kv_setter.py +++ b/transforms/comp_kv_setter.py @@ -34,7 +34,7 @@ def kv_setter(ctx: Context) -> None: kv_name = setter['kv_name'] # Use fixup name if actually set. - kv_value = setter['kv_value_local'] or setter['kv_value_global'] + kv_value = setter['kv_value_pos'] or setter['kv_value_local'] or setter['kv_value_global'] if conv_bool(setter['invert']): kv_value = '0' if conv_bool(kv_value) else '1' From 9cf6bd723f33a96da90ea15d08c8d2adc25771df Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 23 Aug 2023 17:23:03 +1000 Subject: [PATCH 199/243] Bump srctools version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d312e3298..664bb514f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs >= 21.4.0 typing_extensions >= 4.2.0 -srctools >= 2.3.13 +srctools >= 2.3.14 trio >= 0.20.0 trio-typing >= 0.7.0 pyinstaller >= 5.12.0 From 5d38e5663c8c5cdc4764f6d02b6a03c55792281a Mon Sep 17 00:00:00 2001 From: asd417 Date: Tue, 29 Aug 2023 12:06:20 +0900 Subject: [PATCH 200/243] comp_vactube_bezier implementation --- fgd/point/comp/comp_vactube_beizer.fgd | 48 ++++++++++ transforms/geocable.py | 128 ++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 fgd/point/comp/comp_vactube_beizer.fgd diff --git a/fgd/point/comp/comp_vactube_beizer.fgd b/fgd/point/comp/comp_vactube_beizer.fgd new file mode 100644 index 000000000..91f879e79 --- /dev/null +++ b/fgd/point/comp/comp_vactube_beizer.fgd @@ -0,0 +1,48 @@ +@MoveClass base(StaticTargetname, Angles) + sphere(radius) + animator() keyframe() + iconsprite("editor/comp_prop_rope") + appliesto(srctools) += comp_vactube_bezier: "Constructs a custom vactube model, using a set of path points as bezier keyframes. Config values are only read from the starting point" + [ + nextkey(target_destination) : "Next Rope" : : "Name of the next bezier node." + opaque(boolean) : "Opaque Tube" : 0 : "Whether the tube should be glass, or the black opaque version." + + skin[engine](integer) : "Act as Junction" : 0 + skin(choices) : "Mode" : 0 : "Whether this will link to comp_vactube_junctions, or just be a static prop." = + [ + 0: "Static Prop" + 1: "Vactube Junction" + ] + segments(integer) : "Segments" : 2 : "Total number of nodes per curve." + collisions(boolean) : "Enable Collisions" : 1 : "Should a collision mesh should be generated?" + + // A selection of prop_static's keyvalues, excluding some that are rather irrelevant. + linedivider_staticprop[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" + + renderamt[since_L4D](integer) : "Alpha" : 255 : "Alpha of the fade, where 0 = fully transparent and 255 = fully opaque." + rendercolor[since_L4D](color255) : "Color (R G B)" : "255 255 255" + + screenspacefade[until_ASW](boolean) : "Screen Space Fade" : 0 : "The method by which the fading distance should be determined. If 'No', the fade distances is the distance from the player's view to the object, in inches. " + + "If 'Yes', the fade distance is the size of the object onscreen, in pixels." + fademindist(float) : "Start Fade Dist/Pixels" : -1 : "Distance at which the prop starts to fade (<0 = use fademaxdist). If 'Screen Space Fade' is selected, this represents the number of pixels wide covered by the prop when it starts to fade." + fademaxdist(float) : "End Fade Dist/Pixels" : 0 : "Maximum distance at which the prop is visible (0 = don't fade out). If 'Screen Space Fade' is selected, this represents the *minimum* number of pixels wide covered by the prop when it fades." + fadescale(float) : "Fade Scale" : 1 : "If you specify a fade in the worldspawn, " + + "or if the engine is running under dx7 [hl2/ep1/portal] or dx8 [ep2/tf], " + + "then the engine will forcibly fade out props even if fademindist/fademaxdist " + + "isn't specified. " + + "This scale factor gives you some control over the fade. " + + "Using 0 here turns off the forcible fades. " + + "Numbers smaller than 1 cause the prop to fade out at further distances, " + + "and greater than 1 cause it to fade out at closer distances." + + disableshadows(boolean) : "Disable Shadows" : 0 + disableselfshadowing(boolean): "Disable Self-Shadowing": 0 + disablevertexlighting(boolean) : "Disable Vertex lighting" : 0 : "Disable per-vertex lighting on this prop." + + drawinfastreflection[since_L4D](boolean) : "Render in Fast Reflections" : 0 : "If enabled, causes this entity/prop to to render in fast water reflections (i.e. when a water material specifies $reflectonlymarkedentities) and in the world impostor pass." + enablelightbounce[since_CSGO](boolean) : "Enable Bounced Lighting" : 0 : "Whether VRAD should create indirect lighting from this prop." + + movespeed(integer) readonly: "Speed (unused)" : 1 : "This needs to be greater than zero to show the preview lines." + positioninterpolator(integer) readonly : "Rope Mode" : 1 : "Needs to be set to '1' to produce the spline curve preview." + ] diff --git a/transforms/geocable.py b/transforms/geocable.py index babba5381..a42b6e8b2 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -62,6 +62,7 @@ class InterpType(Enum): STRAIGHT = 0 CATMULL_ROM = 1 ROPE = 2 + BEZIER_QUAD = 3 class RopeType(Enum): @@ -218,6 +219,31 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) v_scale = 1.0 flip_uv = False seg_props = VAC_SEG_CONF_SET + elif ent['classname'].casefold() == 'comp_vactube_bezier': + # More restricted config, most are preset. + skin = conv_int(ent['skin']) + rope_type = RopeType.VAC_FUNCTIONAL if skin == 1 else RopeType.VAC_PROP + if conv_bool(ent['opaque']): + material = 'models/props_backstage/vacum_pipe_opaque' + else: + material = 'models/props_backstage/vacum_pipe_glass' + + # Side counts are the same as the original models. + side_count = 24 + if conv_bool(ent['collisions']): + coll_side_count = 12 + coll_segments = math.ceil(segments / 2) + else: + coll_side_count = 0 + coll_segments = -1 + radius = VAC_RADIUS + slack = 0 # Unused. + interp_type = InterpType.BEZIER_QUAD + u_min = 0.0 + u_max = 1.0 + v_scale = 1.0 + flip_uv = False + seg_props = VAC_SEG_CONF_SET else: rope_type = RopeType.ROPE # There's not really a vanilla material we can use for cables. @@ -411,6 +437,8 @@ async def build_rope( # All or nothing. is_vactube = next(iter(nodes)).config.is_vactube vac_points: List[List[Vec]] = [] + + # this needs to be separated to separate beams and glass if is_vactube: mesh.triangles.extend(generate_vac_beams(nodes, bone, vac_points)) @@ -578,7 +606,6 @@ def interpolate_catmull_rom(node1: Node, node2: Node, seg_count: int) -> List[No )) return points - def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: """Compute the move_rope style hanging points. @@ -645,19 +672,111 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: for point in moveable ] +def compute_binominal(n, k): + # n and k must be integers + value = 1.0 + for i in range(1,k+1): + value = value * ((n + 1 - i) / i) + if n == k: + value = 1 + return value + + +def interpolate_bezier_quad(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: + """Interpolate a quadratic bezier curve, for better 90 degrees turn.""" + # reference: + # https://medium.com/geekculture/2d-and-3d-b%C3%A9zier-curves-in-c-499093ef45a9 + #xX yY zZ are all arrays of x y z points + + + points: list[Node] = [] + #int n = xX.size() - 1; + increment = 1/(first_node.config.segments) + # Only the segment count set in the first spline object counts + size = curve_segment_count + # We do not calculate the first and last node of this curve + curve_x = [] + curve_y = [] + curve_z = [] + curnode = first_node + while curnode != None: + curve_x.append(curnode.pos.x) + curve_y.append(curnode.pos.y) + curve_z.append(curnode.pos.z) + curnode = curnode.next + + for l in range(1,first_node.config.segments-1): + t = l * increment + x = de_casteljau(t,curve_x) + y = de_casteljau(t,curve_y) + z = de_casteljau(t,curve_z) + n = Node(Vec(x,y,z), first_node.config, first_node.radius) + points.append(n) + + print("Bezier Curve Successfully made: ",points) + return points + +def de_casteljau(t, coefs): + beta = [c for c in coefs] # values in this list are overridden + n = len(beta) + for j in range(1, n): + for k in range(n - j): + beta[k] = beta[k] * (1 - t) + beta[k + 1] * t + return beta[0] + +def find_all_connected_exclude_firstlast(node: Node): + node_list : List[Node] = [node] + cur_back = node + cur_forward = node + + while cur_back.prev != None: + cur_back = cur_back.prev + node_list.append(cur_back) + assert cur_back.prev != node, 'Circular Node Detected' + + while cur_forward.next != None: + cur_forward = cur_forward.next + node_list.append(cur_forward) + assert cur_forward.next != node, 'Circular Node Detected' + + if cur_back.prev == None: + node_list.remove(cur_back) + if cur_forward.next == None: + node_list.remove(cur_forward) + return node_list,cur_back,cur_forward def interpolate_all(nodes: Set[Node]) -> None: """Produce nodes in-between each user-made node.""" # Create the nodes and put them in a seperate list, then add them # to the actual nodes list second. This way sections that have been interpolated # don't affect the interpolation of neighbouring sections. + + # Set is not subscribable. Set includes all nodes in the whole map + #if nodes[0].config.interp.name.casefold() == "bezier_quad": + #points = compute_Nvertex_bezier_curve(nodes) + #nodes.update(points) + + seen_bezier_nodes: Set[Node] = set() + seen_bezier_nodes_ignore: Set[Node] = set() segments: List[List[Node]] = [] for node1 in nodes: if node1.next is None or node1.config.segments <= 0: continue - node2 = node1.next + interp_type = node1.config.interp + + if interp_type.name.casefold() == "bezier_quad": + if node1 in seen_bezier_nodes or node1 in seen_bezier_nodes_ignore: + continue + b_curve, first, last = find_all_connected_exclude_firstlast(node1) + print(first,"Curve Keyframes: ", b_curve,last) + seen_bezier_nodes.update(b_curve) + seen_bezier_nodes_ignore.update([first,last]) + node1 = first + node2 = last + else: + node2 = node1.next func = globals()['interpolate_' + interp_type.name.casefold()] points = func(node1, node2, node1.config.segments) @@ -668,6 +787,10 @@ def interpolate_all(nodes: Set[Node]) -> None: points[-1].next = node2 segments.append(points) + for removenode in seen_bezier_nodes: + print("Removing Node ",removenode) + nodes.remove(removenode) + for points in segments: nodes.update(points) points[0].prev.next = points[0] @@ -1196,6 +1319,7 @@ async def comp_prop_rope(ctx: Context) -> None: ctx.vmf.by_class['comp_prop_rope'], ctx.vmf.by_class['comp_prop_cable'], ctx.vmf.by_class['comp_vactube_spline'], + ctx.vmf.by_class['comp_vactube_bezier'], ): ent.remove() conf = Config.parse(ent, name_to_segprops_set) From c662aceb29e3cb09b00bec5db43e630b9f76ac87 Mon Sep 17 00:00:00 2001 From: asd417 Date: Tue, 29 Aug 2023 12:15:44 +0900 Subject: [PATCH 201/243] Refactorization + using LOGGER instead of print --- transforms/geocable.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index a42b6e8b2..815cb57c9 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -683,18 +683,12 @@ def compute_binominal(n, k): def interpolate_bezier_quad(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: - """Interpolate a quadratic bezier curve, for better 90 degrees turn.""" + """Interpolate a bezier curve, for better 90 degrees turn.""" # reference: - # https://medium.com/geekculture/2d-and-3d-b%C3%A9zier-curves-in-c-499093ef45a9 - #xX yY zZ are all arrays of x y z points - - + # https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm points: list[Node] = [] - #int n = xX.size() - 1; - increment = 1/(first_node.config.segments) + increment = 1/(curve_segment_count) # Only the segment count set in the first spline object counts - size = curve_segment_count - # We do not calculate the first and last node of this curve curve_x = [] curve_y = [] curve_z = [] @@ -704,16 +698,15 @@ def interpolate_bezier_quad(first_node:Node,last_node:Node,curve_segment_count:i curve_y.append(curnode.pos.y) curve_z.append(curnode.pos.z) curnode = curnode.next - - for l in range(1,first_node.config.segments-1): + # We do not calculate the first and last node of this curve + for l in range(1,curve_segment_count-1): t = l * increment x = de_casteljau(t,curve_x) y = de_casteljau(t,curve_y) z = de_casteljau(t,curve_z) n = Node(Vec(x,y,z), first_node.config, first_node.radius) points.append(n) - - print("Bezier Curve Successfully made: ",points) + LOGGER.debug("Bezier Nodes Generated: ",points) return points def de_casteljau(t, coefs): @@ -750,11 +743,6 @@ def interpolate_all(nodes: Set[Node]) -> None: # Create the nodes and put them in a seperate list, then add them # to the actual nodes list second. This way sections that have been interpolated # don't affect the interpolation of neighbouring sections. - - # Set is not subscribable. Set includes all nodes in the whole map - #if nodes[0].config.interp.name.casefold() == "bezier_quad": - #points = compute_Nvertex_bezier_curve(nodes) - #nodes.update(points) seen_bezier_nodes: Set[Node] = set() seen_bezier_nodes_ignore: Set[Node] = set() @@ -770,7 +758,7 @@ def interpolate_all(nodes: Set[Node]) -> None: if node1 in seen_bezier_nodes or node1 in seen_bezier_nodes_ignore: continue b_curve, first, last = find_all_connected_exclude_firstlast(node1) - print(first,"Curve Keyframes: ", b_curve,last) + LOGGER.debug("Curve Keyframes: ",first,b_curve,last) seen_bezier_nodes.update(b_curve) seen_bezier_nodes_ignore.update([first,last]) node1 = first @@ -788,7 +776,7 @@ def interpolate_all(nodes: Set[Node]) -> None: segments.append(points) for removenode in seen_bezier_nodes: - print("Removing Node ",removenode) + LOGGER.debug("Removing Bezier Keyframe Node ",removenode) nodes.remove(removenode) for points in segments: From f61946c1fda832c3340407b88a0d0fd8823c2c96 Mon Sep 17 00:00:00 2001 From: asd417 Date: Tue, 29 Aug 2023 17:01:42 +0900 Subject: [PATCH 202/243] bezier option as config kv. Added glass separation --- fgd/point/comp/comp_vactube_spline.fgd | 4 +- transforms/geocable.py | 189 +++++++++++++++---------- 2 files changed, 115 insertions(+), 78 deletions(-) diff --git a/fgd/point/comp/comp_vactube_spline.fgd b/fgd/point/comp/comp_vactube_spline.fgd index e585b15fb..c324c6775 100644 --- a/fgd/point/comp/comp_vactube_spline.fgd +++ b/fgd/point/comp/comp_vactube_spline.fgd @@ -15,7 +15,9 @@ 1: "Vactube Junction" ] segments(integer) : "Segments" : 2 : "Number of nodes to generate for this. Higher values make smoother tubes, but produce more faces." - collisions(boolean) : "Enable Collisions" : 1 : "Sholuld a collision mesh should be generated?" + collisions(boolean) : "Enable Collisions" : 1 : "Should a collision mesh should be generated?" + usebezier(boolean) : "Use Bezier Curve Generation" : 0 : "Should this tube be generated as a bezier curve?" + vac_separateglass(boolean) : "Separate Glass and Frame" : 0 : "Separating glass and frame can help with transparency sorting issue" // A selection of prop_static's keyvalues, excluding some that are rather irrelevant. linedivider_staticprop[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" diff --git a/transforms/geocable.py b/transforms/geocable.py index 815cb57c9..12e5a42ee 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -13,6 +13,7 @@ import attrs import trio +from dataclasses import dataclass from srctools import ( logger, conv_int, conv_float, conv_bool, @@ -56,14 +57,21 @@ }} ''' - class InterpType(Enum): """Type of interpolation to use.""" STRAIGHT = 0 CATMULL_ROM = 1 ROPE = 2 - BEZIER_QUAD = 3 + BEZIER = 3 + +class VactubeGenType(Enum): + COMBINE = 0 + SEPARATE = 1 +class VactubeGenPartType(Enum): + ALL = 0 + GLASS = 1 + FRAME = 2 class RopeType(Enum): """Type of rope, for indicating special functionality.""" @@ -170,6 +178,7 @@ class Config: prop_fade_min_dist: float prop_fade_max_dist: float prop_fade_scale: float + vac_separate_glass: bool @staticmethod def _parse_min(ent: Entity, keyvalue: str, minimum: Number, message: str) -> Number: @@ -203,30 +212,16 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) else: material = 'models/props_backstage/vacum_pipe_glass' - # Side counts are the same as the original models. - side_count = 24 - if conv_bool(ent['collisions']): - coll_side_count = 12 - coll_segments = math.ceil(segments / 2) + if conv_bool(ent['usebezier']): + interp_type = InterpType.BEZIER else: - coll_side_count = 0 - coll_segments = -1 - radius = VAC_RADIUS - slack = 0 # Unused. - interp_type = InterpType.CATMULL_ROM - u_min = 0.0 - u_max = 1.0 - v_scale = 1.0 - flip_uv = False - seg_props = VAC_SEG_CONF_SET - elif ent['classname'].casefold() == 'comp_vactube_bezier': - # More restricted config, most are preset. - skin = conv_int(ent['skin']) - rope_type = RopeType.VAC_FUNCTIONAL if skin == 1 else RopeType.VAC_PROP - if conv_bool(ent['opaque']): - material = 'models/props_backstage/vacum_pipe_opaque' + interp_type = InterpType.CATMULL_ROM + + + if conv_bool(ent['vac_separateglass']): + vac_separate_glass = VactubeGenType.SEPARATE else: - material = 'models/props_backstage/vacum_pipe_glass' + vac_separate_glass = VactubeGenType.COMBINE # Side counts are the same as the original models. side_count = 24 @@ -238,12 +233,12 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) coll_segments = -1 radius = VAC_RADIUS slack = 0 # Unused. - interp_type = InterpType.BEZIER_QUAD u_min = 0.0 u_max = 1.0 v_scale = 1.0 flip_uv = False seg_props = VAC_SEG_CONF_SET + else: rope_type = RopeType.ROPE # There's not really a vanilla material we can use for cables. @@ -317,6 +312,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) conv_float(ent['fademindist'], -1.0), conv_float(ent['fademaxdist'], 0.0), conv_float(ent['fadescale'], 0.0), + vac_separate_glass ) def coll(self) -> Optional['Config']: @@ -411,14 +407,14 @@ def __repr__(self) -> str: async def build_rope( - nodes_and_conn: Tuple[FrozenSet[NodeEnt], FrozenSet[Tuple[NodeID, NodeID]]], + nodes_and_conn: Tuple[FrozenSet[NodeEnt], FrozenSet[Tuple[NodeID, NodeID]], VactubeGenPartType], temp_folder: Path, mdl_name: str, args: Tuple[Vec, FileSystem], ) -> Tuple[Vec, List[Tuple[Vec, float, Vec, float]], List[SegProp], List[List[Vec]]]: - """Construct the geometry for a rope.""" + """Construct the geometry for a rope. nodes_and_conn is saved into file system to check if the model needs to be recompiled. args is for information that can be lost after the compile""" LOGGER.info('Building rope {}', mdl_name) - ents, connections = nodes_and_conn + ents, connections, vacgentype = nodes_and_conn offset, fsys = args mesh = Mesh.blank('root') @@ -431,18 +427,24 @@ async def build_rope( compute_orients(nodes) compute_verts(nodes, bone, is_coll=False) - mesh.triangles.extend(generate_straights(nodes)) + # compile_rope uses VactubeGenPartType.ALL for all other rope generation + # skip this when only generating frame + if vacgentype != VactubeGenPartType.FRAME: + mesh.triangles.extend(generate_straights(nodes)) generate_caps(nodes, mesh, is_coll=False) # All or nothing. is_vactube = next(iter(nodes)).config.is_vactube vac_points: List[List[Vec]] = [] - - # this needs to be separated to separate beams and glass - if is_vactube: + if is_vactube and (vacgentype == VactubeGenPartType.FRAME or vacgentype == VactubeGenPartType.ALL): mesh.triangles.extend(generate_vac_beams(nodes, bone, vac_points)) - seg_props = list(place_seg_props(nodes, fsys, mesh)) + # appends rings to the tube model + # skip this when only generating glass + if vacgentype != VactubeGenPartType.GLASS: + seg_props = list(place_seg_props(nodes, fsys, mesh)) + else: + seg_props = [] if coll_nodes: # Generate the collision mesh. @@ -474,9 +476,13 @@ async def build_rope( coll_mesh.export(fb) with (temp_folder / 'model.qc').open('w') as f: - # Desolation needs this hint. - if is_vactube and hasattr(Mesh, 'NEED_TRANSLUCENT_MOSTLYOPAQUE'): - f.write('$mostlyopaque\n') + if is_vactube: + # Desolation needs this hint. + if hasattr(Mesh, 'NEED_TRANSLUCENT_MOSTLYOPAQUE') and vacgentype is VactubeGenPartType.ALL: + f.write('$mostlyopaque\n') + elif vacgentype is VactubeGenPartType.FRAME: + f.write('$opaque\n') + f.write(QC_TEMPLATE.format(path=mdl_name, light_origin=light_origin)) if coll_nodes: f.write(QC_TEMPLATE_PHYS.format(count=sum(node.next is not None for node in coll_nodes))) @@ -672,17 +678,7 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: for point in moveable ] -def compute_binominal(n, k): - # n and k must be integers - value = 1.0 - for i in range(1,k+1): - value = value * ((n + 1 - i) / i) - if n == k: - value = 1 - return value - - -def interpolate_bezier_quad(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: +def interpolate_bezier(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: """Interpolate a bezier curve, for better 90 degrees turn.""" # reference: # https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm @@ -706,6 +702,9 @@ def interpolate_bezier_quad(first_node:Node,last_node:Node,curve_segment_count:i z = de_casteljau(t,curve_z) n = Node(Vec(x,y,z), first_node.config, first_node.radius) points.append(n) + + last_node.config = first_node.config + LOGGER.debug("Bezier Nodes Generated: ",points) return points @@ -731,7 +730,8 @@ def find_all_connected_exclude_firstlast(node: Node): cur_forward = cur_forward.next node_list.append(cur_forward) assert cur_forward.next != node, 'Circular Node Detected' - + + if cur_back.prev == None: node_list.remove(cur_back) if cur_forward.next == None: @@ -754,7 +754,7 @@ def interpolate_all(nodes: Set[Node]) -> None: interp_type = node1.config.interp - if interp_type.name.casefold() == "bezier_quad": + if interp_type is InterpType.BEZIER: if node1 in seen_bezier_nodes or node1 in seen_bezier_nodes_ignore: continue b_curve, first, last = find_all_connected_exclude_firstlast(node1) @@ -1213,11 +1213,44 @@ async def compile_rope( if node.config.coll_side_count >= 3: has_coll = True - model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( - (frozenset(local_nodes), frozenset(connections)), - build_rope, - (center, ctx.pack.fsys), - ) + class ModelContainer: + def __init__(self,mn,lo,cd,sp): + self.model_name = mn + self.light_origin = lo + self.coll_data = cd + self.seg_props = sp + + modellist : List[ModelContainer] = [] + # Separate glass config + # Call get_model twice to get models for frame and glass combined + + # This code need refactorization + if node.config.vac_separate_glass == VactubeGenType.SEPARATE: + + # generate glass only + model_name, (light_origin, coll_data, seg_props, _) = await compiler.get_model( + (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.GLASS), + build_rope, + (center, ctx.pack.fsys), + ) + #hmmmmm + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) + + # generate frame only + # frame generation code also generates the vac_points + model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( + (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.FRAME), + build_rope, + (center, ctx.pack.fsys), + ) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) + else: + model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( + (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.ALL), + build_rope, + (center, ctx.pack.fsys), + ) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) if vac_points and vac_node_mod is not None: for track in vac_points: @@ -1235,39 +1268,41 @@ async def compile_rope( if conf.prop_no_self_shadow: flags |= StaticPropFlags.NO_SELF_SHADOWING - leafs = compute_visleafs(coll_data, ctx.bsp.vis_tree()) - ctx.bsp.props.append(StaticProp( - model=model_name, - origin=center, - angles=Angle(0, 270, 0), - scaling=1.0, - visleafs=leafs, - solidity=6 if has_coll else 0, - flags=flags, - tint=Vec(conf.prop_rendercolor), - renderfx=conf.prop_renderalpha, - lighting=center + light_origin, - min_fade=conf.prop_fade_min_dist, - max_fade=conf.prop_fade_max_dist, - fade_scale=conf.prop_fade_scale, - )) - for seg_prop in seg_props: + for m in modellist: + leafs = compute_visleafs(m.coll_data, ctx.bsp.vis_tree()) ctx.bsp.props.append(StaticProp( - model=seg_prop.model, - origin=center + seg_prop.offset, - angles=(seg_prop.orient @ Matrix.from_yaw(270)).to_angle(), + model=m.model_name, + origin=center, + angles=Angle(0, 270, 0), scaling=1.0, - visleafs=leafs, # TODO: compute individual leafs here? - solidity=6, + visleafs=leafs, + solidity=6 if has_coll else 0, flags=flags, tint=Vec(conf.prop_rendercolor), renderfx=conf.prop_renderalpha, - lighting=center + seg_prop.offset, + lighting=center + m.light_origin, min_fade=conf.prop_fade_min_dist, max_fade=conf.prop_fade_max_dist, fade_scale=conf.prop_fade_scale, )) + for seg_prop in m.seg_props: + ctx.bsp.props.append(StaticProp( + model=seg_prop.model, + origin=center + seg_prop.offset, + angles=(seg_prop.orient @ Matrix.from_yaw(270)).to_angle(), + scaling=1.0, + visleafs=leafs, # TODO: compute individual leafs here? + solidity=6, + flags=flags, + tint=Vec(conf.prop_rendercolor), + renderfx=conf.prop_renderalpha, + lighting=center + seg_prop.offset, + min_fade=conf.prop_fade_min_dist, + max_fade=conf.prop_fade_max_dist, + fade_scale=conf.prop_fade_scale, + )) + @trans('Model Ropes', priority=-10) # Needs to be before vactubes. async def comp_prop_rope(ctx: Context) -> None: From 2b98b31db76b17acfcbeeda4ae8bf82c7d49e9d0 Mon Sep 17 00:00:00 2001 From: asd417 Date: Tue, 29 Aug 2023 22:12:05 +0900 Subject: [PATCH 203/243] Removed spline vactube glass shadow --- transforms/geocable.py | 59 +++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 12e5a42ee..4683abf63 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -94,6 +94,13 @@ class SegPropOrient(Enum): RAND_YAW = 'rand_yaw' RAND_FULL = 'rand' +class ModelContainer: + def __init__(self,mn,lo,cd,sp,sh): + self.model_name = mn + self.light_origin = lo + self.coll_data = cd + self.seg_props = sp + self.noshadow = sh @attrs.define class RopePhys: @@ -1213,44 +1220,27 @@ async def compile_rope( if node.config.coll_side_count >= 3: has_coll = True - class ModelContainer: - def __init__(self,mn,lo,cd,sp): - self.model_name = mn - self.light_origin = lo - self.coll_data = cd - self.seg_props = sp - modellist : List[ModelContainer] = [] # Separate glass config # Call get_model twice to get models for frame and glass combined - # This code need refactorization - if node.config.vac_separate_glass == VactubeGenType.SEPARATE: + is_sep = node.config.vac_separate_glass == VactubeGenType.SEPARATE + # Generate the whole model, or frame only if separating. + model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( + (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.FRAME if is_sep else VactubeGenPartType.ALL), + build_rope, + (center, ctx.pack.fsys), + ) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, False)) - # generate glass only + if is_sep: + # Generate the glass only model_name, (light_origin, coll_data, seg_props, _) = await compiler.get_model( (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.GLASS), build_rope, (center, ctx.pack.fsys), ) - #hmmmmm - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) - - # generate frame only - # frame generation code also generates the vac_points - model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( - (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.FRAME), - build_rope, - (center, ctx.pack.fsys), - ) - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) - else: - model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( - (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.ALL), - build_rope, - (center, ctx.pack.fsys), - ) - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props)) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, True)) if vac_points and vac_node_mod is not None: for track in vac_points: @@ -1261,14 +1251,19 @@ def __init__(self,mn,lo,cd,sp): flags = StaticPropFlags.NONE if conf.prop_light_bounce: flags |= StaticPropFlags.BOUNCED_LIGHTING - if conf.prop_no_shadows: - flags |= StaticPropFlags.NO_SHADOW if conf.prop_no_vert_light: flags |= StaticPropFlags.NO_PER_VERTEX_LIGHTING if conf.prop_no_self_shadow: flags |= StaticPropFlags.NO_SELF_SHADOWING + # separate model set needs both flags + if not is_sep and conf.prop_no_shadows: + flags |= StaticPropFlags.NO_SHADOW + + # is_sep is only true for separate vactube models. + # m.noshadow is only true for the glass model for m in modellist: + new_flags = flags | StaticPropFlags.NO_SHADOW if m.noshadow else flags leafs = compute_visleafs(m.coll_data, ctx.bsp.vis_tree()) ctx.bsp.props.append(StaticProp( model=m.model_name, @@ -1277,7 +1272,7 @@ def __init__(self,mn,lo,cd,sp): scaling=1.0, visleafs=leafs, solidity=6 if has_coll else 0, - flags=flags, + flags=new_flags, tint=Vec(conf.prop_rendercolor), renderfx=conf.prop_renderalpha, lighting=center + m.light_origin, @@ -1294,7 +1289,7 @@ def __init__(self,mn,lo,cd,sp): scaling=1.0, visleafs=leafs, # TODO: compute individual leafs here? solidity=6, - flags=flags, + flags=new_flags, tint=Vec(conf.prop_rendercolor), renderfx=conf.prop_renderalpha, lighting=center + seg_prop.offset, From 3cd85e0170d551022bd1a7b7d49c662aecd54bff Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:05:00 +1000 Subject: [PATCH 204/243] Remove unused imports --- transforms/geocable.py | 1 - 1 file changed, 1 deletion(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 4683abf63..5e55d87fc 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -13,7 +13,6 @@ import attrs import trio -from dataclasses import dataclass from srctools import ( logger, conv_int, conv_float, conv_bool, From b2f70b9d75672085496f74f734abcc037c503465 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:05:28 +1000 Subject: [PATCH 205/243] Use attrs and define types for ModelContainer --- transforms/geocable.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 5e55d87fc..17172b061 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -93,14 +93,6 @@ class SegPropOrient(Enum): RAND_YAW = 'rand_yaw' RAND_FULL = 'rand' -class ModelContainer: - def __init__(self,mn,lo,cd,sp,sh): - self.model_name = mn - self.light_origin = lo - self.coll_data = cd - self.seg_props = sp - self.noshadow = sh - @attrs.define class RopePhys: """Holds the data for move_rope simulation.""" @@ -148,7 +140,8 @@ def __hash__(self) -> int: VAC_RADIUS = 45.0 VAC_COLL_RADIUS = 52.0 VAC_MAT = 'models/props_backstage/vacum_pipe' - +# Pos/radius pairs defining cylinders, for visleaf computation. +CollData = List[Tuple[Vec, float, Vec, float]] @attrs.define class SegProp: @@ -158,6 +151,16 @@ class SegProp: orient: Matrix +@attrs.define +class ModelContainer: + """Temporary container for static props generated.""" + model_name: str + light_origin: Vec + coll_data: CollData + seg_props: List[SegProp] + flags: StaticPropFlags + + @attrs.frozen class Config: """Configuration specified in rope entities. This can be shared to reduce duplication.""" @@ -417,7 +420,7 @@ async def build_rope( temp_folder: Path, mdl_name: str, args: Tuple[Vec, FileSystem], -) -> Tuple[Vec, List[Tuple[Vec, float, Vec, float]], List[SegProp], List[List[Vec]]]: +) -> Tuple[Vec, CollData, List[SegProp], List[List[Vec]]]: """Construct the geometry for a rope. nodes_and_conn is saved into file system to check if the model needs to be recompiled. args is for information that can be lost after the compile""" LOGGER.info('Building rope {}', mdl_name) ents, connections, vacgentype = nodes_and_conn @@ -1230,7 +1233,7 @@ async def compile_rope( build_rope, (center, ctx.pack.fsys), ) - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, False)) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, StaticPropFlags.NONE)) if is_sep: # Generate the glass only @@ -1239,7 +1242,7 @@ async def compile_rope( build_rope, (center, ctx.pack.fsys), ) - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, True)) + modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, StaticPropFlags.NO_SHADOW)) if vac_points and vac_node_mod is not None: for track in vac_points: @@ -1259,10 +1262,9 @@ async def compile_rope( if not is_sep and conf.prop_no_shadows: flags |= StaticPropFlags.NO_SHADOW - # is_sep is only true for separate vactube models. - # m.noshadow is only true for the glass model + # is_sep is only true for separate vactube models. for m in modellist: - new_flags = flags | StaticPropFlags.NO_SHADOW if m.noshadow else flags + new_flags = flags | m.flags # For the glass, we force shadows off. leafs = compute_visleafs(m.coll_data, ctx.bsp.vis_tree()) ctx.bsp.props.append(StaticProp( model=m.model_name, From 12540bc4895a47c922b55164d0eb36e119df1941 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:06:37 +1000 Subject: [PATCH 206/243] Allow setting no-shadows even with separate models Somewhat pointless, but it gives the right result. --- transforms/geocable.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 17172b061..acd34de46 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -1257,9 +1257,7 @@ async def compile_rope( flags |= StaticPropFlags.NO_PER_VERTEX_LIGHTING if conf.prop_no_self_shadow: flags |= StaticPropFlags.NO_SELF_SHADOWING - - # separate model set needs both flags - if not is_sep and conf.prop_no_shadows: + if conf.prop_no_shadows: flags |= StaticPropFlags.NO_SHADOW # is_sep is only true for separate vactube models. From 8bc93001d1f1601869fd9981d76606ad9c7b5626 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:08:40 +1000 Subject: [PATCH 207/243] Make dynamic ropes still work. --- transforms/geocable.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index acd34de46..f49ac0d68 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -226,7 +226,6 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) else: interp_type = InterpType.CATMULL_ROM - if conv_bool(ent['vac_separateglass']): vac_separate_glass = VactubeGenType.SEPARATE else: @@ -1197,12 +1196,18 @@ async def compile_rope( """Compile a single rope group.""" for ent in dyn_ents: origin = Vec.from_str(ent['origin']) - dyn_nodes = frozenset({ + dyn_nodes: FrozenSet[NodeEnt] = frozenset({ node.relative_to(origin) for node in nodes }) + if any(node.config.vac_separate_glass for node in nodes): + LOGGER.warning( + "Vactube at {} is requesting separate models, which is not allowed for " + "prop_dynamic. Ignoring.", + origin, + ) model_name, _ = await compiler.get_model( - (dyn_nodes, frozenset(connections)), + (dyn_nodes, frozenset(connections), VactubeGenPartType.ALL), build_rope, (origin, ctx.pack.fsys), ) From 9638134f74f74a411bf8c30b3b7346c84d1617ec Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:24:13 +1000 Subject: [PATCH 208/243] Make this a little clearer --- transforms/geocable.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index f49ac0d68..87db7e292 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -56,6 +56,7 @@ }} ''' + class InterpType(Enum): """Type of interpolation to use.""" STRAIGHT = 0 @@ -63,14 +64,22 @@ class InterpType(Enum): ROPE = 2 BEZIER = 3 + class VactubeGenType(Enum): + """How vactube models should be generated. + + Splitting the glass and frame parts produces better lighting. + """ COMBINE = 0 SEPARATE = 1 + class VactubeGenPartType(Enum): - ALL = 0 - GLASS = 1 - FRAME = 2 + """What part of the vactube model to generate.""" + ALL = 0 # Both glass + frame, or not a vactube. + GLASS = 1 # Glass only + collision. + FRAME = 2 # Frame only. + class RopeType(Enum): """Type of rope, for indicating special functionality.""" @@ -620,6 +629,7 @@ def interpolate_catmull_rom(node1: Node, node2: Node, seg_count: int) -> List[No )) return points + def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: """Compute the move_rope style hanging points. @@ -686,6 +696,7 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: for point in moveable ] + def interpolate_bezier(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: """Interpolate a bezier curve, for better 90 degrees turn.""" # reference: @@ -1227,18 +1238,19 @@ async def compile_rope( if node.config.coll_side_count >= 3: has_coll = True - modellist : List[ModelContainer] = [] - # Separate glass config - # Call get_model twice to get models for frame and glass combined + # All the configs should be the same, so just use the last node in the set. + conf = node.config - is_sep = node.config.vac_separate_glass == VactubeGenType.SEPARATE - # Generate the whole model, or frame only if separating. + # If separate models are enabled, call get_model twice to get models for the frame and + # glass individually. + is_sep = conf.vac_separate_glass == VactubeGenType.SEPARATE + # First do the frame, or everything if we're not separating them. model_name, (light_origin, coll_data, seg_props, vac_points) = await compiler.get_model( (frozenset(local_nodes), frozenset(connections), VactubeGenPartType.FRAME if is_sep else VactubeGenPartType.ALL), build_rope, (center, ctx.pack.fsys), ) - modellist.append(ModelContainer(model_name, light_origin, coll_data, seg_props, StaticPropFlags.NONE)) + modellist = [ModelContainer(model_name, light_origin, coll_data, seg_props, StaticPropFlags.NONE)] if is_sep: # Generate the glass only @@ -1254,7 +1266,6 @@ async def compile_rope( vac_node_mod.SPLINES.append(vac_node_mod.Spline(center, track)) # Compute the flags. Just pick a random node, from above. - conf = node.config flags = StaticPropFlags.NONE if conf.prop_light_bounce: flags |= StaticPropFlags.BOUNCED_LIGHTING @@ -1265,7 +1276,6 @@ async def compile_rope( if conf.prop_no_shadows: flags |= StaticPropFlags.NO_SHADOW - # is_sep is only true for separate vactube models. for m in modellist: new_flags = flags | m.flags # For the glass, we force shadows off. leafs = compute_visleafs(m.coll_data, ctx.bsp.vis_tree()) @@ -1274,7 +1284,7 @@ async def compile_rope( origin=center, angles=Angle(0, 270, 0), scaling=1.0, - visleafs=leafs, + visleafs=leafs, # TODO: compute individual leafs here? solidity=6 if has_coll else 0, flags=new_flags, tint=Vec(conf.prop_rendercolor), @@ -1291,7 +1301,7 @@ async def compile_rope( origin=center + seg_prop.offset, angles=(seg_prop.orient @ Matrix.from_yaw(270)).to_angle(), scaling=1.0, - visleafs=leafs, # TODO: compute individual leafs here? + visleafs=leafs, solidity=6, flags=new_flags, tint=Vec(conf.prop_rendercolor), From 7f8b1bc7256f3b5daec85e1ce6b68a63dc531d35 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:24:34 +1000 Subject: [PATCH 209/243] Fix regular ropes --- transforms/geocable.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 87db7e292..d084a8545 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -196,7 +196,7 @@ class Config: prop_fade_min_dist: float prop_fade_max_dist: float prop_fade_scale: float - vac_separate_glass: bool + vac_separate_glass: VactubeGenType @staticmethod def _parse_min(ent: Entity, keyvalue: str, minimum: Number, message: str) -> Number: @@ -296,6 +296,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) u_min = abs(conv_float(ent['u_min'], 0.0)) u_max = abs(conv_float(ent['u_max'], 1.0)) flip_uv = conv_bool(ent['mat_rotate']) + vac_separate_glass = VactubeGenType.COMBINE try: seg_props = name_to_segprops[ent['bunting'].casefold()] From 0a6bd774507b80095e565f966e84df8153e15dda Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 11:25:24 +1000 Subject: [PATCH 210/243] Use positioninterpolator keyvalue for spline vactubes, make Bezier available for regular ropes This makes everything consistent, and allows adding additional modes easily later --- fgd/point/comp/comp_prop_cable.fgd | 3 ++- fgd/point/comp/comp_prop_rope.fgd | 3 ++- fgd/point/comp/comp_vactube_spline.fgd | 10 +++++++++- transforms/geocable.py | 12 +++++++++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fgd/point/comp/comp_prop_cable.fgd b/fgd/point/comp/comp_prop_cable.fgd index 7461537aa..d6bff4231 100644 --- a/fgd/point/comp/comp_prop_cable.fgd +++ b/fgd/point/comp/comp_prop_cable.fgd @@ -14,11 +14,12 @@ // Valve's interplator keyvalue just happens to provide the right visuals we want. positioninterpolator[engine](integer): "Type": 2 - positioninterpolator(choices) : "Type" : 2 : "How to interpolate the cable. Straight makes it straight. Spline uses a spline curve, which smoothly blends between points (no visuals). Catenary makes it hang down, like the original move_rope." = + positioninterpolator(choices) : "Type" : 2 : "How to interpolate the cable. Straight makes it straight. Spline uses a spline curve, which smoothly blends between points. Catenary makes it hang down, like the original move_rope. Bezier is an alternate smooth curve, but the visual in Hammer will be incorrect." = [ 0: "Straight" 1: "Spline Curve" 2: "Catenary" + 3: "Bezier" ] segments(integer) : "Segments" : 2 : "Number of nodes to generate between each cable. Higher values make smoother cables, but produce more faces." diff --git a/fgd/point/comp/comp_prop_rope.fgd b/fgd/point/comp/comp_prop_rope.fgd index fcbbf8fc6..d12f813ea 100644 --- a/fgd/point/comp/comp_prop_rope.fgd +++ b/fgd/point/comp/comp_prop_rope.fgd @@ -14,11 +14,12 @@ // Valve's interplator keyvalue just happens to provide the right visuals we want. positioninterpolator[engine](integer): "Type": 2 - positioninterpolator(choices) : "Type" : 2 : "How to interpolate the rope. Straight makes it straight. Spline uses a spline curve, which smoothly blends between points (no visuals). Catenary makes it hang down, like the original move_rope." = + positioninterpolator(choices) : "Type" : 2 : "How to interpolate the rope. Straight makes it straight. Spline uses a spline curve, which smoothly blends between points. Catenary makes it hang down, like the original move_rope. Bezier is an alternate smooth curve, but the visual in Hammer will be incorrect." = [ 0: "Straight" 1: "Spline Curve" 2: "Catenary" + 3: "Bezier" ] segments(integer) : "Segments" : 2 : "Number of nodes to generate between each rope. Higher values make smoother ropes, but produce more faces." diff --git a/fgd/point/comp/comp_vactube_spline.fgd b/fgd/point/comp/comp_vactube_spline.fgd index c324c6775..121b19fa9 100644 --- a/fgd/point/comp/comp_vactube_spline.fgd +++ b/fgd/point/comp/comp_vactube_spline.fgd @@ -17,6 +17,15 @@ segments(integer) : "Segments" : 2 : "Number of nodes to generate for this. Higher values make smoother tubes, but produce more faces." collisions(boolean) : "Enable Collisions" : 1 : "Should a collision mesh should be generated?" usebezier(boolean) : "Use Bezier Curve Generation" : 0 : "Should this tube be generated as a bezier curve?" + positioninterpolator[engine](integer): "Type": 1 + positioninterpolator(choices) : "Type" : 1 : "How to interpolate the tube. Spline uses a spline curve, which smoothly blends between points. Bezier is an alternate smooth curve, but the visual in Hammer will be incorrect." = + [ + // These all function, but are kinda pointless. + // 0: "Straight" + 1: "Spline Curve" + // 2: "Catenary" + 3: "Bezier" + ] vac_separateglass(boolean) : "Separate Glass and Frame" : 0 : "Separating glass and frame can help with transparency sorting issue" // A selection of prop_static's keyvalues, excluding some that are rather irrelevant. @@ -46,5 +55,4 @@ enablelightbounce[since_CSGO](boolean) : "Enable Bounced Lighting" : 0 : "Whether VRAD should create indirect lighting from this prop." movespeed(integer) readonly: "Speed (unused)" : 1 : "This needs to be greater than zero to show the preview lines." - positioninterpolator(integer) readonly : "Rope Mode" : 1 : "Needs to be set to '1' to produce the spline curve preview." ] diff --git a/transforms/geocable.py b/transforms/geocable.py index d084a8545..3451c4aee 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -230,9 +230,15 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) else: material = 'models/props_backstage/vacum_pipe_glass' - if conv_bool(ent['usebezier']): - interp_type = InterpType.BEZIER - else: + try: + interp_type = InterpType(int(ent['positioninterpolator', '1'])) + except ValueError: + LOGGER.warning( + 'Unknown interpolation type "{}" ' + 'for vactube at {}!', + ent['interpolationtype'], + ent['origin'], + ) interp_type = InterpType.CATMULL_ROM if conv_bool(ent['vac_separateglass']): From 084b18563c77df70ef3367b8d2dd319881507e07 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Tue, 29 Aug 2023 20:53:37 -0700 Subject: [PATCH 211/243] Add longer description to trigger changelevel flag --- fgd/brush/trigger/trigger_changelevel.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/brush/trigger/trigger_changelevel.fgd b/fgd/brush/trigger/trigger_changelevel.fgd index 7ead83159..6bcf4e3cd 100644 --- a/fgd/brush/trigger/trigger_changelevel.fgd +++ b/fgd/brush/trigger/trigger_changelevel.fgd @@ -13,7 +13,7 @@ spawnflags(flags) = [ 2: "Disable Touch" : 0 - 4: "To Previous Chapter" : 0 + 4: "To Previous Chapter - disable if map was loaded from a new game" : 0 ] From 512e9e3f8b80460164b4b8b8b4185554d53003de Mon Sep 17 00:00:00 2001 From: Kelsey Date: Tue, 29 Aug 2023 21:05:42 -0700 Subject: [PATCH 212/243] Change ModelSkin to Skin on npc_personality_core This creates a bit of weirdness when opening existing maps where ModelSkin will be an invalid keyvalue, but since it still takes priority over Skin that should be fine --- fgd/point/npc/npc_personality_core.fgd | 32 ++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/fgd/point/npc/npc_personality_core.fgd b/fgd/point/npc/npc_personality_core.fgd index e77995ad5..8d54f105e 100644 --- a/fgd/point/npc/npc_personality_core.fgd +++ b/fgd/point/npc/npc_personality_core.fgd @@ -4,15 +4,23 @@ studioprop() = npc_personality_core: "Aperture Science Personality Construct. Animated balls with handles." [ - modelskin[engine](integer) : "Model Skin" : 0 - modelskin(choices) : "Model Skin" : 0 : "If using the 'alt' skin, this sets the eye color -- If not using the alt skin, (0 = broken) (1=normal)" = + // The default core FGD uses the keyvalue ModelSkin to set the skin, which doesn't preview in Hammer + // Using the regular skin keyvalue instead allows it to preview and actually works fine, but only if ModelSkin isn't also set + // This will create some weirdness when opening existing maps where ModelSkin will need to be removed for this keyvalue to function, + // but existing untouched core entities should still keep working fine so I think it's worth the tradeoff + skin[engine](integer) : "Model Skin" : 0 + skin(choices) : "Model Skin" : 0 : "Sets the skin of the core. If using the alternate model, this sets the eye color. If using the normal model, (0=broken) (1=intact). If using a custom model, any skin number can be entered here. " + + "NOTE: Older maps may have the keyvalue ModelSkin set instead, disable SmartEdit and remove it to ensure this value is used." = [ - 0: "Blue Eye / Broken" - 1: "Green Eye / Normal" - 2: "Orange Eye / NA" - 3: "Purple Eye / NA" + 0: "[0] Blue Eye / Broken" + 1: "[1] Green Eye / Intact" + 2: "[2] Orange Eye / NA" + 3: "[3] Purple Eye / NA" ] + // this is still supported by the engine, but shouldn't appear in hammer + ModelSkin[engine](integer) : "Model Skin" : 0 + altmodel[engine](boolean) : "Use Alternate Skins" : 0 altmodel(choices) : "Use Alternate Skins" : 0 : "Use the model with corrupted skins, instead of the skins in the original model. " = [ @@ -25,22 +33,12 @@ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" ] - model[+srctools](choices) : "[HA] Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins. Alternatively, if Override Model is enabled you can type/paste in a custom model path here." = + model[+srctools](choices) : "[HA] Custom/Hammer Model" : "models/npcs/personality_sphere/personality_sphere.mdl" : "Choose the model to show in Hammer. Set to the same as Use Alternate Skins. Alternatively, if Override Model is enabled you can type/paste in a custom model path here." = [ "models/npcs/personality_sphere/personality_sphere.mdl": "Original (Wheatley)" "models/npcs/personality_sphere/personality_sphere_skins.mdl": "Alternate (Corrupt Cores)" ] - skin[engine](integer) : "Skin" : 0 - skin(choices) : "[H] Skin" : 0 : "If using the 'alt' skin, this sets the eye color (in hammer) -- If not using the alt skin, (0 = broken) (1=normal)" = - [ - 0: "Blue Eye / Broken" - 1: "Green Eye / Normal" - 2: "Orange Eye / NA" - 3: "Purple Eye / NA" - ] - - // Inputs input EnableMotion(void) : "Enable physics motion/collision response." input DisableMotion(void) : "Disable physics motion/collision response." From 39dd4355d1e932b1a3d3a9cc3aba097d6cff95d9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 18:28:22 +1000 Subject: [PATCH 213/243] Ensure forward slashes are used in vactube ents. --- transforms/vactubes/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index f71bcdcfb..c1f2b1f77 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -2,7 +2,7 @@ import subprocess from collections import defaultdict import sys -from pathlib import Path +from pathlib import Path, PurePosixPath from tempfile import TemporaryDirectory from typing import Tuple, Dict, List, Iterable, Optional import math @@ -231,7 +231,7 @@ async def vactube_transform(ctx: Context) -> None: mesh.export(mesh_file) with open(temp_dir + '/prop.qc', 'w') as qc_file: - qc_file.write(QC_TEMPLATE.format(path=anim_mdl_name)) + qc_file.write(QC_TEMPLATE.format(path=PurePosixPath(anim_mdl_name))) for i, anim in enumerate(all_anims): anim.name = anim_name = f'anim_{i:03x}' @@ -275,7 +275,7 @@ async def vactube_transform(ctx: Context) -> None: targetname='_vactube_temp_mover', angles='0 270 0', origin='-16384 0 1024', - model=str(Path('models', anim_mdl_name)), + model=str('models' / PurePosixPath(anim_mdl_name)), rendermode=10, solid=0, spawnflags=64 | 256, # Use Hitboxes for Renderbox, collision disabled. From ca3845cfe88b450ed8eb2b99e449d500bfecc46c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 30 Aug 2023 18:44:59 +1000 Subject: [PATCH 214/243] Add a command-line parameter to force models/resources to be regenerated. --- CHANGELOG.md | 1 + src/hammeraddons/mdl_compiler.py | 6 ++++++ src/hammeraddons/postcompiler.py | 9 ++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ed9a2fb..69394e0e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. * Added `comp_case`, a version of `logic_case` that is collapsed into callers like `comp_relay`. * Added `--verbose` parameter, for showing DEBUG messages. +* Added `--regenerate` parameter, to force all models to be regenerated from scratch. * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Added ability to specify alt skins when using `comp_prop_cable_dynamic`. * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. diff --git a/src/hammeraddons/mdl_compiler.py b/src/hammeraddons/mdl_compiler.py index e1e94bff6..5c150addb 100644 --- a/src/hammeraddons/mdl_compiler.py +++ b/src/hammeraddons/mdl_compiler.py @@ -30,6 +30,7 @@ ModelKey = TypeVar('ModelKey', bound=Hashable) InT = TypeVar('InT') OutT = TypeVar('OutT') +force_regen = False # If set, force every model to be regenerated. class GenModel(Generic[OutT]): @@ -100,6 +101,10 @@ def __enter__(self) -> Self: """Load the previously compiled models and prepare for compiles.""" # Ensure the folder exists. os.makedirs(self.model_folder_abs, exist_ok=True) + + if force_regen: + return self # Skip loading. + data: List[Tuple[ModelKey, str, OutT]] version = 0 try: @@ -119,6 +124,7 @@ def __enter__(self) -> Self: exc_info=True, ) return self + if version != self.version: # Different version, ignore the data. return self diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index 5b05e2216..dbc55db08 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -25,7 +25,7 @@ from srctools.filesys import ZipFileSystem from srctools.packlist import PackList -from hammeraddons import __version__ as version_haddons, config, propcombine +from hammeraddons import __version__ as version_haddons, config, propcombine, mdl_compiler from hammeraddons.bsp_transform import run_transformations from hammeraddons.move_shim import install as install_depmodule_hook @@ -73,6 +73,11 @@ async def main(argv: List[str]) -> None: action="store_false", help="For testing purposes, allow skipping saving the BSP.", ) + parser.add_argument( + "--regenerate", + action="store_true", + help="Force models and similar resources to be regnerated.", + ) parser.add_argument( '-v', '--verbose', action="store_true", @@ -112,6 +117,8 @@ async def main(argv: List[str]) -> None: else: LOGGER.warning('Could not set stdout handler to DEBUG mode.') + mdl_compiler.force_regen = args.regenerate + # The path is the last argument to the compiler. # Hammer adds wrong slashes sometimes, so fix that. # Also, if it's the VMF file, make it the BSP. From b58a0aeeb43dc95d00243e715b8164c653294c8c Mon Sep 17 00:00:00 2001 From: asd417 Date: Wed, 30 Aug 2023 23:06:20 +0900 Subject: [PATCH 215/243] Found bug to fix --- transforms/geocable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 3451c4aee..985c1d9e5 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -797,7 +797,7 @@ def interpolate_all(nodes: Set[Node]) -> None: for a, b in zip(points, points[1:]): a.next = b b.prev = a - points[0].prev = node1 + points[0].prev = node1 # if segment count is low (like 2) for bezier curve, this will cause error. TODO: Fix this? points[-1].next = node2 segments.append(points) @@ -1284,7 +1284,7 @@ async def compile_rope( flags |= StaticPropFlags.NO_SHADOW for m in modellist: - new_flags = flags | m.flags # For the glass, we force shadows off. + new_flags = flags | m.flags # For the glass, we force shadows off leafs = compute_visleafs(m.coll_data, ctx.bsp.vis_tree()) ctx.bsp.props.append(StaticProp( model=m.model_name, From 3b4331f6428847a59be5f6460ebe636fdb0a93a1 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Wed, 30 Aug 2023 21:54:40 -0700 Subject: [PATCH 216/243] This info_target flag was added in l4d2 --- fgd/point/info/info_target.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/point/info/info_target.fgd b/fgd/point/info/info_target.fgd index 53c05e467..6c6cf8871 100644 --- a/fgd/point/info/info_target.fgd +++ b/fgd/point/info/info_target.fgd @@ -7,7 +7,7 @@ spawnflags(flags) : "spawnflags" = [ 1: "Transmit to client (respect PVS)" : 0 - 2: "Always transmit to client (ignore PVS)" : 0 + 2: "Always transmit to client (ignore PVS)" : 0 [since_L4D2, gmod] ] @resources [] From 61e41c7ab11ef8f7f2a685fb94df383627219347 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Wed, 30 Aug 2023 22:17:22 -0700 Subject: [PATCH 217/243] Ensure entities with a model selector have skin KV Hammer will auto add this when selecting a model, so if the keyvalue isn't defined it will appear as invalid --- fgd/point/npc/npc_rocket_turret.fgd | 2 ++ fgd/point/prop/prop_button.fgd | 8 ++++---- fgd/point/prop/prop_exploding_futbol.fgd | 1 + fgd/point/prop/prop_floor_ball_button.fgd | 2 ++ fgd/point/prop/prop_floor_cube_button.fgd | 3 ++- fgd/point/prop/prop_glass_futbol.fgd | 1 + fgd/point/prop/prop_under_button.fgd | 1 + fgd/point/prop/prop_under_floor_button.fgd | 2 ++ fgd/point/prop/prop_wall_projector.fgd | 6 +++--- 9 files changed, 18 insertions(+), 8 deletions(-) diff --git a/fgd/point/npc/npc_rocket_turret.fgd b/fgd/point/npc/npc_rocket_turret.fgd index e26f1d44b..510a5e3e2 100644 --- a/fgd/point/npc/npc_rocket_turret.fgd +++ b/fgd/point/npc/npc_rocket_turret.fgd @@ -26,6 +26,8 @@ TripwireAimTarget[P2](target_destination) : "Tripwire Aim Target" : : "In tripwire mode, the entity to aim at." _sphere_radius[!engine](integer) readonly : "" : 8192 : "How far the turret will be able to see targets. Always 8192, but this keyvalue is needed to display the preview." + // Hammer automatically adds this when selecting a model, this prevents it from appearing as invalid + skin[P2](integer) readonly : "" : 0 : "Skin on the model to use. The game overrides this based on the rocket turret's firing state, so changing it here has no effect." // Inputs input Toggle(void) : "Toggles between activated and deactivated states." diff --git a/fgd/point/prop/prop_button.fgd b/fgd/point/prop/prop_button.fgd index c30d53b61..25c28dfe7 100644 --- a/fgd/point/prop/prop_button.fgd +++ b/fgd/point/prop/prop_button.fgd @@ -8,11 +8,11 @@ model[-srctools](studio) : "[H] Model" : "models/props/switch001.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props/switch001.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." - skin[engine](integer) : "Skin" : 0 : "Should it appear dirty or clean?" - skin(choices) : "Skin" : "0" : "Should it appear dirty or clean?" = + skin[engine](integer) : "Skin" : 0 : "Skin on the model to use." + skin(choices) : "Skin" : "0" : "Skin on the model to use. With the normal model, this sets if the button is clean or dirty. With a custom model, any skin number can be entered here." = [ - 0: "Clean" - 1: "Dirty" + 0: "[0] Clean" + 1: "[1] Dirty" ] @resources diff --git a/fgd/point/prop/prop_exploding_futbol.fgd b/fgd/point/prop/prop_exploding_futbol.fgd index fdba3ebe3..dff36208e 100644 --- a/fgd/point/prop/prop_exploding_futbol.fgd +++ b/fgd/point/prop/prop_exploding_futbol.fgd @@ -5,6 +5,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/npcs/personality_sphere_angry.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin(integer) : "Skin" : 0 : "Skin on the model to use." explodeontouch(boolean) : "Explode on touch" : 1 : "If the bomb should explode when it touches something." diff --git a/fgd/point/prop/prop_floor_ball_button.fgd b/fgd/point/prop/prop_floor_ball_button.fgd index f5b920a5f..b1e555335 100644 --- a/fgd/point/prop/prop_floor_ball_button.fgd +++ b/fgd/point/prop/prop_floor_ball_button.fgd @@ -6,6 +6,8 @@ model[-srctools](studio) : "[H] Model" : "models/props/ball_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props/ball_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + // Hammer automatically adds this when selecting a model, this prevents it from appearing as invalid + skin(integer) readonly : "" : 0 : "Skin on the model to use. The game overrides this based on the button's pressed state, so changing it here has no effect." @resources [ model "models/props/ball_button.mdl" diff --git a/fgd/point/prop/prop_floor_cube_button.fgd b/fgd/point/prop/prop_floor_cube_button.fgd index b89ae7fe7..fd0cef496 100644 --- a/fgd/point/prop/prop_floor_cube_button.fgd +++ b/fgd/point/prop/prop_floor_cube_button.fgd @@ -7,7 +7,8 @@ model[-srctools](studio) : "[H] Model" : "models/props/box_socket.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props/box_socket.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." acceptsball(boolean) : "Accepts Balls" : 0 : "Do Edgeless Safety Cubes activate this? Should almost always be No." - + // Hammer automatically adds this when selecting a model, this prevents it from appearing as invalid + skin(integer) readonly : "" : 0 : "Skin on the model to use. The game overrides this based on the button's pressed state, so changing it here has no effect." @resources [ model "models/props/box_socket.mdl" diff --git a/fgd/point/prop/prop_glass_futbol.fgd b/fgd/point/prop/prop_glass_futbol.fgd index 18d7afbed..fca7fb374 100644 --- a/fgd/point/prop/prop_glass_futbol.fgd +++ b/fgd/point/prop/prop_glass_futbol.fgd @@ -5,6 +5,7 @@ [ model[-srctools](studio) : "[H] Model" : "models/props/futbol.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin(integer) : "Skin" : 0 : "Skin on the model to use." spawnername(target_destination) : "Spawner Name" : : "Name of prop_glass_futbol_spawner for this futbol to respawn in once broken." diff --git a/fgd/point/prop/prop_under_button.fgd b/fgd/point/prop/prop_under_button.fgd index 71cfe5f64..f3ad0fc04 100644 --- a/fgd/point/prop/prop_under_button.fgd +++ b/fgd/point/prop/prop_under_button.fgd @@ -9,6 +9,7 @@ model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props_underground/underground_testchamber_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin(integer) : "Skin" : 0 : "Skin on the model to use." @resources [ diff --git a/fgd/point/prop/prop_under_floor_button.fgd b/fgd/point/prop/prop_under_floor_button.fgd index 12ddb43f9..8eddc3b1e 100644 --- a/fgd/point/prop/prop_under_floor_button.fgd +++ b/fgd/point/prop/prop_under_floor_button.fgd @@ -8,6 +8,8 @@ model[-srctools](studio) : "[H] Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props_underground/underground_floor_button.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + // Hammer automatically adds this when selecting a model, this prevents it from appearing as invalid + skin(integer) readonly : "" : 0 : "Skin on the model to use. The game overrides this based on the button's pressed state, so changing it here has no effect." // Outputs output OnPressedBlue(void) : "Called in Coop when the button has been pressed by ATLAS." diff --git a/fgd/point/prop/prop_wall_projector.fgd b/fgd/point/prop/prop_wall_projector.fgd index 90ea848a8..82d95afc4 100644 --- a/fgd/point/prop/prop_wall_projector.fgd +++ b/fgd/point/prop/prop_wall_projector.fgd @@ -9,10 +9,10 @@ model[+srctools](studio) : "[HA] Custom Model" : "models/props/wall_emitter.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." skin[engine](integer) : "Skin" : 0 - skin(choices) : "Skin" : 0 : "Which skin to use." = + skin(choices) : "Skin" : 0 : "Skin on the model to use. With the normal model, this sets if the emitter is clean or rusted. With a custom model, any skin number can be entered here." = [ - 0: "Clean" - 1: "Rusted" + 0: "[0] Clean" + 1: "[1] Rusted" ] @resources From b697a9272435e51db0ef27a6eb7fc2031360f8c2 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Thu, 31 Aug 2023 00:00:37 -0700 Subject: [PATCH 218/243] Add missing hot_potato keyvalues, other changes --- fgd/point/hot/hot_potato.fgd | 23 +++++++++++++++----- fgd/point/hot/hot_potato_catcher.fgd | 4 ++-- fgd/point/hot/hot_potato_socket.fgd | 10 ++++++--- fgd/point/hot/hot_potato_spawner.fgd | 20 ++++++++++++++--- fgd/point/prop/prop_exploding_futbol.fgd | 4 +++- fgd/point/prop/prop_glass_futbol.fgd | 5 +++-- fgd/point/prop/prop_glass_futbol_spawner.fgd | 2 +- transforms/p2_custom_models.py | 11 ++++++++++ 8 files changed, 61 insertions(+), 18 deletions(-) diff --git a/fgd/point/hot/hot_potato.fgd b/fgd/point/hot/hot_potato.fgd index fd26a026b..988287906 100644 --- a/fgd/point/hot/hot_potato.fgd +++ b/fgd/point/hot/hot_potato.fgd @@ -1,7 +1,18 @@ +@PointClass base(BasePropPhysics, SRCModel) + appliesto(P2) + studioprop() + line(255 255 0, targetname, spawnername) += hot_potato: "A glass futbol variant which explodes and can't be thrown. Spawning this from a hot_potato_spawner allows it to have a timer set." +[ + model[-srctools](studio) : "[H] Model" : "models/props/futbol.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." + model[+srctools](studio) : "[HA] Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." + skin(integer) : "Skin" : 0 : "Skin on the model to use." -@PointClass base(prop_glass_futbol, prop_exploding_futbol) - appliesto(P2) - studioprop("models/props/futbol.mdl") - line(255 255 0, targetname, spawnername) = hot_potato: "This uses the futbol model. It can be carried around, but on impact it explodes, similar to prop_exploding_futbols." - [ - ] + explodeontouch(boolean) : "Explode on touch" : 1 : "If the hot potato should explode when it touches something." + ShouldRespawn(boolean) : "Should respawn" : 1 : "If the hot potato should respawn at the specified spawner when destroyed. Not specifying a spawner is functionally identical to turning this off, but causes extra console spam." + spawnername(target_destination) : "Spawner Name" : : "Name of a hot_potato_spawner for this hot potato to respawn in once broken." + + // Inputs + input Explode(void) : "Explodes the hot potato." + input Dissolve(void) : "Fizzles the hot potato. Doesn't fire the OnFizzled output." +] diff --git a/fgd/point/hot/hot_potato_catcher.fgd b/fgd/point/hot/hot_potato_catcher.fgd index 439661cc4..85dcb2d5e 100644 --- a/fgd/point/hot/hot_potato_catcher.fgd +++ b/fgd/point/hot/hot_potato_catcher.fgd @@ -1,6 +1,6 @@ -@PointClass base(prop_glass_futbol_socket) +@PointClass base(hot_potato_socket) appliesto(P2) - studio("models/editor/axis_helper.mdl") = hot_potato_catcher: "Catches 'hot potatos' and fires an output." += hot_potato_catcher: "Invisible entity that holds 'hot potatos' and fires an output. Identical to hot_potato_socket, except it doesn't play any sounds when a hot potato is inserted." [ ] diff --git a/fgd/point/hot/hot_potato_socket.fgd b/fgd/point/hot/hot_potato_socket.fgd index d9f8443e4..de6a2d51f 100644 --- a/fgd/point/hot/hot_potato_socket.fgd +++ b/fgd/point/hot/hot_potato_socket.fgd @@ -1,6 +1,10 @@ - -@PointClass base(prop_glass_futbol_socket) +@PointClass base(BaseEntityPoint) appliesto(P2) - studio("models/editor/axis_helper.mdl") = hot_potato_socket: "Catches (bombs?) and fires an output." + studio("models/editor/angle_helper.mdl") += hot_potato_socket: "Invisible entity that holds 'hot potatos' and fires an output. When a hot potato comes close it will snap into place." [ + + // Outputs + output OnHotPotatoReleased(void) : "Player has taken the hot potato out of this socket." + output OnHotPotatoCaught(void) : "This socket has captured a hot potato." ] diff --git a/fgd/point/hot/hot_potato_spawner.fgd b/fgd/point/hot/hot_potato_spawner.fgd index 50c399ba5..c4653952b 100644 --- a/fgd/point/hot/hot_potato_spawner.fgd +++ b/fgd/point/hot/hot_potato_spawner.fgd @@ -1,6 +1,20 @@ - -@PointClass base(prop_glass_futbol_spawner) +@PointClass base(BaseEntityAnimating) appliesto(P2) - studioprop("models/props/futbol_dispenser.mdl") = hot_potato_spawner: "Catches 'hot potatos' and fires an output." + studioprop("models/props/futbol_dispenser.mdl") += hot_potato_spawner: "Spawns 'hot potatos'. Will re-create a hot potato when its created hot potato breaks." [ + startwithhotpotato(boolean) : "Start with Hot Potato" : 1 : "Set true if this spawner starts with a hot potato in it. " + + "Set to false if this spawner's hot potato will be manually placed in the map." + IsTimed(boolean) : "Is Timed" : 0 : "If enabled, the hot potatos created from this spawner will automatically explode after a set amount of time." + Timer(float) : "Timer" : "10" : "Length of the timer, in seconds." + TimerIndicatorName(target_destination) : "Timer Indicator Name" : : "Name of a prop_indicator_panel that should show the time remaining." + + // Inputs + input ForceSpawn(void) : "Spawns a new hot potato. Usually isn't necessary, because hot potatos respawn automatically if broken." + + // Outputs + output OnHotPotatoSpawned(void) : "Created a new hot potato." + output OnHotPotatoGrabbed(void) : "Player has taken the hot potato out of this spawner." + output OnHotPotatoCaught(void) : "Player has put the hot potato back into this spawner." + output OnHotPotatoReleased(void) : "Player has grabbed a hot potato from or put a hot potato back into this spawner." ] diff --git a/fgd/point/prop/prop_exploding_futbol.fgd b/fgd/point/prop/prop_exploding_futbol.fgd index dff36208e..af0bf44f4 100644 --- a/fgd/point/prop/prop_exploding_futbol.fgd +++ b/fgd/point/prop/prop_exploding_futbol.fgd @@ -1,4 +1,4 @@ -@PointClass base(BaseEntityPhysics, SRCModel) +@PointClass base(BasePropPhysics, SRCModel) appliesto(P2) studioprop() = prop_exploding_futbol: "The bombs used by Wheatley." @@ -8,9 +8,11 @@ skin(integer) : "Skin" : 0 : "Skin on the model to use." explodeontouch(boolean) : "Explode on touch" : 1 : "If the bomb should explode when it touches something." + // Keyvalues dump includes ShouldRespawn and SpawnerName keyvalues but they don't seem to work // Inputs input Explode(void) : "Explodes the bomb." + input Dissolve(void) : "Fizzles the bomb. Doesn't fire the OnFizzled output." @resources // Assumed [ diff --git a/fgd/point/prop/prop_glass_futbol.fgd b/fgd/point/prop/prop_glass_futbol.fgd index fca7fb374..1a249c8b1 100644 --- a/fgd/point/prop/prop_glass_futbol.fgd +++ b/fgd/point/prop/prop_glass_futbol.fgd @@ -1,13 +1,14 @@ @PointClass base(BasePropPhysics, SRCModel) appliesto(P2) studioprop() - line(255 255 0, targetname, spawnername) = prop_glass_futbol: "A fragile glass ball that the player can pick up and toss. On contact with surfaces it will shatter, and it can be put into holders to power them. It is affected by gel, but the shattering means this has little effect." + line(255 255 0, targetname, spawnername) += prop_glass_futbol: "A fragile glass ball that the player can pick up and toss. On contact with surfaces it will shatter, and it can be put into holders to power them. It is affected by gel, but the shattering means this has little effect." [ model[-srctools](studio) : "[H] Model" : "models/props/futbol.mdl" : "The model to display in Hammer. VScript must be used to set the in-game model." model[+srctools](studio) : "[HA] Custom Model" : "models/props/futbol.mdl" : "The model to display in Hammer, and the custom model to use if Override Model is enabled." skin(integer) : "Skin" : 0 : "Skin on the model to use." - spawnername(target_destination) : "Spawner Name" : : "Name of prop_glass_futbol_spawner for this futbol to respawn in once broken." + spawnername(target_destination) : "Spawner Name" : : "Name of a prop_glass_futbol_spawner for this futbol to respawn in once broken." diff --git a/fgd/point/prop/prop_glass_futbol_spawner.fgd b/fgd/point/prop/prop_glass_futbol_spawner.fgd index 0a0e29327..22e2932be 100644 --- a/fgd/point/prop/prop_glass_futbol_spawner.fgd +++ b/fgd/point/prop/prop_glass_futbol_spawner.fgd @@ -1,4 +1,4 @@ -@PointClass base(BaseEntityPoint) +@PointClass base(BaseEntityAnimating) appliesto(P2) studioprop("models/props/futbol_dispenser.mdl") = prop_glass_futbol_spawner: "Spawns futbols. Will re-create a futbol when its created futbol breaks." [ diff --git a/transforms/p2_custom_models.py b/transforms/p2_custom_models.py index 1d88e803f..6dec99178 100644 --- a/transforms/p2_custom_models.py +++ b/transforms/p2_custom_models.py @@ -19,12 +19,23 @@ "npc_personality_core", "prop_exploding_futbol", "prop_glass_futbol", + "hot_potato", # Not supported: # prop_testchamber_door - reverses animations # prop_tractor_beam - breaks animations # prop_linked_portal_door - breaks animations # prop_monster_box - swaps models dynamically + + # Not tested: + # prop_glass_futbol_spawner + # hot_potato_spawner + # prop_portal_stats_display + # prop_rocket_tripwire + # prop_telescopic_arm + # prop_scaled_cube (Sixense) + # prop_contraption_cube (Edu) + # prop_contraption_cube_button (Edu) ] @trans('Portal 2 Custom Models') From 17cab654e0bcd01793e99c0ac29712fefb0b6448 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 1 Sep 2023 08:38:15 +1000 Subject: [PATCH 219/243] Fix #232: Missing OnFizzled output on prop_weighted_cube --- CHANGELOG.md | 1 + fgd/bases/BaseEntityPhysics.fgd | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69394e0e9..5a790894b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Add an option to allow the sources for compiled models to be preserved. * #210: Add `OnFinished` output to `comp_numeric_transition`. * #10: The center of the axis helper used for sprites can now be clicked on. +* #232: Readd missing `OnFizzled` output on `prop_weighted_cube`. * Add an option to specify the maximum distance for automatic combined props. * Allow combining models containing `$collisionjoints`. * Add missing `bunting` keyvalue to `comp_prop_cable`. diff --git a/fgd/bases/BaseEntityPhysics.fgd b/fgd/bases/BaseEntityPhysics.fgd index cb80c583e..23fb360c7 100644 --- a/fgd/bases/BaseEntityPhysics.fgd +++ b/fgd/bases/BaseEntityPhysics.fgd @@ -64,4 +64,5 @@ // Outputs output OnIgnite(void) : "Fired when this object catches fire." + output OnFizzled[P2](void) : "Fired when this object is fizzled." ] From 6ed8d8dbfa3f11d5cbf83f0bce54991c1a240637 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 1 Sep 2023 09:23:54 +1000 Subject: [PATCH 220/243] Change helper override behaviour - preserve overrides directly defined on an entity. --- src/hammeraddons/unify_fgd.py | 48 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/hammeraddons/unify_fgd.py b/src/hammeraddons/unify_fgd.py index 0910c579a..6085c842a 100644 --- a/src/hammeraddons/unify_fgd.py +++ b/src/hammeraddons/unify_fgd.py @@ -14,7 +14,7 @@ from srctools import fgd from srctools.fgd import ( FGD, AutoVisgroup, EntAttribute, EntityDef, EntityTypes, Helper, HelperExtAppliesTo, - HelperWorldText, KVDef, ValueTypes, match_tags, validate_tags, + HelperTypes, HelperWorldText, KVDef, ValueTypes, match_tags, validate_tags, ) from srctools.filesys import RawFileSystem @@ -920,7 +920,7 @@ def action_export( key, ', '.join([typ.value for typ in types]) )) - # Pick the one with shortest tags arbitrarily. + # Pick the one with the shortest tags arbitrarily. _, value = min( tag_map.items(), key=lambda t: len(t[0]), @@ -1027,31 +1027,37 @@ def action_export( print('Applying helpers to child entities and optimising...') for ent in fgd.entities.values(): # Merge them together. - helpers: List[Helper] = [] + base_helpers: List[Helper] = [] for base in ent.bases: assert isinstance(base, EntityDef) - helpers.extend(base.helpers) - helpers.extend(ent.helpers) + base_helpers.extend(base.helpers) - # Then optimise this list. - ent.helpers.clear() - for helper in helpers: - if helper in ent.helpers: # No duplicates + # Then optimise this list, by re-assembling in reverse. + rev_helpers: List[Helper] = [] + overrides: Set[HelperTypes] = set() + + # Add the entity's own helpers to the end, but do not override within that. + for helper in reversed(ent.helpers): + if helper in rev_helpers: # No duplicates here. continue - # Strip applies-to helper. - if isinstance(helper, HelperExtAppliesTo): + if helper.IS_EXTENSION: continue - # For each, check if it makes earlier ones obsolete. - overrides = helper.overrides() - if overrides: - ent.helpers[:] = [ - helper for helper in ent.helpers - if helper.TYPE not in overrides - ] + # For each, it may make earlier definitions obsolete. + overrides.update(helper.overrides()) + # But the last of any type is always included. + rev_helpers.append(helper) - # But it itself should be added to the end regardless. - ent.helpers.append(helper) + # Add in all the base entity helpers. + for helper in reversed(base_helpers): + # No duplicates or overridden helpers. + if helper in rev_helpers or helper.TYPE in overrides: + continue + if helper.IS_EXTENSION: + continue + overrides.update(helper.overrides()) + rev_helpers.append(helper) + ent.helpers = rev_helpers[::-1] print('Culling unused bases...') used_bases: Set[EntityDef] = set() @@ -1071,7 +1077,7 @@ def action_export( del fgd.entities[classname] continue else: - # Helpers aren't inherited, so this isn't useful anymore. + # Helpers aren't inherited, so this isn't useful any more. ent.helpers.clear() # Cull all base classes we don't use. # Ents that inherit from each other always need to exist. From 2f382bcea350942d4fcd7a12f154592727da69af Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 5 Sep 2023 17:35:36 +1000 Subject: [PATCH 221/243] Add worldspawn's world mins/max keyvalues --- fgd/worldspawn.fgd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fgd/worldspawn.fgd b/fgd/worldspawn.fgd index ca86e0853..7073928e5 100644 --- a/fgd/worldspawn.fgd +++ b/fgd/worldspawn.fgd @@ -1,6 +1,8 @@ // Super-extra special, so don't use normal bases. @SolidClass base(BaseEntity, ResponseContext) = worldspawn: "This is the world entity. Each map can only contain one, and it's automatically created for you." [ + world_mins[engine](vector) : "World Minimums" : : "Set by VBSP to the minimum bounds of the map geometry, excluding the 3D skybox (if present)." + world_maxs[engine](vector) : "World Maximums" : : "Set by VBSP to the maximum bounds of the map geometry, excluding the 3D skybox (if present)." // "Doesn't seem to be used anywhere anymore." - VDC // message(string) : "Map Description / Title" From c8864f915e5d910056d20ae0433d7affa983ba89 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 5 Sep 2023 17:39:08 +1000 Subject: [PATCH 222/243] Simplify this --- transforms/vactubes/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index c1f2b1f77..3b80c8cb9 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -2,7 +2,7 @@ import subprocess from collections import defaultdict import sys -from pathlib import Path, PurePosixPath +from pathlib import Path from tempfile import TemporaryDirectory from typing import Tuple, Dict, List, Iterable, Optional import math @@ -231,7 +231,7 @@ async def vactube_transform(ctx: Context) -> None: mesh.export(mesh_file) with open(temp_dir + '/prop.qc', 'w') as qc_file: - qc_file.write(QC_TEMPLATE.format(path=PurePosixPath(anim_mdl_name))) + qc_file.write(QC_TEMPLATE.format(path=anim_mdl_name.as_posix())) for i, anim in enumerate(all_anims): anim.name = anim_name = f'anim_{i:03x}' @@ -275,7 +275,7 @@ async def vactube_transform(ctx: Context) -> None: targetname='_vactube_temp_mover', angles='0 270 0', origin='-16384 0 1024', - model=str('models' / PurePosixPath(anim_mdl_name)), + model=Path('models', anim_mdl_name).as_posix(), rendermode=10, solid=0, spawnflags=64 | 256, # Use Hitboxes for Renderbox, collision disabled. From 4d3ae9539d77063df2b7c21577eb962c07c66696 Mon Sep 17 00:00:00 2001 From: TeamSpen210 Date: Sat, 9 Sep 2023 20:10:22 +1000 Subject: [PATCH 223/243] Tweak flag descriptions --- fgd/point/env/env_fade.fgd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fgd/point/env/env_fade.fgd b/fgd/point/env/env_fade.fgd index 6b9be9edc..2e330b539 100644 --- a/fgd/point/env/env_fade.fgd +++ b/fgd/point/env/env_fade.fgd @@ -6,9 +6,9 @@ spawnflags(flags) = [ 1: "Fade From" : 0 - 2: "Modulate" : 0 - 4: "Triggering player only" : 0 [!L4D] - 8: "Stay Out" : 0 + 2: "Multiply Colors" : 0 + 4: "Only Fade !activator Player" : 0 [!L4D] + 8: "Stay Out (Indefinite Duration)" : 0 ] duration(float) : "Duration (seconds)" : 2 : "The time that it will take to fade the screen in or out." From bd7ad917a4e29121fb9fcb935def8d613dafdc61 Mon Sep 17 00:00:00 2001 From: Kelsey Date: Sun, 10 Sep 2023 02:04:14 -0700 Subject: [PATCH 224/243] trigger_transition doesn't inherit from base trigger --- fgd/brush/trigger/trigger_transition.fgd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fgd/brush/trigger/trigger_transition.fgd b/fgd/brush/trigger/trigger_transition.fgd index e30e50d92..c0e2d46f6 100644 --- a/fgd/brush/trigger/trigger_transition.fgd +++ b/fgd/brush/trigger/trigger_transition.fgd @@ -1,5 +1,5 @@ @SolidClass - base(Trigger, Origin) + base(BaseEntityBrush, Origin) = trigger_transition: "A volume that's used to control which entities go through the level transition. " + "Create one or more trigger_transitions and give them the same name as the changelevel landmark. " + "Any entities within the trigger_transition(s) will go to the next map. " + From 1e70c1be58eb546c475a46e44da6dc709dfcf01b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 11 Sep 2023 08:49:53 +1000 Subject: [PATCH 225/243] Don't use Squirrel lambda syntax here, might not be supported. --- src/hammeraddons/bsp_transform/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hammeraddons/bsp_transform/__init__.py b/src/hammeraddons/bsp_transform/__init__.py index ac7f66ba7..122a19174 100644 --- a/src/hammeraddons/bsp_transform/__init__.py +++ b/src/hammeraddons/bsp_transform/__init__.py @@ -201,7 +201,7 @@ async def run_transformations( # If the entity script defines either, running the second script will make the # chainer pick it up again, calling the function twice. So edit the script to # first blank out the functions. - code = 'OnPostSpawn<-Precache<-@()null\n' + code + code = 'OnPostSpawn<-Precache<-function(){}\n' + code init_scripts.append(pack.inject_vscript(code.replace('`', '"'))) ent['vscripts'] = ' '.join(init_scripts) From b4fdb085ccb2169352d6eaef780600ffc35aa1d2 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Fri, 15 Sep 2023 10:35:05 +1000 Subject: [PATCH 226/243] #241: Redo tools/toolspropcombine texture to be square, less opaque Co-authored-by: <75689188+TPEcool@users.noreply.github.com> --- hammer/materials/tools/toolspropcombine.vtf | Bin 22080 -> 22052 bytes materialsrc/tools/toolspropcombine.pdn | Bin 16598 -> 8315 bytes materialsrc/tools/toolspropcombine.tga | Bin 66075 -> 7792 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/hammer/materials/tools/toolspropcombine.vtf b/hammer/materials/tools/toolspropcombine.vtf index 20697d518dab969f2915894b75ae7bac7ad0d882..4f6423918e71c1512632eb5d5a2c1c41353256ec 100644 GIT binary patch literal 22052 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 baJ9Rfs1N;8~D8*fY literal 22080 zcmeG^4Oo-cxrfBqUp#kMzF_sp3O+$QPOnkiqvXRLg+-L$CfN3B(? zxN|bIz^xwx=tq!dhm+hLj3pqVp?iSM) zguJ$*|6%O_JImB6wKVQl=s&11lqSNLJEJ{P37?A1N{bf0+!>~25q!3n9ZkXT6+6Yf z6V^;v^T&cMXV-4{@b^xSc9YYYS5f`%tB#%*pDlHIn95kSW=wDRy$iqiQOifaX$kH7 z^A+=_D$lM?4DEm4ijhA-vULCl&Ouz*!o9I$_WICyd{(N?dhYPn9iek+UE_P0tD67Q z(D^j4uYM}3DxoEGP6sS!RsVV1FgTKxYn5P@ zWzQB3TiaGPWC*Mmu2<%zD}{-x+L}!dIX!45LQS*35b9rRecAFyXFYvOl|g)jCn9xDG6%gx{KHk7t; z9-P~lh|tBQg^M4C^>LlrnTw;qx64 za$ifxeNEaQuxBqUB6_<=mmtFY2s6y3Lc{QnNzPDA_xFeFS&teY7II_%twK4Af%TxOva)N-4b%U_k@|o{f4+H9`!d6w_yx-_<>l|JYcrJ2w|Tfx)c|)DvL$VXy*YIJWPm#h|GXvu zbEB>T{N?LpQ5N~J$_D&_J&MTx|F`@tHAIM;y-=z3agzSOx|3{Uvqw&qV9@{D58K}l z&2NE~I2z+L)jI?1Z5jozW^?4XYabmPC7UM_r2c zj5aPQi(7I{I8Y{kY{R2WJ9uXPong5B@%o7w>I)3eC$(}+!FlHXpS8@aMlDF`sw-XP zx4ih&yH8xTfW3)JT3dIlxAleNU)@>yU!ncO_HTLr{Qkj7=Vp!T0(+-~?Q`Z+WRhyA zPD$7AkjZ5Jc|Yd!lNRTrGZ3GYl$7*f@sU%bSI24Fky1CT4$SC7gHMmHNrUr_AGRUm z1;52mGNc^sr{~FVORVN2^;y-Y@2_??AT*=2q$KaOkf@4RC^?v42SHEx{(6INy8qyP z2lvDFM*m%S&ck$yU|&Oc3e%YEdicXIIDEWZ!VA*<18rw)5s;}k-@Wp>HgHwx{dhK5T zjzfs}B>II#c~rjOy$=uz%;zqT0eYDKOpc`$#>daN5Ew5`lJGm47&vcO&z0$99;|QG zuHOS5;uDACCi@5b6VQK_{r3GQmIEteI!a*u2+{kz2d6rpDnFFLd2G4KGTA==vY3^r z|6LOU@ql)L(YRnYo?24-(6x6sh?i3^-b>0;@%K4%0=FHZWP>3Y_l!PvY3VjS)rZ5A z7n#T^X6zTgn0wrL3@Mi_d+RM<9&ednGEh(XMf-fwq+r&-H!>zKrTag8;J~LAS8_t) z%)!$6p}meH;VmKpOUBkE|V0fzF>5Xn1Sw5w4dF@9_^`MVT zClfc91qoW-?ues!SkLpb5HdSI`(Yl{SCHSybs$lD^|75?04^_w@#YE@3eNWd(EsIS zahXZtkH`PQfY4W#{ROGgd`x^!4juoKl9=FlBM0-JsF~yq%#RwTQ~ohwK0h?x?@uX_Oz#Zw&Ao=?lV&{^p;vgW7+;v|o@viunJC z{}0*!e2>vJH|0DiC;{PN5AL7ei~=MDO|mjXxkmGx8$9D~CK2 zfLT_;Lq$0R-|KjBA?&YP?GMGnHpus(-aH@#-|PG9ZT+|o_W!3=Dc(d#q2NW~$_l&t zU#WbJ@DUfHSbsMmq4N+I>RlUwakH+w?AZQ2zt+|J(Y} zoBrG?-qKL_mj80Am*$rhnVFgIwILTF3C}x=pd7%ma*~%Pt3cxshJ136gH8YPWkT%U z6&pDZ6NhlVdD9j-j3@fwc#W-@(kG1$!g$uzRc<3GgM3@7l<6F7rKJB?20(otDoiK^ zcc6lEJ|f|9rioqu>AW67rfId1yH>KSMd+5f29d7<^dlT>4cL>73_lb zL$&_1YCk<65P#-HJm`a(XDn-Iza=Uv%17QC=T!d4h$ue8n3?Vqo{rwUcxw%nXHij+ z8UK}ZB#;h}4?>XNvOPj{S&dC?t_X)1q zIKbc0i7Ee+gMmNPr?B8(hz4Oa$Z6Y)kkw(;gwNmK_V;erf821PSkiwuKPrEc2YT{R zbq%eLv+NeRAK*hvQyzo3_~y!Es+dhI$omzcs%;JB%az6 zHa&wQnE5C%LpGkuR~MzwVLu2T$5MJF&vT5qE6IiQuJ6HkC74HDxPF>1GqK*rTIqfo zjTxE548mW$^?kZNdjE(S){A1aP~QgrRt+u(Yc%>kTVOqvh|FWQ`MuK!y^lKR!H3V^ zoBrM^{$A7n<2l`L*}IG3ylhneqr&wA*E4dA267<3bz(sNGHp=)w7%)&iOUWm0Q>P0 z1ClX!k+pg+jsLVt2W!go&C_?;J;fkTE?z`Ys%2NmT5?D|p6WN?dGiG{^GQk%_XiP{ zCA=Ilc&uOBu2q^?tLj9K$1M<#oz~0uh@yxC`hOplH|2k)^*j80!uj9r`p=6Hjzj(d z@@#j(k=yVzDL$t#?T_?!|8uUR*Gu#_#`ht5(7)jPxxx9FcKVJcgvY`62R3Y7NAm+1 zzX{6UH^JXH|KH31oB3~~|2F^Iix1cT@cYr*{fXfJx$>Vo?SK0Fen;j9XZ1Jg8{y~E z*ZALjE^$xP0GS*7ofaZ3qxpof0XU|^`vhc5XRFMoUh1%uB)<*(-wz$^bKOF8Pt9V> z2(WkYNEWT&_ZWtZ`PDcnenKLjVZLfy5A@I4z#n(BY?W}-0Q7c0oe%Q6=gck?%{<_e zf&GR4aI_--$kZn9W$@SFk7<60n||$CAh;43fCuSx8eewe;XH2#^sgb>78$Ohk+}l% z6L`Hx12XnB-%|O=wfY3QelaB_<-K|dKUup;!dES?Gq@q6d>ljn7RF!n3DMDiMsiW| z|B$bl_Zr^mN>;mR{E2y<7utsQaB7+_FmY-K7|R_~N=VN(0{vYC?;uUE|1WEvk&OiZ zr9?7_FV4UCsjo)urSYK+5^gt+EviYiBvN`g#5ZJJB7L4IqEd;!wNhHY8i_Qngnndk zqXFtMAFG&3fUVXseYM}W>i^AG>(u^BweWfj@~_OaG~Do592zhl;x9Z(6(yJZvxN@9 zOXFj#5PI^|^X}p|fN^i!8qqMLSpm#D+kU5*{#8|DO zDVfgCi5O?M=({G9u8lMv(?L9in}hiQ$eZQ|{*CBKo8m=8^KDJD$@J0*c#q=90{#%s zLx6t555li4_QPvED*m)HJY>Ju-hG6=UjXr^;Ln7P2ZgH;AHsVQT@^d~0)zu6u1PSw zHe`#=_cUh-E(X>g2kF`f^FBuHPTtrh%@6XQ-B#>QEq2cVIF8m+wz(i!_3pD%c|kv| zkqDxEEE)s#3fRBK!3tuXTFSp+|FquX#vuGu3(a5SXnb3`i{`JLguWlpXsj&W3a4YZ zLhEnzeFTfI0DQs)@g;>PYWixw!~K6>_5XKXZ>{%IlBB<|zsys-ugUJE@i`tPlgp!o z`uX)y3K_i5kn97;@0mWlXtjF=Jzs=mBQ~`4l-Ygxgz8&!lPN85f9*go=E8s|=PNCL z{KV6fKITKZ4>FTmifr_**rad9`HPcDRz% zzOkV5zPV`PRhWRTPcw$bqkXH--m?DJ*Qwo#hA7HEgBQ_nFK%|PCAD4*u&Rqq$LE<^ zSl(=Uo_soM^$JGI$SI{#eMX^@BYbv$v9IQH&~K=pWbnAa|A1=&(9``z1HE5}-V+6; zlj4Ei0rGIcV|I?7-v~+{rvJaam^Q6r^|PZvAL3^$m^6vxL-LJf<0*eQKHZ$tpKbHP zSKVI?h3VY9b zJ5&QvQ&VH(NuG4U^medsPctwjsX|($;AN6LA^-fA{x?JM3J9qD!CbJo9+f20^}+sW z{)tbcx*eobAA;|+{)fl^M|13MJ6)d#@8)5NPX5>@eh-ChyQ2Z_?S8g=P0wu)qu)z^ zMd7>XLEfDi*WoG5NN^xNr!aj#w~%D8}QopZ2*V%2lh*% z59c3F&x>H+KtJhxlH(5cPeS~{>A#tuX?IHe0{RQ%rTli!b@cFj>ALj?))$WduK95= ze+|dqs{V(^kA1y=5qeOTKE&s@`+URwKduuzFTj#$Kfq{&n7I!8cMyhf6G`{`I{044 zhxG5e_2Zv=|D@#cI$B2y^nXC!k^Vne@6t&BKhpn~e&4J&`*5%0?{@zm>Hqg{d=T#c z`HkOi|B?QGr2kL*qmll9r2il3|A)Q*iS+*?{eSrV o$w>b{@c-|N;M1& diff --git a/materialsrc/tools/toolspropcombine.pdn b/materialsrc/tools/toolspropcombine.pdn index efacf9da702d1318f0e4fb55f15ade1e2e4e6bea..6e60ca7d3c9c4006ca4ed9aca2fb65096a4bb177 100644 GIT binary patch literal 8315 zcmd^Dd9)l=nSal`EDr)n*b?FldAuM-8&kbhbyY(iNo~DVFI8Q=b3&-Ds;;W;>aDh} zZVdtD03Jk~Gep4=azxIF9x#juB8!9>6&yq|1T+dlIP3#LfD9q)>#6FOm>1O1nKSbj z)$e|H|9$s<+r9VEL@KcHl#w-Up&4s%k~q;-1Vf&h41$vrvZzS1F;iB#o~Y?llTNeF zS)x!-3_0(#D6QtyB(yrT8VUM+t9`*haPs0cyr~Hz4*=$rM+D&O{QpscKOxH_A zq!#LYDK;mQIms!JZWTjuwB+P?hbu-!w*@Vx*o-%ZYC6aV8Mh(RWO6>t8DrPF7E)qz zZeyG$Y)Weae!=1aF&ONHLTU?6 z`6C{ptdKO(!?Rt@Dv5eC)Px`bs#!)qh7ye!$jDjC8#5Yal!3??MH#)86ksU>Cp6JQ zvwA8lcGzA5^bkF;D~BpIw9v{kp#lfzV`|J&y&lb^S`?iHiwRR=lXBN?X9U6)>M&Cw z%6=Tz#H)AL?XO!p=*%q?qyez%U(^JrtV5`V!$u%8aj-5fVLt?6Z|x z#*Zsf$dKR`Z)hk?QjtJ3*-HYnoX7fMsM;+QGTE9$Fb0wLmsqa?1wdLULxLQp&3w_H zj>V*2l}8!CDuZ@7*C0Da1L-hWz^VeEXg9im%;(KSpQ=S8N|5ixk+i2OJIj_!3=NZI-Ubnw^><<%Zi9gA725s)u7;aEsSWzkJS1oJZk6|i+ql2e z@YDbSFGGU_=^hQNbgctH~ zKF@mr#b6NvkuYxr;&a}15CLjrUJ0qNjhB4YULA+CW`vLQ;3lE;eVz;|2V%`+na(&0 z4o4HUZn7!1JcriNKvTT z&nuZ?oyh1W+0atak`@v*7SqxxK#(hae?`rtS)GNov}H0uM(Kudtxe%3<5LMVr35^& zly0(6GAb6UMmJAHG>?FGz_K6g*szX=3aLhqXjoM<69T*)J&)@ZrB~KjJ40 z2xVz+tX)oK+I28f0zk=N%4Q%OfE5T)VIy8dvuU1B1WUd$qNUS)O2?6`P?ODO-4^|! zWSUCQDLiZdysxF=K!}AS6sD=wM7zu+@lwIa#{o79h>B@=EN@Xvr+F;XhjG-3dcaCN zpEBjVO#^v9tlF@v@e(B?z+%CP3J=2p>g- zV7e${Py>dG4HUz(QN^mtmeo#Y;{sgt$Gi!Z)xAUvk_x5>MY>JRk9o2-pk(SFmaR8sY2pq7?R00BJ(X;eCwbj)3##ZbIt#uF*PCpU^V#&?2j+^QF$bUm)gcraeC z#|p__6mE6fWw@dzXqN7v9x)qe=6DPNyjHdzhx)9Bus8+N5LKirJ(5mZb$=dg)Dr}( zVXYG8^VIv2>Mho@fK}@%WTTi2c|4pFNh4~A;%t^^BtuNe>Y5#c?Wfrk zT8M#-8je9~AmuRE?}VDX&_d;SHHf#%DkbKTV9-Br34bk0B7nMsW2quwYiucD+tDTdXLeX#Hjan3IVRk%Y))=Yi(^SMm=|H{ShH7>h z>$DM+N3c*lS))M(fYW&{TuG<7DkU*6=Ni*B zW4;8LugYa#9m8eZ(yVwl$`Y4#n%qW=5(RjNnPZ z4FSf}p=_*dWLe4^!ctV;(@sY9rrOftVIUzkIJ|77yA-QLNRBDO65Nq=pIZWl?jLrEgJ~-Rdb8IGJ+5T`) z$9+V_2E7R#WHB(RhY~$2AI_TH8sJF?BA(PDoEWO+6ob>#AyN^N5zJ4t5FvrYwM^5h zdmz4yB(joHZuJSJ+^iW%vTUJY56_o`K*e98xQc+Ji6#&OvIHrk?RHrx*WxUN^Gyzc z8JUIyY@nCPg-pz>bnAItWwm6m0y%R~)+2NQ%S!bU9$hdP3g$5&-XS`1PhM7xYR^|` zL=iO6N8zSzrU<2|X{0KJ0L>I8BK-eu|%X4bMqzio14o<;IZ)LV~~hj-u< z(ywAn7O3`NZyJd*a5qEnIjj{4WmP>^5Kyy+IG16}&g=PPIur86;&e3QT!y`Idw3Z> zdYA43{VD`UeE}O)8%>lV`cT$=prE80&emCzY2f(e1aR>h;Mn5`IQHbacx2T1jf@PO z&&}O$;U^rDq&cN&5G^Am8mkG;;4YXr_T)O@#dX01ZmNc&jCpZg=8Dc{p85tA*Zuu zG_L0g(z8Kvg}rE2gRb<&(`MDJnfLgZ1OI3BJfjPjj=Q!nKHniOuVHe@N7OW2$A%sUTAVc zb%N2PFE}}2^xERoq~+V9J#u(owx7= zF0jkx6sHL|&cz>cz=vEM>=`D3KQcijbO!MKV}OfJ07P8C#ZD7E3lMRDhJeU1KxZFE zR}BP87@C#{C(h3R?>Pp1+6llR7x?s#0y_))TY(oJ^VRV)9F*}hU2>L7mO5nNGKZXS zSl}&9JRTtbH;0AGX6fvRjwwciYY*f4@Vsbit+uEcim1=mO|8oD;^JYa=lDT#@=U|< z99cM1o;gj9jyw)AHVi?Z`wRR$vv|{gWb2yX>9c3BGs|#=F}h&ExwE@wm=EcR0K;H- zwliaVxkES|y(V1-i{pK!?W0Zt$s8FjTY z95Cq=##cJz_<-}A%3PQA|7_cC_$kp$IhR<$((aAV>2DPC)Ls#oO-nS31^O* z#y{ndx$aK?oZws?oN(yR)15O{5a;SV*YrA_V@{wy!6uSoqowsQwZ+$6sk>?2(d5Sd z@$;R^TvwETT^X)TjWxN3_^0=a`R-BU?0BJ~V;o6c4RUi*IK$R>f@`N%IL@yb9XsG>c5#hw6I}- z+pu9|feYn+qplMSJ>|1*&GiP?kk%bdLw$Rk%BbrZuFmIO;E=JfqN+2DN7s$+bQrtL zosN-F*Jp-!fLohz$e-Z3MmOp@(h$$*6y{+qO%SzXTN(Y;Y{W@tzz`91=egeYCy0>S z<9gl@G3XS=vZ8Gq#d~D*7qjr@y590PdYChE=wWkRlmAOT;u`1z$3Z1S4ua#(zhmg( z9dCgkRz*z{1*dy_bky~tp;+B~biS)JSGdFPV{(xibxq8b{-|qFu1JpyWYjg6VV`Rq zAL(-yKkRe$d}80pj;fu;neSICu3IouI_d4-`cC?(ckx_i#pgc#{@I#-4U*h>`;wg- z?rA-;Y|Sl~L3iFTXa7qN-MVQ9FKxQ?hUd2Le*W<5hxR=EZ#Ue!`_zXIeqr0Y>!h7~ zw+;^8H8{BCffZLgnSE!ky5)uKe;DlBbZGmL6;bB?9Y5Uo>cu>$z^oc;CqL@eKs(e7|qNG=J?Kfvv#)Ax5P2HIY0Ppc+Qj8E&Tez){DT+ z%~wX}Ec8Ay@_$6`ns@59-!HoNqpSGFtW`YsgHs-vSU7_2TX7W>TYg@!{oaR}8+U&3 z5wrAGYfE5X;>qnlyyGW_=qCQzcKW>=HvM}4V@`h8@`0P@US8qk+iQoN z{MqSWd-jYD4(vJn;NUL~+F!VS)8UslJh%POt<&>2%KPSzZDC>`S>v25X07q2_0cT4 z?CKpPu{1LlTXy7YG{he<1%Md)|^I<3}dOPFufh*}3b_SY|9C?9sc1ckkZ)1S}zYes{B8J%7Q% zpL&n~ty~G+w)M6{w;|7j51jkd%E#BAmCIay=9`yZ+b6ev<~|a9@q>-q#*S>ojgt>- zIQRZV#u5FQPm$lb_M%^GD(+srp*Fo4zjo`?U_Md*USsoj&w6j^J>S3mFt%g+&a3RZ z({Q_bNIu{F0X%U z-L|t|eejvx%igTqy6Nph<1gJn>g-Ji)~>$rEZ=~=u}(hUcyrIjs|MiIu9c)7xM*O?%!=t%438*z@v^P4L5akn@+^Faht_`__)ZWvA}n zl$`!xedx;fJ5S+1`;vUo(j$v*yL0f93~-Rx_{N3u-c7G;r|*39;3JM`_IKa7@Rd8? zID6lo2i8jau07|vtH}a+=rg;%w0GqBr;W_UId9&xcC)tR$nXETOE+xyx^S z`HhuVw)iXV{m;d3e|i1jPAYWJGG6+pi{giVV7>CA+rIVa{a0PrUAg7<2Ua{IU31@8 z3)`H36z%$2>)VGGyu0bg!S%0f_~jycdil=X*ROf#8&8>@e?IG}{hxgQ`Be`FZasNb z_nQ5M&7Xhk1@GX2cTV=cwzIfM+`IOcpKpKX`w|9i+jaTx_ack7jQ&PamhU~~o+dw7 z``#S~<{Gu0ea`AH4vN1x`^>->4_h~O`=7t*b{O7v-M-0pu6uXw zmwOjpxp&DF{`e~!Uau_QbMvafvs<6uw&w7z-sNA&+_SduBXa+)BVU<*c;%MJ%EO1G zvCowb?_!_($0gGreC21KSuwcxg#*{$JtySj{oal z8Xzyd@QE7+r~LTRJ?l%aJ->Hks{860kAH|A{_yF0e;V4qXw`QAbp!L6AAN0l^L^ko zhaL(%9(?~h?|ClVviHb?p%))s%1pENhmR=_jg=11|M4}e2ICKX`q`gA_)CH?k3ZrRnd P{>ayW-Tr6yxX$=rik$D0 literal 16598 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

;Gu`iXWPP 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 b_6(+Xo_Fqd&D8#!=bhez8=t(o??3)8v#Qc( diff --git a/materialsrc/tools/toolspropcombine.tga b/materialsrc/tools/toolspropcombine.tga index b04c4e61bf8726a2f44c3171793d97c3e171f49b..55328e3796839e9929b26476f8800efe12b7a2fb 100644 GIT binary patch literal 7792 zcmeHM>r<3v6hFJLuq?YQ;&Mk=yc+|8c)MNIp=q-&kG_R zvHne#$r5ynv|SDEmjhR~@vSrbCV1-r6@<{n8}im}NTD|U!KG98YM}zp4PdyLJIT@7 zp&YOs^nxy6PKZvp?l=0AKWuq-Omj*<&7&X-6u|^CK2ENkivcNiX=FRB-!BUh9B# zuebwOBx}@HQn|X7at4+^0fMAtp5WR)_#{u3uWI8ucR~y0CW$9^54HM-d=I&lmm+C7 z`^aZuF`mJ2ucMos$`X>)0VklUT|9Yvyi}T&bC8FJHt*v)HS3sUj##IhA99^jZ5Rol zJgG=&O*YxeSKUclVZVuz$Kb&MBcGA<{GFt$Z%MafbqC%feNe*!(p5dBC!1J<0<4gp zmebiT8G~OD$truwEH8y*D>PzFmN8G(@ldko8;YH>tBPX1Tmdsg1}ay_80E=-n``&L z_en=k)ww+BP=_BAh07HZhaojg%iT*eNODWvma!5KCsYFG_!lNJauJL!-&qRMNWV(S z_Sg*%O(#Q2#;2yH;PknfWKA9L5lNN@3nxFob!yhpZeZXJSb=EZX~D^}!{K^R!$On$ zG}nnRAng0MvIF)c{MBI z#K+~mNQb)_7DO%q6rOi6HwY*eNQavDu}JZ>d9*gtPs}2lw0#{7N%5Z|!zfld1eM-9 zo936?-5dkQEG$$c_kAU=YnB+E#~ykJ&POMlE4@p*$YJeCUx>Hx7sxG(+`W`F_SqKF(I(@?(rTY+ z*;jZjZ{ji==vp=v=+S~(4Z~xiE_WC*!DFSd0W~DU<{R6Y}uUn$RuZe9&)HbkIs=IGU%nW0EZg%a}}gr;ar!R#ty2w)PqeCG{(nmeG^?|691 z9GT3(85K-orVCuU9g-FQ4B>#n075DG2X8Qq2DP{`q@DkOns`iuvPpxuh}0x4wUaC( z%n)zU7VdhOzu3vHI!9?R)j#BA%0Np*djy0L3wQH`kNpLDt>F!hoa!^W?$of)2!jQW zb>UFy>nUDkcTf($kj*zMrjgs^B}zfCBYP1)^nYDzlvodu%CNmTWKP#Ax&Q2=*uTc- zxUHz1dcr6weR34U?3ghtTi(b*>Q3{EPAwK-!KB5Mm0k5YOQxW13FC@^pW^?7JSY+L z+zBr)B~3FZY7Qo}gtjF|TT2O6HUYW)7cu6ZWxRtdmV@ku2lc+zMI&*2uYY30%KimjBgfeSJhK|dj6YqRg!`~#T{I#6dolxM6Z*QSF?OutioP|JVSd` zt2SdJ`yj?N##FnD7eNFT{R}kRwc&A3zR>}bbU=YRKbc%scXRTdLKNW;wvg|+yPp|~i}SnG2M^o-IUN!Pp!2N8u7)GxtV z1Yn-#6{EX_af>c|=|C1owHyrTpgBeuMzk$n4qP(*_-YA% zLHIu7$=R1$Np;B5KbORqACmIct(593{yEkNW(Ezru5Ra|+kgQM@)jBiueLJ>oMEcB z=p2KaD(-`>!Qff8GQSq={etP8BJUioR-ld}(Xj w+KRT8wZU-}l@${c9S?%&F#kt0Cao+omUCw}H_mIC({$g=^3c?#8I2Y4ANcyM?EnA( literal 66075 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 GSNt6={al3r From c8356e8cbbfae1ffdc02297d5b5d017a0317d8cb Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 09:02:08 +1000 Subject: [PATCH 227/243] Remove old keyvalue --- fgd/point/comp/comp_vactube_spline.fgd | 1 - 1 file changed, 1 deletion(-) diff --git a/fgd/point/comp/comp_vactube_spline.fgd b/fgd/point/comp/comp_vactube_spline.fgd index 121b19fa9..1c0d414b7 100644 --- a/fgd/point/comp/comp_vactube_spline.fgd +++ b/fgd/point/comp/comp_vactube_spline.fgd @@ -16,7 +16,6 @@ ] segments(integer) : "Segments" : 2 : "Number of nodes to generate for this. Higher values make smoother tubes, but produce more faces." collisions(boolean) : "Enable Collisions" : 1 : "Should a collision mesh should be generated?" - usebezier(boolean) : "Use Bezier Curve Generation" : 0 : "Should this tube be generated as a bezier curve?" positioninterpolator[engine](integer): "Type": 1 positioninterpolator(choices) : "Type" : 1 : "How to interpolate the tube. Spline uses a spline curve, which smoothly blends between points. Bezier is an alternate smooth curve, but the visual in Hammer will be incorrect." = [ From b35995aefaad5bb6426633db274c3929e085f4d0 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 09:07:34 +1000 Subject: [PATCH 228/243] Remove comp_vactube_beizer --- fgd/point/comp/comp_vactube_beizer.fgd | 48 -------------------------- 1 file changed, 48 deletions(-) delete mode 100644 fgd/point/comp/comp_vactube_beizer.fgd diff --git a/fgd/point/comp/comp_vactube_beizer.fgd b/fgd/point/comp/comp_vactube_beizer.fgd deleted file mode 100644 index 91f879e79..000000000 --- a/fgd/point/comp/comp_vactube_beizer.fgd +++ /dev/null @@ -1,48 +0,0 @@ -@MoveClass base(StaticTargetname, Angles) - sphere(radius) - animator() keyframe() - iconsprite("editor/comp_prop_rope") - appliesto(srctools) -= comp_vactube_bezier: "Constructs a custom vactube model, using a set of path points as bezier keyframes. Config values are only read from the starting point" - [ - nextkey(target_destination) : "Next Rope" : : "Name of the next bezier node." - opaque(boolean) : "Opaque Tube" : 0 : "Whether the tube should be glass, or the black opaque version." - - skin[engine](integer) : "Act as Junction" : 0 - skin(choices) : "Mode" : 0 : "Whether this will link to comp_vactube_junctions, or just be a static prop." = - [ - 0: "Static Prop" - 1: "Vactube Junction" - ] - segments(integer) : "Segments" : 2 : "Total number of nodes per curve." - collisions(boolean) : "Enable Collisions" : 1 : "Should a collision mesh should be generated?" - - // A selection of prop_static's keyvalues, excluding some that are rather irrelevant. - linedivider_staticprop[!engine](string) readonly : "----------------------------------------------------------------------------------------------------------" : "" - - renderamt[since_L4D](integer) : "Alpha" : 255 : "Alpha of the fade, where 0 = fully transparent and 255 = fully opaque." - rendercolor[since_L4D](color255) : "Color (R G B)" : "255 255 255" - - screenspacefade[until_ASW](boolean) : "Screen Space Fade" : 0 : "The method by which the fading distance should be determined. If 'No', the fade distances is the distance from the player's view to the object, in inches. " + - "If 'Yes', the fade distance is the size of the object onscreen, in pixels." - fademindist(float) : "Start Fade Dist/Pixels" : -1 : "Distance at which the prop starts to fade (<0 = use fademaxdist). If 'Screen Space Fade' is selected, this represents the number of pixels wide covered by the prop when it starts to fade." - fademaxdist(float) : "End Fade Dist/Pixels" : 0 : "Maximum distance at which the prop is visible (0 = don't fade out). If 'Screen Space Fade' is selected, this represents the *minimum* number of pixels wide covered by the prop when it fades." - fadescale(float) : "Fade Scale" : 1 : "If you specify a fade in the worldspawn, " + - "or if the engine is running under dx7 [hl2/ep1/portal] or dx8 [ep2/tf], " + - "then the engine will forcibly fade out props even if fademindist/fademaxdist " + - "isn't specified. " + - "This scale factor gives you some control over the fade. " + - "Using 0 here turns off the forcible fades. " + - "Numbers smaller than 1 cause the prop to fade out at further distances, " + - "and greater than 1 cause it to fade out at closer distances." - - disableshadows(boolean) : "Disable Shadows" : 0 - disableselfshadowing(boolean): "Disable Self-Shadowing": 0 - disablevertexlighting(boolean) : "Disable Vertex lighting" : 0 : "Disable per-vertex lighting on this prop." - - drawinfastreflection[since_L4D](boolean) : "Render in Fast Reflections" : 0 : "If enabled, causes this entity/prop to to render in fast water reflections (i.e. when a water material specifies $reflectonlymarkedentities) and in the world impostor pass." - enablelightbounce[since_CSGO](boolean) : "Enable Bounced Lighting" : 0 : "Whether VRAD should create indirect lighting from this prop." - - movespeed(integer) readonly: "Speed (unused)" : 1 : "This needs to be greater than zero to show the preview lines." - positioninterpolator(integer) readonly : "Rope Mode" : 1 : "Needs to be set to '1' to produce the spline curve preview." - ] From e5d1535622fdb1e1c5381790bd20542e23e53161 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 09:07:34 +1000 Subject: [PATCH 229/243] Remove comp_vactube_beizer, again. --- transforms/geocable.py | 1 - 1 file changed, 1 deletion(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index 93de63295..b91b16744 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -1390,7 +1390,6 @@ async def comp_prop_rope(ctx: Context) -> None: ctx.vmf.by_class['comp_prop_rope'], ctx.vmf.by_class['comp_prop_cable'], ctx.vmf.by_class['comp_vactube_spline'], - ctx.vmf.by_class['comp_vactube_bezier'], ): ent.remove() conf = Config.parse(ent, name_to_segprops_set) From 3b1806d7dfa336fddabba98ef0d6af645fc2a86b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 09:18:06 +1000 Subject: [PATCH 230/243] Add to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a790894b..9436e30ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # Version (dev) * #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. * Added `comp_case`, a version of `logic_case` that is collapsed into callers like `comp_relay`. +* Vactube props can now be generated with glass/frames seperately, and with bezier curves instead of catmul-romm. (Authored by [asd417](https://github.com/asd417)) * Added `--verbose` parameter, for showing DEBUG messages. * Added `--regenerate` parameter, to force all models to be regenerated from scratch. * Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. From 9c783c6b59a427c61db725a1f0203ec95b097073 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 10:05:28 +1000 Subject: [PATCH 231/243] The filesystem doesn't need to be checked every time. --- src/hammeraddons/bsp_transform/packing.py | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/hammeraddons/bsp_transform/packing.py b/src/hammeraddons/bsp_transform/packing.py index ec1f5672a..462aaa7f0 100644 --- a/src/hammeraddons/bsp_transform/packing.py +++ b/src/hammeraddons/bsp_transform/packing.py @@ -196,16 +196,6 @@ def comp_pack_rename(ctx: Context): name_dest = ent['filedest'] file_type_name = ent['filetype'] - try: - file = ctx.sys[name_src] - except FileNotFoundError: - LOGGER.warning( - 'File cannot be loaded to pack! \n{} -> {}', - name_src, - name_dest, - ) - continue - try: res_type = PACK_TYPES[file_type_name.casefold()] except KeyError: @@ -219,7 +209,17 @@ def comp_pack_rename(ctx: Context): try: data = file_data[name_src] except KeyError: - with ctx.sys, ctx.sys.open_bin(file) as f: - data = file_data[name_src] = f.read() + try: + file = ctx.sys.open_bin(name_src) + except FileNotFoundError: + LOGGER.warning( + 'File cannot be loaded to pack! \n{} -> {}', + name_src, + name_dest, + ) + continue + with file: + data = file_data[name_src] = file.read() + LOGGER.info('Force packing "{}" as "{}"...', name_src, name_dest) ctx.pack.pack_file(name_dest, res_type, data) From 769cad514f83299884ddc71bb9f9011154dfa661 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Thu, 21 Sep 2023 10:13:37 +1000 Subject: [PATCH 232/243] Automatically pack extra files when comp_pack_rename is used with models --- CHANGELOG.md | 1 + fgd/point/comp/comp_pack_rename.fgd | 2 +- src/hammeraddons/bsp_transform/packing.py | 29 ++++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9436e30ab..d6894c5d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. * Allow `comp_vactube_start` to be set to have a timer which starts disabled. * If required, add various QC flags like `$mostlyopaque` to propcombined props. +* `comp_pack_rename` will now automatically also include the auxiliary `.mdl` files. * Limit the size of propcombined groups to avoid hitting vertex limits. * Weapon scripts are now packed along with the models/sounds they use. * Prevent automatically packing models specified only as Hammer previews in various entities. diff --git a/fgd/point/comp/comp_pack_rename.fgd b/fgd/point/comp/comp_pack_rename.fgd index a85c2a72a..e3e32d3ab 100644 --- a/fgd/point/comp/comp_pack_rename.fgd +++ b/fgd/point/comp/comp_pack_rename.fgd @@ -10,7 +10,7 @@ filedest(string): "Destination Filename": : "Filename to pack under." filetype[engine](string): "File Type": "GENERIC" : "File type to record it as." - filetype(choices): "File Type": "GENERIC" : "File type to record it as." = + filetype(choices): "File Type": "GENERIC" : "File type to record it as. For Model files, the .vtx/vtx/phy etc files are also packed automatically." = [ "GENERIC": "Generic" "SOUNDSCRIPT": "SoundScript file (add to manifest)" diff --git a/src/hammeraddons/bsp_transform/packing.py b/src/hammeraddons/bsp_transform/packing.py index 462aaa7f0..ce763497a 100644 --- a/src/hammeraddons/bsp_transform/packing.py +++ b/src/hammeraddons/bsp_transform/packing.py @@ -4,7 +4,7 @@ from srctools import Entity from srctools.logger import get_logger -from srctools.packlist import FileType, unify_path +from srctools.packlist import FileType, strip_extension, unify_path, MDL_EXTS from srctools.sndscript import SND_CHARS from hammeraddons.bsp_transform import Context, check_control_enabled, trans @@ -223,3 +223,30 @@ def comp_pack_rename(ctx: Context): LOGGER.info('Force packing "{}" as "{}"...', name_src, name_dest) ctx.pack.pack_file(name_dest, res_type, data) + + if res_type is FileType.MODEL: + # Pack additional files. + name_src_stem = strip_extension(name_src) + name_dest_stem = strip_extension(name_dest) + for ext in MDL_EXTS: + if ext == '.mdl': # TODO use MDL_EXTS_EXTRA + continue + + name_add = name_src_stem + ext + + try: + data = file_data[name_add] + except KeyError: + try: + file = ctx.sys.open_bin(name_add) + except FileNotFoundError: + # Optional. + continue + with file: + data = file_data[name_add] = file.read() + + LOGGER.info( + 'Force packing "{}" as "{}{}"...', + name_add, name_dest_stem, ext, + ) + ctx.pack.pack_file(name_dest_stem + ext, FileType.GENERIC, data) From 3dc0ac6fb577b1f71634851efb6216b6cd4cfd5f Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 15 Oct 2023 09:43:20 +1000 Subject: [PATCH 233/243] Fix spurious warning logic --- transforms/geocable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transforms/geocable.py b/transforms/geocable.py index b91b16744..8d8c85769 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -1253,7 +1253,7 @@ async def compile_rope( break skins.append(skin_str) - if any(node.config.vac_separate_glass for node in nodes): + if any(node.config.vac_separate_glass is VactubeGenType.SEPARATE for node in nodes): LOGGER.warning( "Vactube at {} is requesting separate models, which is not allowed for " "prop_dynamic. Ignoring.", From fedef49729072ef64fd52e5c532d9da4131dce0b Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Sun, 15 Oct 2023 09:49:57 +1000 Subject: [PATCH 234/243] Import this from a public location --- src/hammeraddons/bsp_transform/packing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hammeraddons/bsp_transform/packing.py b/src/hammeraddons/bsp_transform/packing.py index ce763497a..f4c524369 100644 --- a/src/hammeraddons/bsp_transform/packing.py +++ b/src/hammeraddons/bsp_transform/packing.py @@ -4,7 +4,8 @@ from srctools import Entity from srctools.logger import get_logger -from srctools.packlist import FileType, strip_extension, unify_path, MDL_EXTS +from srctools.mdl import MDL_EXTS +from srctools.packlist import FileType, strip_extension, unify_path from srctools.sndscript import SND_CHARS from hammeraddons.bsp_transform import Context, check_control_enabled, trans From 2e8f8bbadc7c8c528d00d85290bb5e80de9d890a Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 23 Oct 2023 09:03:16 +1000 Subject: [PATCH 235/243] Upgrade vactube weights calculation, to allow for decimal weights --- transforms/vactubes/objects.py | 37 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/transforms/vactubes/objects.py b/transforms/vactubes/objects.py index 217b1acc7..8ac8655fe 100644 --- a/transforms/vactubes/objects.py +++ b/transforms/vactubes/objects.py @@ -1,7 +1,8 @@ """Handles configuration for the objects appearing inside vactubes.""" -from typing import Optional, Tuple, List, Dict +from typing import Optional, Tuple, List, Dict, Union from typing_extensions import TypeAlias from collections import defaultdict +from fractions import Fraction import os.path import math @@ -41,7 +42,7 @@ def __init__( model_vac: str, model_drop: Optional[str], offset: Vec, - weight: int=1, + weight: Union[Fraction, int] = 1, skin_tv: int=0, skin_drop: int=0, skin_vac: int=0, @@ -83,21 +84,33 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]] """ cube_objects: Dict[Tuple[str, str, int], VacObject] = {} vac_objects: Dict[str, List[VacObject]] = defaultdict(list) + # To allow decimal weights, parse them as fractions, then multiply them all by every denominator. + # That'll cancel out the fraction, making them all integer. We then compute the common multiple + # and reduce down. + group_multipliers: dict[str, int] = defaultdict(lambda: 1) for i, ent in enumerate(vmf.by_class['comp_vactube_object']): offset = Vec.from_str(ent['origin']) - Vec.from_str(ent['offset']) + group = ent['group'] + try: + weight = Fraction(ent['weight', '1']) + except ValueError: + weight = Fraction(1) + else: + group_multipliers[group] *= weight.denominator + obj = VacObject( f'obj_{i:x}', - ent['group'], + group, ent['model'], ent['cube_model'], offset, - srctools.conv_int(ent['weight']), + weight, srctools.conv_int(ent['tv_skin']), srctools.conv_int(ent['cube_skin']), srctools.conv_int(ent['skin']), ) - vac_objects[obj.group].append(obj) + vac_objects[group].append(obj) # Convert the ent into a precache ent, stripping the other keyvalues. mdl_name = ent['model'] ent.clear() @@ -107,7 +120,7 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]] if obj.model_drop: cube_objects[ - obj.group, + group, obj.model_drop.replace('\\', '/'), obj.skin_drop, ] = obj @@ -116,11 +129,15 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]] # Each group is the same, so it can be shared among them all. codes = {} for group, objects in sorted(vac_objects.items(), key=lambda t: t[0]): - # First, see if there's a common multiple among the weights, allowing - # us to simplify. - multiple = objects[0].weight + if (group_mult := group_multipliers[group]) != 1: + for obj in objects: + obj.weight *= group_mult + # The fractions should be integral now. + + # Now see if there's a common multiple among the weights, allowing us to simplify. + multiple = int(objects[0].weight) for obj in objects[1:]: - multiple = math.gcd(multiple, obj.weight) + multiple = math.gcd(multiple, int(obj.weight)) if multiple > 1: LOGGER.info('Group "{}" has common factor of {}, simplifying.', group, multiple) code = [] From 486c2259811636f5f391587ea6cb6c6e46416136 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Mon, 23 Oct 2023 09:13:32 +1000 Subject: [PATCH 236/243] Replace HTML to CSS --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 57c0774ac..858283438 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -

- Hammer Addons +
+ Hammer Addons

Teamspen's Hammer Addons

From b1afe342e459cee156de9ea277dfe7e770d592f9 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 24 Oct 2023 14:50:18 +1000 Subject: [PATCH 237/243] Extract parallel transport calculation to a common module --- src/hammeraddons/splines.py | 21 +++++++++++++++++++++ transforms/geocable.py | 12 ++---------- 2 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 src/hammeraddons/splines.py diff --git a/src/hammeraddons/splines.py b/src/hammeraddons/splines.py new file mode 100644 index 000000000..fafd679b7 --- /dev/null +++ b/src/hammeraddons/splines.py @@ -0,0 +1,21 @@ +"""Math related to spline generation.""" +import math +from typing import Generator + +from srctools import Matrix, Vec + + +def parallel_transport(orient1: Matrix, tanj1: Vec, tanj2: Vec) -> Matrix: + """Given a series of vectors along a path, produce a corresponding set of orientations. + + This allows creating geometry which smoothly follows a curve. + The code is based on the info at: https://janakiev.com/blog/framing-parametric-curves/. + """ + b = Vec.cross(tanj1, tanj2) + if b.mag_sq() < 0.001: + # Aligned, yield the same orientation. + return orient1.copy() + else: + phi = math.acos(Vec.dot(tanj1, tanj2)) + up = orient1.up() @ Matrix.axis_angle(b.norm(), math.degrees(phi)) + return Matrix.from_basis(x=tanj2, z=up) diff --git a/transforms/geocable.py b/transforms/geocable.py index 8d8c85769..78d89ee05 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -24,6 +24,7 @@ from hammeraddons.mdl_compiler import ModelCompiler from hammeraddons.bsp_transform import Context, trans +from hammeraddons.splines import parallel_transport LOGGER = logger.get_logger(__name__) NodeID = NewType('NodeID', str) @@ -871,16 +872,7 @@ def compute_orients(nodes: Iterable[Node]) -> None: while node1.next is not None: node2 = node1.next all_nodes.discard(node2) - tanj1 = tangents[node1] - tanj2 = tangents[node2] - b = Vec.cross(tanj1, tanj2) - if b.mag_sq() < 0.001: - node2.orient = node1.orient.copy() - else: - b = b.norm() - phi = math.acos(Vec.dot(tanj1, tanj2)) - up = node1.orient.up() @ Matrix.axis_angle(b, math.degrees(phi)) - node2.orient = Matrix.from_basis(x=tanj2, z=up) + node2.orient = parallel_transport(node1.orient, tangents[node1], tangents[node2]) node1 = node2 From 5b6a6857a9a4dac3524e531a09837144620713b0 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 24 Oct 2023 14:50:50 +1000 Subject: [PATCH 238/243] Fix some type errors --- transforms/comp_cubemap_parallax.py | 6 +-- transforms/fgd_tweaks/mat_modify_control.py | 4 +- transforms/geocable.py | 46 +++++++++++---------- transforms/vactubes/__init__.py | 2 +- transforms/vactubes/animations.py | 4 +- transforms/vactubes/objects.py | 2 +- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 731d429a1..405773a2f 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -140,12 +140,12 @@ def helper(a, b, x, y): encoded = io.StringIO() material.export(encoded) - ctx.pack.pack_file(name, data = encoded.getvalue()) + ctx.pack.pack_file(name, data=encoded.getvalue().encode('utf8')) for config in parallax_cubemap_configs: if config['used'] == 0: LOGGER.warning( 'No materials found affected by a cubemap within {} units for comp_cubemap_parallax at ({})!', - parallax['radius'], - parallax['origin'], + config['radius'], + config['origin'], ) diff --git a/transforms/fgd_tweaks/mat_modify_control.py b/transforms/fgd_tweaks/mat_modify_control.py index 78b27b6ae..989645bd9 100644 --- a/transforms/fgd_tweaks/mat_modify_control.py +++ b/transforms/fgd_tweaks/mat_modify_control.py @@ -1,6 +1,6 @@ """Tweak material_modify_control to avoid the variable showing up in instances.""" from collections.abc import Iterable -from typing import Counter, Dict, Set +from typing import Counter, Dict, Set, Tuple import srctools.logger from hammeraddons.bsp_transform import trans, Context @@ -68,7 +68,7 @@ def material_has_proxy(mat_name: str) -> bool: continue filter_mat = matmod['materialname'].casefold() - targets: Set[tuple[str, str]] = set() + targets: Set[Tuple[str, str]] = set() ent_materials: Iterable[str] found_count = Counter[str]() for parent in ctx.vmf.search(matmod['parentname']): diff --git a/transforms/geocable.py b/transforms/geocable.py index 78d89ee05..4c816fe41 100644 --- a/transforms/geocable.py +++ b/transforms/geocable.py @@ -1,6 +1,6 @@ """Compile static prop cables, instead of sprites.""" from typing import ( - Optional, List, Tuple, FrozenSet, + Optional, List, Tuple, FrozenSet, Callable, TypeVar, MutableMapping, NewType, Set, Iterable, Dict, Iterator, cast, ) from typing_extensions import Final, Self @@ -266,7 +266,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) v_scale = 1.0 flip_uv = False seg_props = VAC_SEG_CONF_SET - + else: rope_type = RopeType.ROPE # There's not really a vanilla material we can use for cables. @@ -344,7 +344,7 @@ def parse(cls, ent: Entity, name_to_segprops: Dict[str, FrozenSet[SegPropConf]]) vac_separate_glass ) - def coll(self) -> Optional[Self]: + def coll(self) -> Self: """Extract the collision options from the ent.""" return attrs.evolve( self, @@ -728,17 +728,17 @@ def interpolate_rope(node1: Node, node2: Node, seg_count: int) -> List[Node]: ] -def interpolate_bezier(first_node:Node,last_node:Node,curve_segment_count:int) -> List[Node]: +def interpolate_bezier(first_node: Node, last_node: Node, curve_segment_count: int) -> List[Node]: """Interpolate a bezier curve, for better 90 degrees turn.""" # reference: # https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm - points: list[Node] = [] - increment = 1/(curve_segment_count) + points: List[Node] = [] + increment = 1 / curve_segment_count # Only the segment count set in the first spline object counts curve_x = [] curve_y = [] curve_z = [] - curnode = first_node + curnode: Optional[Node] = first_node while curnode is not None: curve_x.append(curnode.pos.x) curve_y.append(curnode.pos.y) @@ -766,51 +766,53 @@ def de_casteljau(t, coefs): beta[k] = beta[k] * (1 - t) + beta[k + 1] * t return beta[0] -def find_all_connected_exclude_firstlast(node: Node): - node_list : List[Node] = [node] - cur_back = node - cur_forward = node +def find_all_connected_exclude_firstlast(node: Node) -> Tuple[List[Node], Optional[Node], Optional[Node]]: + node_list: List[Node] = [node] + cur_back: Optional[Node] = node + cur_forward: Optional[Node] = node - while cur_back.prev != None: + while cur_back.prev is not None: cur_back = cur_back.prev node_list.append(cur_back) assert cur_back.prev != node, 'Circular Node Detected' - while cur_forward.next != None: + while cur_forward.next is not None: cur_forward = cur_forward.next node_list.append(cur_forward) assert cur_forward.next != node, 'Circular Node Detected' - - if cur_back.prev == None: + if cur_back.prev is None: node_list.remove(cur_back) - if cur_forward.next == None: + if cur_forward.next is None: node_list.remove(cur_forward) - return node_list,cur_back,cur_forward + return node_list, cur_back, cur_forward + def interpolate_all(nodes: Set[Node]) -> None: """Produce nodes in-between each user-made node.""" # Create the nodes and put them in a separate list, then add them # to the actual nodes list second. This way sections that have been interpolated # don't affect the interpolation of neighbouring sections. - + seen_bezier_nodes: Set[Node] = set() - seen_bezier_nodes_ignore: Set[Node] = set() + # Add None in here to make code simpler to handle - we always ignore missing endpoints. + seen_bezier_nodes_ignore: Set[Optional[Node]] = {None} segments: List[List[Node]] = [] for node1 in nodes: if node1.next is None or node1.config.segments <= 0: continue - + interp_type = node1.config.interp if interp_type is InterpType.BEZIER: if node1 in seen_bezier_nodes or node1 in seen_bezier_nodes_ignore: continue b_curve, first, last = find_all_connected_exclude_firstlast(node1) - LOGGER.debug("Curve Keyframes: ",first,b_curve,last) + LOGGER.debug("Curve Keyframes: ",first, b_curve, last) seen_bezier_nodes.update(b_curve) - seen_bezier_nodes_ignore.update([first,last]) + seen_bezier_nodes_ignore.add(first) + seen_bezier_nodes_ignore.add(last) node1 = first node2 = last else: diff --git a/transforms/vactubes/__init__.py b/transforms/vactubes/__init__.py index 3b80c8cb9..324a87ed7 100644 --- a/transforms/vactubes/__init__.py +++ b/transforms/vactubes/__init__.py @@ -205,7 +205,7 @@ async def vactube_transform(ctx: Context) -> None: LOGGER.info('Generating animations...') all_anims = animations.generate(sources) # Sort the animations by their start and end, so they ideally are consistent. - all_anims.sort(key=lambda a: (a.start_node.origin, a.end_node.origin)) + all_anims.sort(key=lambda a: (a.start_node.origin, a.end_node.origin if a.end_node is not None else Vec())) anim_mdl_name = Path('maps', ctx.bsp_path.stem, f'vac_anim_{random.randrange(0xffffff):06x}.mdl') diff --git a/transforms/vactubes/animations.py b/transforms/vactubes/animations.py index 57676672e..6ad00fdbb 100644 --- a/transforms/vactubes/animations.py +++ b/transforms/vactubes/animations.py @@ -83,13 +83,13 @@ def __init__(self, start_node: nodes.Spawner) -> None: # The source of the cubes on this animation. self.start_node = start_node # Either the start point, or the splitter to move in the secondary direction. - self.cur_node: Union[nodes.Spawner, nodes.Splitter] = start_node + self.cur_node: nodes.Node = start_node # Once done, this is the ending node so that we can determine if it's a dropper or not. self.end_node: Optional[nodes.Destroyer] = None # When branching, the amount we overshot into this node from last time. self.start_overshoot = 0.0 - def tee(self, split: nodes.Splitter, split_type: DestType, overshoot: float) -> 'Animation': + def tee(self, split: nodes.Node, split_type: DestType, overshoot: float) -> 'Animation': """Duplicate this animation so additional frames can be added. Note: Does not fully copy, the existing frame data is shared so diff --git a/transforms/vactubes/objects.py b/transforms/vactubes/objects.py index 8ac8655fe..158e70fdb 100644 --- a/transforms/vactubes/objects.py +++ b/transforms/vactubes/objects.py @@ -87,7 +87,7 @@ def parse(vmf: VMF, pack: PackList) -> Tuple[int, VacObjectDict, Dict[str, str]] # To allow decimal weights, parse them as fractions, then multiply them all by every denominator. # That'll cancel out the fraction, making them all integer. We then compute the common multiple # and reduce down. - group_multipliers: dict[str, int] = defaultdict(lambda: 1) + group_multipliers: Dict[str, int] = defaultdict(lambda: 1) for i, ent in enumerate(vmf.by_class['comp_vactube_object']): offset = Vec.from_str(ent['origin']) - Vec.from_str(ent['offset']) From 7a2f695e01b5000c2ee46c59561f18123b0af147 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 24 Oct 2023 16:21:58 +1000 Subject: [PATCH 239/243] Swap parallax cubemaps to a class --- transforms/comp_cubemap_parallax.py | 93 ++++++++++++++++++----------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/transforms/comp_cubemap_parallax.py b/transforms/comp_cubemap_parallax.py index 405773a2f..667f4bd4b 100644 --- a/transforms/comp_cubemap_parallax.py +++ b/transforms/comp_cubemap_parallax.py @@ -1,4 +1,7 @@ """Adds keys to generated cubemap materials to map them to the bounds of a cubeoid.""" +from typing import List, Optional, Tuple + +import attrs from srctools import Matrix, Vec, conv_float from srctools.vmt import Material @@ -11,12 +14,45 @@ LOGGER = get_logger(__name__) +# 4x4 matrix, including translation. +Matrix4 = Tuple[ + float, float, float, float, + float, float, float, float, + float, float, float, float, + float, float, float, float, +] + + +def matmul(a: Matrix4, b: Matrix4) -> Matrix4: + """Multiply two 4x4 matrixes.""" + def helper(a: Matrix4, b: Matrix4, x: int, y: int) -> float: + """Calculate a single result value.""" + return a[x] * b[y * 4] + a[x + 4] * b[y * 4 + 1] + a[x + 8] * b[y * 4 + 2] + a[x + 12] * b[y * 4 + 3] + + return ( + helper(a, b, 0, 0), helper(a, b, 1, 0), helper(a, b, 2, 0), helper(a, b, 3, 0), + helper(a, b, 0, 1), helper(a, b, 1, 1), helper(a, b, 2, 1), helper(a, b, 3, 1), + helper(a, b, 0, 2), helper(a, b, 1, 2), helper(a, b, 2, 2), helper(a, b, 3, 2), + helper(a, b, 0, 3), helper(a, b, 1, 3), helper(a, b, 2, 3), helper(a, b, 3, 3), + ) + + +@attrs.define +class Config: + """Configuration of a parallax entity.""" + origin: Vec + radius: float + radius_sqr: float + used: int + obb1: str + obb2: str + obb3: str @trans('comp_cubemap_parallax') def comp_cubemap_parallax(ctx: Context): """Modify cubemap materials to contain parallax information.""" - parallax_cubemap_configs = [] + parallax_cubemap_configs: List[Config] = [] for parallax in ctx.vmf.by_class['comp_cubemap_parallax']: parallax.remove() @@ -32,18 +68,7 @@ def comp_cubemap_parallax(ctx: Context): # ensure bounding box has volume size.max((1.0, 1.0, 1.0)) - # we need a 4-component matrix here because we need to translate - def matmul(a, b): - def helper(a, b, x, y): - return a[x] * b[y * 4] + a[x + 4] * b[y * 4 + 1] + a[x + 8] * b[y * 4 + 2] + a[x + 12] * b[y * 4 + 3] - - return ( - helper(a, b, 0, 0), helper(a, b, 1, 0), helper(a, b, 2, 0), helper(a, b, 3, 0), - helper(a, b, 0, 1), helper(a, b, 1, 1), helper(a, b, 2, 1), helper(a, b, 3, 1), - helper(a, b, 0, 2), helper(a, b, 1, 2), helper(a, b, 2, 2), helper(a, b, 3, 2), - helper(a, b, 0, 3), helper(a, b, 1, 3), helper(a, b, 2, 3), helper(a, b, 3, 3), - ) - + # We need a 4-component matrix here because we need to translate translate1_matrix = ( 1.0, 0.0, 0.0, -origin[0], 0.0, 1.0, 0.0, -origin[1], @@ -72,15 +97,15 @@ def helper(a, b, x, y): 0.0, 0.0, 0.0, 1.0, )) - parallax_cubemap_configs.append({ - 'origin': origin, - 'radius': radius, - 'radius_sqr': radius**2, - 'used': 0, - 'obb1': f"[{scale_matrix[0]:f} {scale_matrix[1]:f} {scale_matrix[2]:f} {scale_matrix[3]:f}]", - 'obb2': f"[{scale_matrix[4]:f} {scale_matrix[5]:f} {scale_matrix[6]:f} {scale_matrix[7]:f}]", - 'obb3': f"[{scale_matrix[8]:f} {scale_matrix[9]:f} {scale_matrix[10]:f} {scale_matrix[11]:f}]", - }) + parallax_cubemap_configs.append(Config( + origin=origin, + radius=radius, + radius_sqr=radius**2, + used=0, + obb1=f"[{scale_matrix[0]:f} {scale_matrix[1]:f} {scale_matrix[2]:f} {scale_matrix[3]:f}]", + obb2=f"[{scale_matrix[4]:f} {scale_matrix[5]:f} {scale_matrix[6]:f} {scale_matrix[7]:f}]", + obb3=f"[{scale_matrix[8]:f} {scale_matrix[9]:f} {scale_matrix[10]:f} {scale_matrix[11]:f}]", + )) cubemap_material_name_pattern = re.compile(r"materials/maps/.*_(-?[0-9]+)_(-?[0-9]+)_(-?[0-9]+)\.vmt") for name in ctx.bsp.pakfile.namelist(): @@ -90,11 +115,11 @@ def helper(a, b, x, y): cubemap_origin = Vec(int(match.group(1)), int(match.group(2)), int(match.group(3))) - best_match = None - best_match_distance_sqr = -1 + best_match: Optional[Config] = None + best_match_distance_sqr = -1.0 for config in parallax_cubemap_configs: - distance_sqr = (cubemap_origin - config['origin']).len_sq() - if distance_sqr > config['radius_sqr']: + distance_sqr = (cubemap_origin - config.origin).len_sq() + if distance_sqr > config.radius_sqr: continue if best_match is None or best_match_distance_sqr > distance_sqr: best_match = config @@ -104,7 +129,7 @@ def helper(a, b, x, y): continue try: - material = Material.parse(ctx.bsp.pakfile.read(name).decode('utf-8'), filename = name) + material = Material.parse(ctx.bsp.pakfile.read(name).decode('utf-8'), filename=name) except Exception as exc: LOGGER.exception( "Could not parse packed cubemap patch material {}!", @@ -128,14 +153,14 @@ def helper(a, b, x, y): ) continue - best_match['used'] += 1 + best_match.used += 1 material.blocks[0].name = 'insert' with material.blocks[0].build() as builder: builder['$envmapparallax']('1') - builder['$envmapparallaxobb1'](best_match['obb1']) - builder['$envmapparallaxobb2'](best_match['obb2']) - builder['$envmapparallaxobb3'](best_match['obb3']) + builder['$envmapparallaxobb1'](best_match.obb1) + builder['$envmapparallaxobb2'](best_match.obb2) + builder['$envmapparallaxobb3'](best_match.obb3) builder['$envmaporigin'](f'[{cubemap_origin}]') encoded = io.StringIO() @@ -143,9 +168,9 @@ def helper(a, b, x, y): ctx.pack.pack_file(name, data=encoded.getvalue().encode('utf8')) for config in parallax_cubemap_configs: - if config['used'] == 0: + if config.used == 0: LOGGER.warning( 'No materials found affected by a cubemap within {} units for comp_cubemap_parallax at ({})!', - config['radius'], - config['origin'], + config.radius, + config.origin, ) From 054d66e9a43d459f213a0ebce9b81c19e715e728 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 1 Nov 2023 12:43:24 +1000 Subject: [PATCH 240/243] Reorganise changelog --- CHANGELOG.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6894c5d2..4ab5053c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,39 @@ # Version (dev) + +## Enhancements * #163: Added `comp_adv_output`, which allows adding a single output with complex behaviour. * Added `comp_case`, a version of `logic_case` that is collapsed into callers like `comp_relay`. * Vactube props can now be generated with glass/frames seperately, and with bezier curves instead of catmul-romm. (Authored by [asd417](https://github.com/asd417)) * Added `--verbose` parameter, for showing DEBUG messages. * Added `--regenerate` parameter, to force all models to be regenerated from scratch. -* Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. * Added ability to specify alt skins when using `comp_prop_cable_dynamic`. -* Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. -* Allow `comp_vactube_start` to be set to have a timer which starts disabled. -* If required, add various QC flags like `$mostlyopaque` to propcombined props. -* `comp_pack_rename` will now automatically also include the auxiliary `.mdl` files. -* Limit the size of propcombined groups to avoid hitting vertex limits. * Weapon scripts are now packed along with the models/sounds they use. -* Prevent automatically packing models specified only as Hammer previews in various entities. -* Fix propcombine sometimes removing collisions entirely from component props. -* Add an option to allow the sources for compiled models to be preserved. * #210: Add `OnFinished` output to `comp_numeric_transition`. -* #10: The center of the axis helper used for sprites can now be clicked on. -* #232: Readd missing `OnFizzled` output on `prop_weighted_cube`. -* Add an option to specify the maximum distance for automatic combined props. + Add an option to specify the maximum distance for automatic combined props. * Allow combining models containing `$collisionjoints`. * Add missing `bunting` keyvalue to `comp_prop_cable`. * Areaportal windows will automatically force the brushes used to nonsolid, and clear some physics data. -* Propcombine will no longer merge props found in different areaportal areas. This allows props on - the outside of a building to be culled when inside, or vice versa. -* Restore missing projected texture shadow keyvalues. +* Propcombine will no longer merge props found in different areaportal areas. This allows props on the outside of a building to be culled when inside, or vice versa. * Automatically set the "transmit to client" flag for `info_target`s used as particle system destinations. * Change `sky_camera` model to be more visible with `tools/toolsskybox` behind it. -* Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. -* Set Precache/OnPostSpawn in generated VScript, to prevent double-firing functions already in Entity Scripts. * Allow physboxes to completely override their mass. * The postcompiler can now automatically handle custom models for various Portal 2 entities. +## Bugfixes +* `comp_pack_rename` will now automatically also include the auxiliary `.mdl` files. +* #10: The center of the axis helper used for sprites can now be clicked on. +* #232: Readd missing `OnFizzled` output on `prop_weighted_cube`. +* Limit the size of propcombined groups to avoid hitting vertex limits. +* Prevent automatically packing models specified only as Hammer previews in various entities. +* Fix propcombine sometimes removing collisions entirely from component props. +* Add an option to allow the sources for compiled models to be preserved. +* Improve description of `wait` and `OnTrigger` options in `trigger_multiple`. +* Fix #192: Use both specified model and cube type field to find matching cubes for vactubes. +* Restore missing projected texture shadow keyvalues. +* Fix `comp_precache_sound` not handling sound characters at the start of raw filenames. +* Allow `comp_vactube_start` to be set to have a timer which starts disabled. +* If required, add various QC flags like `$mostlyopaque` to propcombined props. +* Set Precache/OnPostSpawn in generated VScript, to prevent double-firing functions already in Entity Scripts. -------------------- From b2b0ce55aa91c016e18b2b37555948a598b62f1c Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 1 Nov 2023 13:05:18 +1000 Subject: [PATCH 241/243] Bump srctools version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 664bb514f..a7f87fc30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ attrs >= 21.4.0 typing_extensions >= 4.2.0 -srctools >= 2.3.14 +srctools >= 2.3.15 trio >= 0.20.0 trio-typing >= 0.7.0 pyinstaller >= 5.12.0 From d27c86f2abd025a6982964f35f4def86b3525903 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 1 Nov 2023 13:30:42 +1000 Subject: [PATCH 242/243] Ensure all hammeraddons and srctools modules gets included in the build --- postcompiler.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/postcompiler.spec b/postcompiler.spec index f5b2dc2b2..215a0f047 100644 --- a/postcompiler.spec +++ b/postcompiler.spec @@ -1,6 +1,7 @@ """Build the postcompiler script.""" from pathlib import Path +from PyInstaller.utils.hooks import collect_submodules import versioningit # PyInstaller-injected. @@ -38,12 +39,15 @@ a = Analysis( datas=DATAS, hiddenimports=[ # Ensure these modules are available for plugins. - 'abc', 'array', 'base64', 'binascii', 'binhex', 'graphlib', + 'abc', 'array', 'base64', 'binascii', 'graphlib', 'bisect', 'colorsys', 'collections', 'csv', 'datetime', 'contextlib', 'decimal', 'difflib', 'enum', 'fractions', 'functools', 'io', 'itertools', 'json', 'math', 'random', 're', 'statistics', 'string', 'struct', - 'srctools', 'attr', 'attrs', + *collect_submodules('srctools', filter=lambda name: 'scripts' not in name), + *collect_submodules('attr'), + *collect_submodules('attrs'), + *collect_submodules('hammeraddons'), ], excludes=[ 'IPython', # Via trio From 8caff8b71effd752ec6c889474fc42323ca81643 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Wed, 1 Nov 2023 13:47:08 +1000 Subject: [PATCH 243/243] Update to PyInstaller v6, move files to a binaries/ subfolder --- postcompiler.spec | 16 +++++++++++----- requirements.txt | 2 +- src/hammeraddons/__init__.py | 14 ++++++++++++++ src/hammeraddons/postcompiler.py | 10 ++++++---- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/postcompiler.spec b/postcompiler.spec index 215a0f047..2ad857077 100644 --- a/postcompiler.spec +++ b/postcompiler.spec @@ -1,4 +1,5 @@ """Build the postcompiler script.""" +import shutil from pathlib import Path from PyInstaller.utils.hooks import collect_submodules @@ -24,14 +25,9 @@ with open(Path(SPECPATH, 'src', 'hammeraddons', '_version.py'), 'w') as f: f.write(f'__version__ = {version!r}\n') DATAS = [ - (str(file), str(file.relative_to(root).parent)) - for file in (root / 'transforms').rglob('*.py') -] + [ (str(root / 'crowbar_command/Crowbar.exe'), '.'), (str(root / 'crowbar_command/FluentCommandLineParser.dll'), '.'), ] -for src, dest in DATAS: - print(src, '->', dest) a = Analysis( ['src/hammeraddons/postcompiler.py'], @@ -64,6 +60,8 @@ exe = EXE( name='postcompiler', debug=False, bootloader_ignore_signals=False, + # Don't use bin/, in case someone puts this right in a game dir. + contents_directory='binaries', strip=False, upx=True, console=True, @@ -78,3 +76,11 @@ coll = COLLECT( upx=True, name='postcompiler' ) + +# Copy transforms to the same place as the EXE, not into the binaries subfolder. +app_folder = Path(coll.name) +for file in (root / 'transforms').rglob('*.py'): + dest = app_folder / file.relative_to(root) + print(file, '->', dest) + dest.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(file, dest) diff --git a/requirements.txt b/requirements.txt index a7f87fc30..2540481c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,5 @@ typing_extensions >= 4.2.0 srctools >= 2.3.15 trio >= 0.20.0 trio-typing >= 0.7.0 -pyinstaller >= 5.12.0 +pyinstaller >= 6.1.0 versioningit >= 2.1.0 diff --git a/src/hammeraddons/__init__.py b/src/hammeraddons/__init__.py index 39d760fba..cf7fbc54c 100644 --- a/src/hammeraddons/__init__.py +++ b/src/hammeraddons/__init__.py @@ -1,4 +1,8 @@ """Postcompiler logic.""" +from pathlib import Path +import sys + + try: from ._version import __version__ except ImportError: @@ -8,3 +12,13 @@ import sys as _sys del _sys.modules[_version.__name__] # type: ignore # noqa del _version, _sys # type: ignore # noqa + + +try: + # PyInstaller sets this attribute. + BINS_PATH = Path(sys._MEIPASS) # noqa + FROZEN = True +except AttributeError: + # Root directory is up thrice from postcompiler.py. + BINS_PATH = Path(sys.argv[0], '..', '..', '..').resolve() + FROZEN = False diff --git a/src/hammeraddons/postcompiler.py b/src/hammeraddons/postcompiler.py index dbc55db08..04a8ca007 100644 --- a/src/hammeraddons/postcompiler.py +++ b/src/hammeraddons/postcompiler.py @@ -14,18 +14,20 @@ from typing import Dict, List, Optional from collections import defaultdict from logging import FileHandler, StreamHandler -import math -import shutil import argparse +import math import os import re +import shutil from srctools import __version__ as version_lib, conv_bool from srctools.bsp import BSP, BSP_LUMPS from srctools.filesys import ZipFileSystem from srctools.packlist import PackList -from hammeraddons import __version__ as version_haddons, config, propcombine, mdl_compiler +from hammeraddons import ( + BINS_PATH, __version__ as version_haddons, config, mdl_compiler, propcombine, +) from hammeraddons.bsp_transform import run_transformations from hammeraddons.move_shim import install as install_depmodule_hook @@ -246,7 +248,7 @@ async def main(argv: List[str]) -> None: if 'CROWBAR_LOC' in os.environ: crowbar_loc = Path(os.environ['CROWBAR_LOC']).resolve() else: - crowbar_loc = Path(sys.argv[0], '../Crowbar.exe').resolve() + crowbar_loc = Path(BINS_PATH, 'Crowbar.exe').resolve() else: crowbar_loc = None