Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.

Commit c0cc74f

Browse files
committed
Add Dynamic Priority
1 parent 365d30d commit c0cc74f

2 files changed

Lines changed: 221 additions & 0 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Made for the dynamic priority stuff
2+
3+
4+
::DYNPR_SCOPE <- Storage.CreateScope("DYNAMIC_PRIORITY");
5+
6+
7+
function ChangeMode(modename) {
8+
9+
local name = ""
10+
11+
DYNPR_SCOPE.SetInt("current_mode", modename);
12+
13+
switch (modename) {
14+
case 2:
15+
ActivateLow();
16+
ActivateMedium();
17+
name = "High";
18+
break;
19+
20+
case 0:
21+
DeactivateLow();
22+
ActivateMedium();
23+
name = "Medium";
24+
break;
25+
26+
case 1:
27+
DeactivateMedium();
28+
DeactivateLow();
29+
name = "Low";
30+
break;
31+
32+
default:
33+
printl("No setting for " + modename + "!");
34+
printl("Reverting to default!");
35+
DYNPR_SCOPE.SetInt("current_mode", 0);
36+
ChangeMode(0)
37+
return;
38+
}
39+
40+
printl("Changing mode to " + name + "!");
41+
}
42+
43+
function DeactivateLow() {
44+
EntFire("light_dynpr_dynamic_0", "TurnOff", "", 0, null);
45+
EntFire("light_dynpr_static_0", "TurnOn", "", 0, null);
46+
}
47+
48+
function DeactivateMedium() {
49+
EntFire("light_dynpr_dynamic_1", "TurnOff", "", 0, null);
50+
EntFire("light_dynpr_static_1", "TurnOn", "", 0, null);
51+
}
52+
53+
54+
function ActivateLow() {
55+
EntFire("light_dynpr_dynamic_0", "TurnOn", "", 0, null);
56+
EntFire("light_dynpr_static_0", "TurnOff", "", 0, null);
57+
}
58+
59+
function ActivateMedium() {
60+
EntFire("light_dynpr_dynamic_1", "TurnOn", "", 0, null);
61+
EntFire("light_dynpr_static_1", "TurnOff", "", 0, null);
62+
}
63+
64+
65+
66+
function LoadFromMemory() {
67+
68+
local cur_mode = 0;
69+
70+
// 0 Medium
71+
// 1 Low
72+
// 2 High
73+
74+
// This is mixed up as GetInt seems to return 0 if value wasn't initialized
75+
76+
try {
77+
cur_mode = DYNPR_SCOPE.GetInt("current_mode");
78+
} catch (err) {
79+
DYNPR_SCOPE.SetInt("current_mode", 2);
80+
cur_mode = 2;
81+
}
82+
83+
ChangeMode(cur_mode);
84+
}

transforms/dynamic_priority.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
from hammeraddons.bsp_transform import trans, Context
2+
3+
from srctools import VMF, Entity, conv_int, Vec, Output
4+
import logging
5+
6+
LOGGER = logging.getLogger("[Transform][Dynamic Priority]")
7+
8+
9+
@trans("Dynamic Priority")
10+
def dynamic_priority(ctx: Context):
11+
vmf = ctx.vmf
12+
13+
light: Entity
14+
15+
lights = set(vmf.by_class["light_rt_spot"]) | set(vmf.by_class["light_rt"])
16+
17+
added_logic = False
18+
19+
all_lights = lights \
20+
| set(vmf.by_class["light"]) \
21+
| set(vmf.by_class["light_spot"]) \
22+
| set(vmf.by_class["light_environment"]) \
23+
| set(vmf.by_class["light_directional"])
24+
25+
# Each switchable light has a lightstyle assigned to it, we have to check how many of them are used and then use the unused ones
26+
# Check https://github.com/ValveSoftware/source-sdk-2013/blob/39f6dde8fbc238727c020d13b05ecadd31bda4c0/src/utils/vbsp/writebsp.cpp#L982-L1038
27+
# for reference
28+
29+
used_styles = set()
30+
31+
for light in all_lights:
32+
used_styles.add(conv_int(light['style'], default=0))
33+
34+
35+
available_styles = set(range(32, 65 + 32))
36+
37+
available_styles = list(available_styles - used_styles)
38+
39+
lg0_static_style = available_styles[0]
40+
lg0_dynamic_style = available_styles[1]
41+
lg1_static_style = available_styles[2]
42+
lg1_dynamic_style = available_styles[3]
43+
44+
for light in lights:
45+
46+
if conv_int(light["_lightmode"], 2) != 2: # Only Baked Bounce makes sense to have this functionality
47+
continue
48+
49+
if light["targetname", ""] != "":
50+
LOGGER.info(f"Lights with targetnames will be skipped! Light: {light['targetname', '']} at {light.get_origin()}")
51+
continue
52+
53+
dynpr = conv_int(light["_dynamic_priority"], default=-1)
54+
55+
if dynpr not in (0, 1, 2):
56+
LOGGER.warning(f"Invalid _dynamic_priority for light at {light.get_origin()}, skipping...")
57+
continue
58+
59+
60+
if dynpr == 2: # On High, don't change since the light is always dynamic
61+
continue
62+
63+
LOGGER.info(f"Processing light at {light.get_origin()}")
64+
65+
if not added_logic:
66+
added_logic = True
67+
LOGGER.info(f"Additionally, handler logic will be spawned at the position mentioned above!")
68+
AddLogic(vmf, light.get_origin())
69+
70+
71+
light_copy = light.copy()
72+
light_bounce = light.copy()
73+
74+
light_bounce["_lightmode"] = 2 # Ensure Bounce is created
75+
light_bounce["_removeaftercompile"] = 1 # Make VRAD remove this light after compilation
76+
# This trick allows us to create artificial bounce-only lights, because named lights don't get bounce lights
77+
78+
# The thing is, even when switching the modes, bounce lights will remain on, because we're switching between groups and not on/off
79+
80+
light["targetname"] = f"light_dynpr_dynamic_{dynpr}"
81+
82+
if dynpr == 0:
83+
light["style"] = lg0_dynamic_style
84+
elif dynpr == 1:
85+
light["style"] = lg1_dynamic_style
86+
87+
88+
light_copy["targetname"] = f"light_dynpr_static_{dynpr}"
89+
90+
if dynpr == 0:
91+
light_copy["style"] = lg0_static_style
92+
elif dynpr == 1:
93+
light_copy["style"] = lg1_static_style
94+
95+
# Create a static copy
96+
light_copy["_lightmode"] = 0 # Fully static
97+
98+
# We expect the mode to be medium by default, it also limits the amount of lights being switched at once when changing from this mode on map load
99+
if dynpr == 1: # Medium, set the static light to dark
100+
spawnflags = conv_int(light_copy["spawnflags", 0])
101+
spawnflags |= 1 # Sets Initially Dark to True
102+
light_copy["spawnflags"] = spawnflags
103+
104+
elif dynpr == 0: # Low, set the dynamic light to dark
105+
spawnflags = conv_int(light["spawnflags", 0])
106+
spawnflags |= 1 # Sets Initially Dark to True
107+
light["spawnflags"] = spawnflags
108+
109+
vmf.add_ents([light_copy, light_bounce])
110+
111+
112+
113+
def AddLogic(vmf: VMF, pos: Vec):
114+
"""Add the necessary logic to load the saved state on every map load."""
115+
116+
logic_auto = vmf.create_ent(
117+
classname = 'logic_auto',
118+
angles = Vec(0, 0, 0),
119+
spawnflags = 0,
120+
origin = pos
121+
)
122+
# Don't remove on fire, we can fire on every map load, even reloads.
123+
# If the game saves the state of the lights the script won't do anything
124+
# and if it doesn't, this will make sure that they are switched to a correct mode
125+
126+
logic_script = vmf.create_ent(
127+
classname = 'logic_script',
128+
angles = Vec(0, 0, 0),
129+
targetname = '@PC_dynpr',
130+
vscripts = 'dynamic_priority.nut',
131+
origin = pos
132+
)
133+
134+
logic_auto.add_out(
135+
Output("OnMapSpawn", "@PC_dynpr", "RunScriptCode", "LoadFromMemory()")
136+
)
137+

0 commit comments

Comments
 (0)