Standin-style CP with overtime

Standin-style CP with overtime v1

nᵗʰSonata

Takes way to long to make and update maps
aa
Jun 11, 2015
433
436
Standin-style CP with overtime - Standin-style 3CP domination with working overtime

WHAT:
This is logic for 3CP Standin-style domination (one team wins by capping and holding all three points) with support for overtime mechanics, which enables when a team caps their third control point while the opposing team is contesting one or both of their other points.
It contains: Three control points with standard I/O, plus special I/O to enable overtime; base logic for Standin-style CP; logic for overtime and spawn times; and some geometry for organization. All of this is grouped together in handy user visgroups.

WHY:

Standin-style CP doesn't enable overtime by default, and this tends to confuse people sometimes. Also, with the "Rule of Threes" contest going on, I expect people might be wanting to make a Standin-style map for the contest; I've been sitting on this logic for a while, so I thought now would be a good time to release it to the public.

HOW DO I ADD THIS TO MY MAP?:
Just copy the control points into your map where you want to place them, and use vertex edit to stretch the capture trigger to take up the area you want it to. All the logic for each control point is grouped together for ease of copying, so just copy it all in together. Then copy the base logic and all the overtime logic in, and just place it in some random place in your map (ideally somewhere where you can access it easily in case of troubleshooting).
***!!! IMPORTANT NOTE !!!***
Some of the logic works to change the capture time of each capture point to simulate overtime; each point has a logic_compare named num_cappers_compare_[point] in the center above it which handles this. If you want to change the capture time of the points, make sure to change the number at the end of the output "OnGreaterThan cap_zone AddOutput area_time_to_cap [number]" to the capture time you want, and change the number at the end of the output "OnEqualTo cap_zone AddOutput area_time_to_cap [number]" to 1/6th that number. The capture time you set in the trigger_capture_area entity is technically not needed, as it is instantly overwritten, but I'd still suggest changing it anyways, just to keep track of things easier.

This hasn't been fully tested yet (I wanted to get it out before the custom content deadline for the contest), so if you run into any bugs, or have any questions, please let me know in this thread! Feel free to @ me in the #mapping-help channel in the Discord as well! If you end up using this logic in your map, you don't need to add me as a contributor or anything, but a shout-out would be nice.
 

nᵗʰSonata

Takes way to long to make and update maps
aa
Jun 11, 2015
433
436
HOW DOES ALL THIS WORK?:
Well, I'm glad you asked! Read ahead if you're interested, but feel free to skip this if you aren't. This is pretty complicated, so I'm going to break these into sections and place them under spoilers.
Overtime is usually enabled when one team reaches a win condition (usually, Red's timer running out) while the other team is making progress on something which could prevent that (usually, Blue trying to cap, which would add time to the timer). It is not usually enabled in symmetrical gamemodes when the "winning" team caps the last point they need, and you definitely wouldn't want it in 5CP, but, as stated above, this often confuses people in Standin-style 3CP, especially given that the gamemode is rarely played. So, this exists to manually trigger a "fake" overtime whenever that happens.
Overtime generally consists of three effects. The first, and simplest to simulate, playing an "Overtime!" voiceline from the announcer. The second, and most important, is halting the team from winning. The final effect varies based on gamemode, but the one we're interested in, for CP gamemodes, is that any existing capture progress on a control point which is not currently being capped decays six times faster, so as to prevent stalemates.
As stated before, we want to trigger overtime when a team caps all three points, but at least one is being contested by the other team. First, we keep track of how many points each team has (the "balance") using a math_counter named "balance_counter". For each point, we have an output which adds 1 to this counter if Red caps, and one which subtract 1 if Blue caps. We then have two relays which add or subtract an extra 1 if the point has already been captured. This has the effect that "balance_counter" will always be equal to the number of points Red owns subtracted by the number of points Blue owns (e.g. if Red owns two points and Blue owns one, the value is 1, and if Red owns zero points and Blue owns two, it is -2). We then send this value to a logic_case named "balance_case", which has different outputs depending on the number input. Normally this just sets respawn wave times for each team, but if the value is -3 (Blue owns all points) or 3 (Red owns all points), it also triggers a relay which enables overtime, named "overtime_enable". However, we only want to trigger it if the "losing" team is trying to cap, so we keep it disabled unless at least one point is being contested.
We keep track of how many points are being contested using a math_counter named "contesting_counter" and sending that value to a logic_compare named "contesting_compare", which enables (but does not trigger!) "overtime_enable" if that number is greater than zero, and disables it if the value is equal to zero (no points being contested). How do we figure out how many points are being contested? The capture zone entity, trigger_capture_area, contains three outputs which are useful for this: OnStartCap, which fires when a capper steps on the point while there is no capture progress; OnEndCap, which fires when a point is successfully captured; and OnBreakCap, which fires when a point's capture progress fully runs out. So, for each control point, we have a OnStartCap output which adds 1 to "contesting_counter" and OnEndCap and OnBreakCap outputs which both subtract 1 from the counter. All of this comes together to enable overtime if a) all three points are captured by the same team, and b) at least one point is currently being contested by the other team.
As stated under "How does overtime usually work?", overtime consists of three effects: playing a voiceline, preventing a win condition, and causing capture progress to decay faster. The "overtime_enable" relay does two of these, the voiceline and capture progress. Preventing a win is instead handled in "contesting_compare", the logic_compare which keeps track of how many points are being contested, so we know for sure nobody will win on accident while a point is contested. To play the voiceline, all we do is trigger a logic case named "overtime_playsound", which randomly picks from four ambient_generics named "overtime_sound[1-4]", which each play a different "Overtime!" announcer voiceline to the whole map.
Preventing a win is more complicated: when "contesting_compare" detects that at least one point is being contested, it will send the team_control_point_master (named "master_control_point") the input "AddOutput cpm_restrict_team_cap_win 1", which sets the "Restrict team from winning" keyvalue to "Both", allowing neither team to win by holding all three control points. When it detects that no more points are being contested, it instead sends the input "AddOutput cpm_restrict_team_cap_win 1", which sets the "Restrict team from winning" keyvalue to "Neither", allowing both teams to win, as long as they own all the control points. (This could also be done without AddOuput by restricting both teams from winning by holding all points and instead using a game_round_win entity instead, but this method still works, and it's the method that I happened to use.)
Lastly, we need to make capture progress on the points decay six times faster if they aren't currently being capped. We can't just set the "Time to cap" keyvalue to 1/6th its regular value, as that would also make captures easier. So, we need some way to detect if the point is being capped, and change the time to cap to 1/6th the regular value only if it's not currently being capped. Luckily, trigger_capture_area has some outputs we can use for this. We can't use OnStartCap and OnBreakCap, as those are only fired when capture progress goes up from zero or down to zero, respectively. Instead, we use OnNumCappersChanged, which detects how many cappers are on the point (this does not detect defenders, nor players that cannot cap the point, such as cloaked spies). We send this number to a logic_compare for each point (named "num_cappers_compare_[a-c]"), which sends the corresponding trigger_capture_area the input "AddOutput area_time_to_cap [regular cap time]" if the number is greater than 0, and "AddOutput area_time_to_cap [cap time divided by six]" if it's equal to 0. But wait! There's no way to disable logic_compares! So instead, we set the compare value to -1 by default (so that it will never be triggered), and set it to 0 when overtime is enabled (and trigger a compare, since setting the compare doesn't trigger this on it's own, unlike SetValueCompare).
Overtime can be disabled in two ways: by having capture progress run out on all points (leading to a win), or by a point being captured (continuing the game). If a point is captured, we trigger a relay named "overtime_disable" to disable overtime. If capture progress runs out, it will already cause a win, rendering overtime moot, but we still trigger the relay, just in case. The relay itself is only enabled once overtime has already started. Since winning is already handled, all it does is set the compare value for "num_cappers_compare_[a-c]" back to -1, so that cap time decays at the normal rate again (and, again, trigger a compare since that won't do it automatically).
Congratulations if you managed to read through all of that; it took me an hour and a half to fully write! Hopefully some of you found this interesting.