Skip to content
Server Changelog

Server Update Log

Plugin, map, and rule changes, synced from GitHub. If the fetch fails, the local cache is shown.

Cache time: 2026-10-15 03:31 Using cache View GitHub Source

October 5, 2026 Tank Throw Timing and Rock Protection (ai_tank3 2.3.2 / l4d_rock_lagcomp 2.2-anne)

  • Rocks retain fixed 100 health. Release protection increases by 0.1 seconds at every tier, plus another 0.05 seconds for tiers 1–5: 0.15 / 0.20 / 0.25 / 0.30 / 0.35 / 0.35 seconds for both AI and player Tanks. The plugin default is 0.25 seconds, with no additional health or distance-based protection. Assuming launch speed 800 for tiers 1–5 and 1000 for Neri, near-horizontal travel during protection is about 120 / 160 / 200 / 240 / 280 / 350 units; actual distance depends on launch speed and pitch.
  • Normal AI throws now start only with a visible eligible target 200–450 units away, including both boundaries. The nearest eligible target within that range is selected, even if the chase target is too close or too far. No suitable candidate means no new throw. At release, an in-range target is selected again; if none remains, the original trajectory is retained and the active throw still releases its rock. Forced anti-block/rider throws retain their dedicated rules. Normal bhop strength, throw intervals and damage to survivors are unchanged.
  • Removed the extra upward impulse at the start of a rock throw, together with ai_tank3_jump_rock and its settings in all six tiers. Normal bhop behavior remains unchanged, so Tanks may still throw during a hop; the throw animation no longer triggers an additional jump.
  • Rock protection and historical hits use the same shooter time. Rollback uses outgoing latency plus interpolation, bounded by sv_maxunlag and valid history. The approximate aim ray checks obstructions and damages only the nearest rock.
  • sm_rock_native_shotgun 1 uses actual engine shotgun hit damage instead of assigning all pellets to one aim ray. Shotguns do not use this plugin's position rollback; set it to 0 to restore the old approximation.
  • Recompiled ai_tank3.smx and l4d_rock_lagcomp.smx; updated all six tiers and documentation. Regression checks passed for protection boundaries, rollback history, shotgun damage routing and throw timing. Not deployed to live servers; hit rates and high-latency behavior still need validation.

October 3, 2026 Boomer Model Initialization for Coop Maps in Versus

  • Updated map voting to 0.9.4-custom. Versus mission data is now injected during OnMapInit, with the previous map's injection cache cleared. This fixes Utopia's first chapter returning no mission during model initialization, leaving an incomplete Boomer list that crashes spawning. Both male and female models are retained.
  • The existing l4d2_mapvote_versus_from_coop switch still applies and must be enabled before map loading. Reload the map after updating the plugin; no no_female_boomers override or NAV changes are needed.

October 3, 2026 Coop-Only Map I/O in Versus

  • Added l4d2_coop_versus_compat.smx. Before map entities load, it moves coop-only custom maps' info_gamemode connections from OnCoop / OnCoopPostIO to their versus counterparts, preserving parameters, delays and fire counts without spoofing the game mode or firing global OnCoop events.
  • Automatically detects coop maps without native versus declarations and protects official/native-versus maps. Per-map overrides are in configs/l4d2_coop_versus_compat.cfg; the console command sm_coop_versus_status reports the result. Reload the map after loading the plugin or changing its settings.
  • Updated map voting to 0.9.3-custom; coop mission injection now defaults to all configs (l4d2_mapvote_versus_from_coop 2). The compatibility plugin does not repair NAV, script mode checks or custom finales. Team swaps, chapter transitions and scoring still require testing on each map.
  • Added the server-only sm_mapvote_list command, which reads the actual third-party campaign menu items. On cloud35, Utopia was absent in versus with injection disabled and appeared when enabled; all three chapters loaded with correct chapter/finale detection. Human interaction, natural team swaps and finale completion remain untested. An extra dot before the VPK extension prevented mounting on this server; a temporary alias without extra dots mounted successfully. The server was restored after testing; see docs/coop_versus_cloud35_test_2026-10-03.md.

2026-10-01 Network quality hint: choke detection and loss direction fixes

  • Upgraded network_quality_hint to 1.1.7. The engine's loss/choke values are only a ~1-second moving average, and older versions read them once every 5 seconds, effectively sampling one random second. One- or two-second choke bursts during hordes or entity-heavy fights were almost never caught, and chat warnings also required 3 bad samples in a row, so choke practically never triggered.
  • The plugin now probes every second and judges ping/loss/choke by the average over each check window (nqh_check_interval, default 5 seconds); a timeout counts if it appears anywhere in the window. Thresholds are unchanged: 5% choke for chat warnings, 20% for website incidents.
  • Website reports use each window's average for avg/p95 and the highest 1-second probe for max, so short spikes are no longer lost.
  • Loss is now measured on packets the player sends to the server (the same number status shows). The server cannot tell whether its own packets were dropped, so the server-to-player loss checked by older versions was always 0 and loss warnings and website incidents never fired.
  • Added nqh_report_loss_limit (default 5%): a website incident is recorded only when the window's average loss exceeds it. Chat warnings still use nqh_loss_limit (2%, three windows in a row).
  • The !net measurement note now explains the loss direction. Warning reasons and the hard-coded "[网络]" tag now come from translation files, so all six languages follow the player's language.
  • Recompiled network_quality_hint.smx. Matching NewAnneWeb changes: the admin loss column shows player-to-server loss, and incident rows show their reason (loss / ping / choke / timing_out).

2026-09-26 Item beam settings in the RPG menu

  • The !rpg / !buy main menu has a new "Item beam settings" entry after the hit feedback menu; it opens the !beam settings menu. It only shows in modes that load l4d_random_beam_item, which now registers a library of the same name for rpg.smx to check.
  • Recompiled rpg.smx and l4d_random_beam_item.smx.

2026-09-26 Smaller pain pill beam

  • In data/l4d_random_beam_item.cfg the pain pill beam height goes from 250 to 100 and its width from 15 to 10; the color (green), HDR brightness and see-through-walls halo are unchanged. This is the server default: players who changed length or width with !beam get their percentages applied to the new values.

2026-09-25 Fewer database connections: new anne_db connection hub

  • Added extend/anne_db.smx, loaded on the first line of cfg/generalfixes.cfg. Each database now keeps a single shared MySQL connection, and every plugin gets a copy of it. Each server goes from up to about 22 database connections to 3 (5 in Anne modes). SourceMod already queues all threaded queries on one database thread, so sharing the connection does not slow anything down.
  • Migrated: l4d_stats, rpg, l4d2_hitsound, l4d2_damage_show, l4d2_scripted_hud, l4d2_blacklist, global_chat, chatlog, lilac, l4d_player_count_unload_mode, l4d_random_beam_item, annehappy_dynamic_ai_difficulty, anne_traitor_quota, spawn_vote_menu, sbpp_main, sbpp_comms, sbpp_checker and sbpp_sleuth. If anne_db is not loaded, each plugin falls back to its own connection as before.
  • A database outage no longer freezes the server: for 30 seconds after a failed connect, requests return immediately instead of waiting on the main thread, and reconnects during gameplay never block the main thread. The MySQL connect timeout in databases.cfg drops from the default 60 seconds to 15.
  • The hub pings each connection every 120 seconds (MySQL wait_timeout is 600), so the first query after an idle period no longer fails. Per-plugin keepalives and per-map reconnects were removed.
  • Fixes: l4d_player_count_unload_mode leaked database connections when several cvars changed in a row; clearing l4d_stats now uses a SourceMod transaction, so other plugins' queries can no longer end up inside it; fixed a race in l4d_stats when reading affected rows.
  • New admin console command sm_annedb_status shows the state of each database connection.
  • The rules for writing database plugins are now in AGENTS.md.

2026-09-25 Player-customizable item beams (l4d_random_beam_item 1.1.0)

  • Standard beam: the old default section of data/l4d_random_beam_item.cfg is now standard (the standard beam: length 100, width 6, HDR 2.0, white, no halo), and each item only lists what differs from it. Every item now has a fixed per-category color instead of a random one. The file is now only the server default: only pain pills (green, length 250, width 15, halo) and gnomes (white, length 30, width 5) are shown by default, as before.
  • Item groups: items are sorted into 14 groups (pain pills, adrenaline, first aid kits, defibrillators, throwables, upgrade packs, tier 1 weapons, tier 2 weapons, special weapons, pistols, melee, ammo piles and upgrades, carryables, other). Groups and their menu order are set with group / groups in the data file.
  • Player settings: !beam opens a menu. It only changes what that player sees:
  • All items: show (default / hide all), length and width (50%–300% of the standard), color (9 choices), halo (on / off).
  • By item: per group, show (shown / hidden), length, width, color and halo. Unset options follow "All items"; groups with their own settings are marked [custom].
  • Quick styles: Bright (length and width 150%, halo on), Subtle (length 50%, width 75%, halo off), All off, Default. They only change "All items"; the numbers are in the styles section of the data file. !beam bright/subtle/off/default works directly, and !beam reset resets everything.
  • Items hidden by default can be turned on: if someone sets, say, adrenaline to shown, the server creates adrenaline beams but only turns them on for the players who asked; everyone else doesn't see them. The beams are removed when no player needs them anymore. Once the server uses l4d_random_beam_item_player_edict_limit edicts (default 1900) no more of these beams are created and the player is told; 0 turns the feature off. Physics props (prop_physics) can't be turned on, there are far too many of them.
  • Saved in MySQL: settings go to the rpg database in databases.cfg, new table beam_item_prefs keyed by Steam2 ID. The plugin creates the table itself and it was added to database.sql. Settings load on join, save 3 seconds after a change, and save again on disconnect. If the database is unavailable, settings last for the current connection and the player is told.
  • How it works: the SendProxy extension (sendproxy2) rewrites each beam's on/off state, length, width, color and halo per player as it is sent to clients; the beams on the server are unchanged. The client reads the color and halo only when it creates a beam, so after a change the beams are turned off for that player for 0.3 seconds and back on. Only values that differ from the server are rewritten, so players on default settings add no extra packing work. Without the SendProxy extension everyone sees the data file and !beam says the feature is unavailable.
  • sm_beamreload keeps the settings of online players by group name.
  • Chat messages and menus are available in Simplified Chinese, Traditional Chinese, English, Japanese, Korean and Vietnamese.
  • Added sm_beam to docs/plugin_commands.md; recompiled l4d_random_beam_item.smx.

2026-09-23 Tank turns around instead of hopping away when its target ends up behind it (ai_tank3 2.3.0)

  • Problem: when a survivor sidestepped the Tank during a direct chase, or it switched to a target behind it, the in-air correction stopped steering as soon as the angle between the Tank's velocity and the target direction went past 89° (the cap in the shared movement code). On landing it then jumped again under the 1.x additive rule: the old velocity was kept in full and only one impulse was added toward the target, so it kept hopping the same way and lost only a few dozen units of speed per hop. It looked like the Tank was staring at you while bunny-hopping backwards several times, and it only turned around once its speed fell below 200 or it hit a wall.
  • Air brake: on a hop started as a direct chase, once the direction it wants to go falls behind it, ai_tank3_bhop_reverse_brake (default 1500 units/s²) brakes the horizontal speed down to run speed. This only slows the Tank and does not change its direction. Before braking, the rest of the route is rechecked at the braked speed; if the landing spot isn't safe (a ledge or pit edge), it doesn't brake.
  • Turnaround hop on landing: if the target is still more than 89° behind when the Tank lands, ai_tank3_bhop_reverse_hop (default 1) turns the jump into a turnaround hop: aimed straight at the target at run speed, with no impulse and no side strafe on this hop. The bhop chain doesn't break, but the built-up speed resets, then builds up hop by hop as usual. The cost of being dodged changes from "running off the wrong way for several seconds" to "losing the built-up bhop speed".
  • Hopping the wrong way is now limited to whatever is left of the current hop (a full hop is about 0.77 s); the Tank no longer keeps hopping backwards. Direct chases with the target within 89° in front (including side strafing and the 45°–60° correction threshold) and path-following bhops are unchanged.
  • Neither setting is tiered by dynamic difficulty; set ai_tank3_bhop_reverse_hop 0 and ai_tank3_bhop_reverse_brake 0 to restore the old behavior. With ai_tank3_log_level 2 the console prints a reverse hop line for in-game testing.
  • Synced docs/annehappy_dynamic_ai_difficulty.md and the Tank scenario guide (docs/ai_tank3_scenarios_2026-09-14.html), and recompiled ai_tank3.smx.

2026-09-23 Pill placement control for the pills modes (confoglcompmod ItemTracking)

  • Normal / hardcore / shotgun pills modes (and WitchParty and the all-Charger mode, which share the normal pills config) now only spawn pills between 30% and 90% map progress: confogl_pills_flow_min 0.3, confogl_pills_flow_max 0.9. This is the flow window upstream ItemTracking already had; the pills modes simply never turned it on. The pill count still follows confogl_pills_limit / mapinfo pain_pills, picked evenly by progress between 30% and 90%.
  • New confogl_pills_flow_max_detour (300 in the pills modes): only pills on the main start-to-end route are kept. The plugin first measures the shortest walkable route from the start to the end saferoom, then checks how much extra walking each pill costs. A pill you pass on the main route costs close to 0; one in a side room, dead end or branch of depth d costs about d. Anything over 300 is removed. This runs before pill spacing and the count limit, so the pills that remain are all picked from spots on the main route.
  • New confogl_pills_flow_fill (1 in the pills modes): when fewer pills than the limit survive, side-room pills are not used to make up the count; instead the missing pills are spawned on the main route between 40% and 80% progress (confogl_pills_flow_fill_min 0.4, confogl_pills_flow_fill_max 0.8, never outside the 30%–90% window above). 40%–80% is split into as many equal parts as pills are missing, and a random progress is picked in each part. The pill goes to a random spot in the main-route nav area (flagged ESCAPE_ROUTE, excluding the start area, saferooms and rescue areas) closest to that progress; the spot must have floor under it, room for the pill, be at least 128 units from any other pill, and lie outside the start/end saferoom radius from mapinfo. Up to 32 nav areas are tried per progress, and if pills are still missing a few more random progress values in 40%–80% are tried. Maps with no original pills get filled too. This step does no pathfinding. Spawned pills are recorded by ItemTracking, so they stay in the same place after a wipe and restart; if the count can't be reached, the log records how many were spawned.
  • More visible pill beams: in data/l4d_random_beam_item.cfg the pain pill beam goes from white and 20 tall to green, 250 tall and 15 wide, with the base halo turned on (the halo can be seen through walls). Anne coop and mutation4 use this file too, so their pill beams change as well.
  • New anne_pill_hint.smx (loaded in normal / hardcore / shotgun pills modes, WitchParty and the all-Charger mode): when the first survivor leaves the saferoom, chat tells everyone how many pills lie on the way and at what progress, e.g. "38%, 52%×2, 71%". Saferoom pills are not counted; progress uses the flow of the pill's nav area, the same measure used when removing pills. The message is available in Simplified Chinese, Traditional Chinese, English, Japanese and Korean.
  • Individual maps can be tuned in mapinfo with pillflow_min, pillflow_max, pillflow_max_detour, pillflow_fill, pillflow_fill_min and pillflow_fill_max; confogl_pills_flow_visualize 1 removes nothing and only glows the result (white = kept, green = spawned to fill, red = outside the progress window, magenta = off the main route, orange = too close to the previous pill, cyan = over the count). Finale maps skip these filters by default (confogl_pills_flow_finale 0).
  • Recompiled confoglcompmod.smx; compiled the new anne_pill_hint.smx.

2026-09-23 Tighter Charger mid-air turning (ai_charger3 1.0.1.18)

  • Air turn budget: new ai_charger3_air_turn_budget. Each time the Charger leaves the ground it records its takeoff direction, and for the rest of that hop its mid-air corrections can only swing its velocity this many degrees away from it; once the budget is spent it flies the hop out on momentum. Approach air correction and the pre-charge air tracking share the same budget. The hop before a charge used to re-aim at your predicted position every 0.05 s with a 1° minimum threshold, so sidestepping after seeing it jump barely worked; stepping outside the budget now makes it whiff. The six tiers are 12 / 16 / 20 / 30 / 40 / 45 degrees, and the swing of side strafing (the S-curve) is capped by the same budget.
  • The more it turns, the less it gains: every hop's bhop impulse is scaled to impulse × (1 − θ / 90), with θ above 90 treated as 90 (no gain). θ is the takeoff turn, the angle between its ground velocity before the jump and the new hop direction; for the hop taken right after landing it also adds the largest angle the previous hop's air corrections reached away from that hop's takeoff direction. Weaving, tracking and accelerating all at once, or snapping to a new direction the moment it lands, is no longer free. The retreat hop in melee baiting is essentially a U-turn, so after scaling it mostly keeps only run speed.
  • Final hop before a charge: it used to add the full bhop impulse outright, so even a hop taken straight from running got a whole extra impulse. It now follows the same rules as every other hop (first hop scaled by ai_charger3_bhop_first_hop_ratio, then scaled by the turn angle). It reaches charge range in one hop less often and runs the last stretch more often.
  • Top-tier speed caps: ai_charger3_bhop_max_speed 600 → 500 on the extreme tier (level5) and 700 → 600 on the Neri tier (level6).
  • Debug logging: the Path / Direct hop logs always showed landing hop as 0 because it was read after the landing flag had been consumed. Fixed; all four hop types now log the turn angle counted for the hop and the resulting impulse scale.
  • Recompiled ai_charger3.smx.

2026-09-22 Smokers can no longer pull from ladders on any tier

  • Fixed Smokers on the Neri difficulty being able to fire from a ladder and, once they had someone, keep climbing while dragging the victim up. Neri turns on l4d_air_abilities_patch_neri, which removes the two vanilla "must be on the ground" checks on the tongue so Smokers can fire and pull in mid-air: the fire condition in CTongue::IsAbilityReadyToFire, and the "break when leaving the ground" rule in CTongue::OnUpdateAttachedToTargetState. Climbing a ladder also counts as not on the ground, so Smokers could fire from ladders too, and the tongue no longer broke once attached. Vanilla roots a pulling Smoker by capping GetRunTopSpeed at 1, but ladder climbing uses a fixed climb speed that ignores that cap, so the AI's ladder climb simply carried on and hauled the victim upward; the l4d2_ai_ladder_boost climb speed-up multiplied that speed further.
  • An air pull is an air pull, not a ladder pull: l4d_air_abilities_patch now applies this on every tier, whether or not the Neri patches are on. While a Smoker is on a ladder its tongue is always treated as not ready, so neither AI nor human Smokers can fire, and a Smoker that ends up climbing while holding a victim lets go immediately (through the same ReleaseTongueVictim path as the vanilla break, with the normal cooldown). Vanilla already requires standing on the ground to fire, but a Smoker that has just mounted the foot of a ladder with its feet still on the floor counts as grounded; that case is now blocked too. Neri's mid-air firing and pulling are unaffected.
  • Truly rooted while pulling: vanilla's "a pulling Smoker can't move" only caps the Smoker's run speed at 1 and strips jump and duck while its tongue is out (CTerrorGameMovement::CheckParameters); the forward key and move input survive. Mounting a ladder only needs move input toward a ladder within 2 units, so an AI that grabs someone from the ground just as it reaches the foot of a ladder, with its pathing still pushing into the ladder, mounts it and climbs at the fixed ladder speed (vanilla then breaks the tongue for leaving the ground; on Neri the Smoker dragged the victim all the way up). Now, from the moment the tongue goes out until it is back (m_tongueState non-zero, the same condition vanilla roots on), every Smoker's movement keys and move input are cleared, so it really cannot move and never gets onto the ladder, and the pull is not wasted by a forced release; letting go on a ladder stays as a fallback. ai_smoker3 also stops bunny hopping and mid-air velocity correction during this window, since both set velocity directly and would bypass the root.
  • ai_smoker3_jump_pull decided "in the air" by the absence of a ground entity, which is also true on ladders, so the AI kept holding the tongue button while climbing. Ladders are now excluded.
  • Added a DHooks function definition for CTongue::IsAbilityReadyToFire to l4d2_air_data.txt; recompiled l4d_air_abilities_patch.smx and ai_smoker3.smx (1.0.1.5).

2026-09-22 Tank switches targets more readily, deprioritizes biled survivors, back fist spares the downed (ai_tank3 2.2.0)

  • Fixed the Tank often locking onto one survivor and rarely switching. Three layers stacked up: travel estimates were refreshed only when the game called ChooseVictim, one survivor per call, so a survivor walking toward the Tank still carried an older, larger estimate; the estimate was the upper bound of the matching bucket, so the step from the 1.15× to the 1.5× bucket alone was 30%; and to absorb that noise the switch threshold was set high (25% lower score, at least 150 lower, 2 s apart), which almost never triggered while survivors stood within a couple hundred units of each other.
  • Estimates are now refreshed independently from OnPlayerRunCmd: each Tank revisits every survivor every 0.5 s (1 s for unreachable ones), with at most one estimate per tick across the plugin. After a bucket matches, the interval between the last failing and the first passing bucket is bisected down to 64 units and its midpoint is used. Score = current straight-line distance × detour ratio, so it follows the real distance between refreshes.
  • Switching within the same tier now needs a new score ≤ current × 0.85 (ai_tank3_target_switch_ratio), at least 75 lower (new ai_tank3_target_switch_gain), and 1 s since the last switch (ai_tank3_target_commit_time); when the new score is at most half the current one, the 1 s wait is skipped (new ai_tank3_target_decisive_ratio, 0 disables). 75 exceeds 64, the upper bound of the combined estimate error of two survivors, so estimate noise alone cannot trigger a switch. The freeze near and on ladders is unchanged.
  • The victim-scoring multiplier for survivors covered in Boomer bile goes from 1.25 to 2.0. At equal path distance the Tank now walks around a biled survivor and only chases one when nobody else is nearby.
  • Note on the difference from the config: the Tank entry in l4d_target_override is order 1,2 plus voms2 0, which drops biled survivors from the candidate list entirely, so order 2 can never match. The plugin keeps a soft penalty instead, so a Tank standing next to nothing but biled survivors still has someone to chase.
  • docs/ai_tank3_scenarios_2026-09-14.html is no longer labelled a historical comparison: the “survivor on a platform vs survivor on the ground” scenario and the victim-picker calculator now follow the 2.2.0 rules (bisected estimates, bile ×2, the four switch cvars editable on the page), the other scenarios are still drawn from the 2.0 logic, and the “chasing on open ground” and “ledge ahead” cards note what changed in 2.1. The 1.x comparison for biled survivors was also corrected (voms2 0 drops them before ordering, so they never reach order 2). Current values are in docs/annehappy_dynamic_ai_difficulty.md.
  • The back fist no longer finishes off downed or ledge-hanging survivors: while anyone in range is still on their feet, the extra SweepFist only hits those, and it falls back to the downed ones only when everyone nearby is down. The loop previously checked alive/visible/within 128 units, so a teammate lying at the Tank's feet took a free extra hit from every swing (1.x had the same loop).
  • Recompiled ai_tank3.smx.

2026-09-19 Fix dancers getting stuck when someone opens the saferoom door

  • Fixed dancers being left unable to shoot or perform any action after another player left the safe area or opened the saferoom door. When a dance starts, fornite_l4d sets m_hActiveWeapon to -1 and remembers the original weapon entity, while l4d2_med_dynamic deletes every survivor's old medkit and hands out a new one on player_left_start_area. When the deleted item was the very weapon the dancer had hidden, the stored entity reference was already invalid, WeaponUnblock() skipped the restore, and m_hActiveWeapon stayed at -1 forever.
  • fornite_l4d now hooks player_left_start_area and door_open (checkpoint doors only) with EventHookMode_Pre, so every dance ends before the Post handlers that hand out items run.
  • A dance now records the slot of the hidden weapon in addition to its entity. WeaponUnblock() falls back in order: the original weapon entity, then whatever now occupies that same slot, then any carried weapon (only when the player is genuinely empty-handed). Someone who danced while holding a medkit ends up holding a medkit again instead of being switched to their primary. The restore also clears m_flNextAttack, m_flNextPrimaryAttack and m_bInReload, so writing m_hActiveWeapon directly no longer leaves the weapon unable to fire.
  • StopEmote() only restored the camera, the weapon and MOVETYPE_WALK while the emote entity was still valid, and cleared its flags without restoring anything once the entity was gone; TerminateEmote() (used by round_start, connect and disconnect) never restored the movetype at all. Both now call the new RestoreEmoteState(), which clears the parent, camera, weapon and movetype whether or not the emote entity still exists.
  • Added a self-heal: OnPlayerRunCmd restores a player whose dance has ended but whose state was never reverted, so a stuck player no longer has to wait for death or a map change.
  • Recompiled fornite_l4d.smx.

2026-09-19 Fix intermittent 1vht accuracy stats

  • 1v1_skeetstats gated its shot counter behind bPlayerLeftStartArea, which only the player_left_start_area event set and every round_start cleared. Whenever that event was missed, or another round_start arrived after the survivor had already left the saferoom, the whole round recorded zero shots and the accuracy line printed "no shots fired" while the kill and skeet lines, which were never gated, looked normal. Shot counting now asks the director through left4dhooks' L4D_HasAnySurvivorLeftSafeArea(), backed by L4D_OnFirstSurvivorLeftSafeArea_Post.
  • Fixed accuracy going over 100%: hits in player_hurt were counted with no gate while shots in weapon_fire were gated. Both now use the same condition.
  • iClientPlaying was only assigned at round_start and player_left_start_area, so a player who took the survivor slot later in the round was never tracked, and GetCurrentSurvivor() could pick a placeholder bot. It is now re-resolved on use and skips bots.
  • 1vHunters runs as versus with versus_round_restarttimer 2, so round_start wiped every counter before the report's 3 second delay elapsed and the whole block printed zeros or nothing at all. The report is now snapshotted at round_end and the delay shortened to 2 seconds.
  • Recompiled 1v1_skeetstats.smx.

2026-09-19 Fix Hunter turning in mid-air

  • Fixed Hunters being able to re-pounce repeatedly during a pounce and change direction in mid-air. The high difficulty tiers zero z_lunge_interval, z_lunge_cooldown and z_pounce_crouch_delay and enable l4d2_hunter_patch_crouch_pounce 2, so the AI re-fired its lunge every tick while airborne and the engine overwrote its current velocity with a fresh vector. Both ai_hunter_2 and ai_hunter_new now strip the attack button while a pounce is in flight.
  • ai_hunter_2's angleLunge() rebuilds the whole lunge vector from "current position to the target's current position", so firing it mid-flight let the Hunter re-aim and receive a brand new full-speed vector. m_queuedLunge may now only be rewritten for a pounce that just left the ground (within 0.25 seconds). ai_hunter_new received the same guard.
  • Fixed ai_hunter_2 computing ignoreCrouch but never using it: with l4d2_hunter_patch_crouch_pounce 2 the Hunter never ducks, which previously disabled the no-sight pounce queue and the pre-pounce claw entirely.
  • Recompiled ai_hunter_2.smx and ai_hunter_new.smx.

2026-09-17 Restore Tank target-tracking turn thresholds

  • Restored ai_tank3_airvec_modify_degree to 60 / 55 / 50 / 45 / 45 / 45 degrees across the six tiers and ai_tank3_airvec_modify_degree_max to 135 for every tier. Plugin defaults return to 45 / 135 degrees.
  • The shared movement code still caps the allowed direction error at 89 degrees. Path following from 1 degree, refreshed airborne paths, alternating hops, safe drops and difficulty-based rock protection remain in place.
  • Updated the 3D demonstration and parameter documentation; recompiled ai_tank3.smx.

September 17, 2026 Charger Door Breaking (ai_charger3 1.0.1.16)

  • Fixed Chargers getting stuck against ordinary doors without punching when the door blocks sight of their target. While grounded and slowed by an obstacle, they check for an ordinary door within claw range along their movement direction and prioritize a normal claw attack.
  • Walls, players, and other entities still block detection. Saferoom doors, unbreakable doors, and airborne, ladder, stagger, charge, or pin states are excluded.
  • Added door behavior regression checks and recompiled ai_charger3.smx.

2026-09-16 Tank rock protection after release (l4d_rock_lagcomp 2.1-anne)

  • Rock protection after actual release now follows the dynamic difficulty profile: Easy / Normal / Hard / Expert / Extreme / Neri use 0 / 0.05 / 0.10 / 0.15 / 0.20 / 0.25 seconds. This protects the rock entity for both AI and player-controlled Tanks.
  • Added sm_rock_release_godframes (default 0.15; set to 0 for immediate damage after release). Existing sm_rock_godframes 1.7 remains the fallback measured from entity creation if the release callback is missed. The timers are independent, and translucent feedback follows the protection window.
  • Timing boundary regression checks passed; recompiled the Anne version of l4d_rock_lagcomp.smx.

2026-09-16 Tank continuous hops over safe drops

  • For ordinary drops of up to 256 units on a known path, successful safety checks allow the Tank to hop off at speed, retain momentum during the fall and immediately hop again on landing with the full landing impulse.
  • Checks use the longer airtime calculated from the actual drop, covering walls, ceilings and ground support at all four landing corners. Active side hops and jump rocks stay disabled near the edge; deeper drops, ladders, jump gaps and unsafe routes retain their restrictions.
  • Updated the 3D comparison while retaining the early slowdown of version 2.0. Movement regression checks passed; recompiled ai_tank3.smx.

2026-09-15 Tank air tracking and alternating hops (ai_tank3 2.1.0)

  • Refreshes safe path lookahead every 0.05 s in the air, continuing to the next segment after passing the old goal. Target-tracking thresholds are now 10 / 8 / 6 / 5 / 4 / 3 degrees across the six tiers; path following starts at 1 degree, retaining smooth turns and the 89-degree limit.
  • Restores lateral impulse from native left/right inputs. Beyond 600 units, safe direct chases can alternate hop sides at 8 / 10 / 12 / 15 / 18 / 20 degrees by tier. The first side is random; subsequent hops switch sides after a confirmed landing. Set the angle to 0 to disable.
  • Active side hops require a visible target on the same level and a supporting path. Close range, path turns, or blocked side routes restore normal tracking. Turns sweep the actual collision hull along sampled ballistic segments, including ascent, and check gaps and special path segments using the ground below and remaining airtime, preserving ladder handling, speed caps and landing acceleration.
  • Recompiled ai_tank3.smx; added movement regression checks for small target movements, path refresh, blocked-side fallback, alternating sides and terrain rejection.

2026-09-15 AI behavior review fixes

  • Spitter consumes its deferred spit on the first grounded frame. If sight is lost, the ability is not ready, or another branch returns early, the record is discarded instead of triggering an automatic spit on a later hop.
  • Each Tank now has its own budget of up to 3 path lookahead traces per tick. Candidates are checked from near to far along the path, retaining the last reachable point when blocked or out of budget, so distant corners or another Tank cannot consume the budget and erase a usable hop route.
  • Recompiled: ai_spitter_3.smx and ai_tank3.smx.

2026-09-15 Spitter landing spit re-press (ai_spitter_3 3.0.11)

  • Symptom: On the Easy–Expert tiers the AI Spitter almost never got a spit out; it looked like it did not know how to use its ability.
  • Root cause: With ai_spitter3_air_spit 0, an airborne IN_ATTACK is discarded outright — not queued, not deferred — so the only frame it can spit on is the one where FL_ONGROUND is set. Meanwhile the plugin itself re-jumps on every landing frame within ai_SpitterBhopStartDistance (2500 in all three Anne configs), and z_spit_range is far below that, so the entire spit range sits inside the hop radius. One hop stays airborne for sqrt(2 * 56 / sv_gravity) * 2, and TickrateFixes pins sv_gravity to 750, so about 0.77 s — at 100 tick that is 77 airborne frames plus 1 grounded frame, a ground duty cycle of roughly 1/78. Over 98% of frames had the attack button stripped, and the remaining under-2% still had to coincide with the AI's firing moment. The Extreme/Neri tiers were fine only because air spit is on there and they take the buttons |= IN_JUMP jump-spit path, which never checks the ground.
  • Landing re-press: the spit that gets stripped in the air is timestamped (SPIT_OWE_TTL 1 s, slightly longer than one hop's airtime, so only the current hop's owed spit counts). On the first landing frame, if the target is still visible and the ability can still fire, the plugin presses attack for it and skips the jump for that one frame. Only that frame — the next one hops as usual, still within the 0.15 s landing-impulse window, so the hop chain is unbroken and keeps its full impulse. The re-press uses the AI's own aim: the if (buttons & (IN_ATTACK | IN_ATTACK2)) AIPathMovement_Reset(spitter); at the top of the function already disabled path correction for that hop, so AlignFacing no longer overrides facing — the plugin presses the button, it does not aim.
  • Hopping resumes the moment the spit is out: the grounded attack branch used to strip IN_JUMP whenever IN_ATTACK was held, so a Spitter whose AI keeps the button down stuck to the ground. It is now gated on spitReadyToFire() (the m_nextActivationTimer timestamp has passed): the jump is suppressed only while the spit can still come out; once the ability is in its wind-up or on cooldown the held button is ignored and hopping continues.
  • No new cvar and no per-tier values: this is a bug fix, not a nerf. Extreme and Neri have ai_spitter3_air_spit 1 and take the old jump-spit path unchanged.
  • Recompiled: ai_spitter_3.smx.

2026-09-09 Drop new zonemod_anne stripper entities that would block SI

  • Upstream zonemod add:s clips, blockers, solid props, and ladders. Anne did not update the matching nav, so AI SI still path on the old mesh and get stuck on those new entities.
  • Custom maps only: stripped post-2026-05 zonemod-sync ladders/solid props that would block SI from cfg/stripper/zonemod_anne (City 17, No Echo m3, Carried Off, Dark Carnival Remix, Parish Overgrowth, and others). Official c1–c14 nav was already reviewed, so their geometry add:s were restored unchanged. env_physics_blocker and env_player_blocker are kept with BlockType set to survivors only (1).
  • Custom maps still keep filter/modify, item/pill add:s, nav_fixes logic_auto, and older anne clips. Later custom-map mirrors must not copy SI-blocking ladders/solid props without matching nav.

2026-09-09 Tiered Charger bunnyhop nerf (config only, no plugin change)

  • Why only the hops: the Charger runs at 220, exactly like a survivor, so whether it ever gets on top of anyone comes down entirely to bunnyhopping. The hop parameters *are* its pressure. Everything here changes *how* it hops, never *whether* it hops — the Charger still visibly comes at you, it is just easier to shoot and easier to dodge, not a Charger standing around looking stupid. All changes live in the six tiers of dynamic_ai_difficulty.cfg; plugin source and behavior are untouched, and the Neri tier (level6) sits exactly on the plugin defaults, identical to before.
  • Side strafing (best value in the set): ai_charger3_bhop_strafe_mindeg / maxdeg across the tiers 8–18 / 14–28 / 20–38 / 25–46 / 28–52 / 30–55 degrees. Amplitude is tan(random(mindeg, maxdeg)), so 30–55° gives 0.58–1.43 and 8–18° leaves only 0.14–0.32 — the approach path is close to a straight line. It costs the Charger no speed at all; it simply stops weaving out of your line of fire, so good aim kills it on the way in. Paired with ai_charger3_bhop_strafe_mindist 800 / 700 / 600 / 500 / 420 / 400, the low tiers mean "weave only from far away, run straight once inside 800".
  • Where hopping stops: ai_charger3_bhop_min_dist 170 / 150 / 130 / 115 / 105 / 100. Below this the Charger leaves APPROACH for BAIT, i.e. stops hopping. Note that CHARGE_GUARANTEED_MAX_DIST (110) already caps the guaranteed-hit floor, so raising this does not make the Charger commit from 170 — it only makes the last 60 units happen at 220 run speed, which gives the player a visible run-up instead of a hop straight into their face.
  • Blind hopping: ai_charger3_bhop_no_vision 0 / 1 / 1 / 1 / 1 / 1 and ai_charger3_bhop_nvis_maxang 35 / 35 / 50 / 65 / 80 / 90. The easy tier requires line of sight before the Charger may build speed; without vision executeGroundBhop returns early and falls back to native ground movement, so nothing jams the state machine. What this removes is "bursts out from behind a corner you cannot see at 400 speed" — the burst now starts after you have seen it.
  • Air turning: ai_charger3_airvec_modify_min_deg / max_deg 65–70 / 60–74 / 55–78 / 50–83 / 47–89 / 45–89 degrees. Direction correction only applies inside that angle band; raising min means it does not track small dodges, lowering max means it gives up on large deviations. The easy tier keeps only the narrow 65–70° band, so a hop essentially flies where it was aimed and the landing spot is much easier to sidestep. The S-curve's return to path uses AIR_CORRECTION_MIN_TARGET_DEG (1.0) rather than this min, so the strafe curve itself is unaffected.
  • Speed axis trimmed one more notch: ai_ChagrerBhopSpeed 30/40/50/60/80 → 20/30/42/55/70, ai_charger3_bhop_max_speed 400/400/400/500/700 → 330/360/390/450/600, ai_charger3_bhop_first_hop_ratio 0.2/0.3/0.4/0.5/0.6 → 0.0/0.15/0.3/0.45/0.55. A first-hop ratio of 0 on the easy tier means a hop taken straight from running only changes direction and adds no speed. The Neri tier keeps all three.
  • Left alone: ai_charger3_bhop_before_charge stays 1 on every tier. Turning it off makes CHARGING fall back to APPROACH inside the (commitDist, bhopMinDist + 50] tolerance band, which risks visible APPROACH ↔ CHARGING ping-ponging, and the gain is only moving the commit distance from about 150 to about 110. Not worth it — but it is the knob to try first if another notch is wanted.
  • No recompile needed.

2026-09-08 Charger melee backhop normalization (ai_charger3 1.0.1.15)

  • Symptom: while bunnyhopping toward a melee survivor, the Charger would suddenly lose all horizontal speed in mid-air and drop straight down; the retreat hop after landing was also faster than a player can actually jump.
  • Air freeze: entering the melee bait zone from Approach, and non-retreat airborne frames in Bait, zeroed XY velocity with TeleportEntity. Players cannot dump a 400-speed hop in the air, which is why it looked like physics had been turned off. Those writes are gone; the current hop keeps its momentum until landing, then the retreat hop fires on the ground.
  • Retreat hop speed: used to stack survivor chase speed × 1.2 on top of run speed, so a single hop could exceed 400. It now uses the same Path / Direct hop model: keep any existing away-from-target component, dump speed that was flying into the survivor on the turn, add the landing impulse, and clamp to the bhop cap.
  • Recompiled: ai_charger3.smx.

2026-09-08 Charger melee-standoff stutter and endless stalemate fix (ai_charger3 1.0.1.14)

  • Symptom: with the Charger on low health and the survivor holding a melee weapon, the Charger stuttered inside the bait band — dash, hard stop, dash, hard stop — and the standoff could last forever.
  • Direct cause of the stutter: maintainMeleeBaitDistance was a bang-bang controller — full-speed retreat below baitZoneMin, full-speed advance above baitZoneMax, and a 35-unit deadzone in between that zeroed horizontal velocity with TeleportEntity every tick. A 1-unit error and a 30-unit error both used runTopSpeed, so the Charger crossed the whole band in 3-5 ticks and settled into "burst out at full speed, snap to zero, survivor steps in, burst again".
  • Distance control is now hysteresis + proportional speed + minimum dwell: once moving, it travels to the band center before releasing (a direction flip must cross half the band); speed scales with the distance error to that center, reaching full speed only at BAIT_MOVE_SPEED_RAMP, with a 35% floor; a direction is held for at least BAIT_MOVE_HOLD_TIME (0.25s).
  • The deadzone no longer zeroes velocity; it decays it by 0.7 per tick, so the Charger coasts to a stop instead of snapping to one.
  • After a retreat hop lands, a 0.45s lock prevents distance control from immediately turning back in, killing the per-tick fight between "hop out, advance on landing, too close again, hop out".
  • Inverted emergency threshold: the emergency retreat line was hard-coded at melee_range + 20 (90), which lands *inside* the bait band (85-120) — the innermost 5 units of the band were permanently in "emergency", where the retreat hop overrode the deadzone, so the Charger was told to hop again the moment it backed off to 85. The line now sits inside the band (melee_range + min(20, baitZoneMinRange × 0.5) = 77.5), making the full 35-unit band genuinely holdable.
  • The safety-net forced charge never fired: the ai_charger3_bait_max_duration (7s) fallback in isTargetVulnerable hung off the Bait state's getStateDuration, and the state startTime resets on every transitionTo — one retreat hop clears the exit line, and a single Bait → Approach → Bait round trip zeroed the timer, which is why the stalemate could run forever. The stalemate clock is now bound to "target + continuous melee bait" and keeps accumulating as long as two melee-bait moments are less than 2s apart, so Approach ↔ Bait churn no longer resets it.
  • Decision space for a low-health Charger: of the three paths in shouldChargeInMeleeBait, "health above the melee kill line" is always false after the health nerf, and isTargetVulnerable nests the same health check for melee targets, leaving only "the survivor looked away". isTargetVulnerable does carry a generic attack-interval check, but it uses one "next attack > now + 0.5s" threshold for every weapon, so the usable window is (weapon refire delay − 0.5s) — the shorter the swing cycle the narrower it gets, and a fast melee (refire ≤ 0.5s) yields a zero-width window that never fires at all. Added a whiff-punish path using a 0.2s threshold for melee targets, so every melee leaves a real window and a low-health Charger gets an earned opening.
  • Stalemate escalation ladder: past ai_charger3_bait_max_duration it escalates feint → forced charge → target switch. The feint pushes in to draw a swing (handing control back at the emergency line so the normal retreat hop pulls it out), turning the aimless in-out into a deliberate, rhythmic fake that the whiff-punish path then converts. When feints run out it charges anyway — trading a pin for its life beats twitching in place. A charge on cooldown only means "not yet", not "impossible", so it never switches targets just because the ability is down.
  • New cvars:
  • ai_charger3_melee_bait_orbit (default 1): inside the band, circle tangentially instead of standing still, turning "twitching in place" into "circling you for an opening". A blocked orbit direction flips and retries; when both directions are unsafe it holds position rather than handing control back to native pathing (which would walk the Charger straight into the survivor and make things worse). Set 0 to restore standing still.
  • ai_charger3_melee_bait_stalemate_switch (default 0): the last rung — switch to another survivor when the ability is ready but the charge still cannot land (height unreachable / path blocked). Off by default; watch how the rest feels first.
  • ai_charger3_melee_bait_blacklist_dur (default 10.0): how long an abandoned target stays out of target selection. Without it L4D2_OnChooseVictim picks the same melee survivor straight back (usually the nearest one) and the switching itself becomes the new jitter. The blacklist is only a preference — getClosestSurvivorAndValid ignores it as a last resort so the Charger is never left without a target.
  • Recompiled: ai_charger3.smx.

2026-09-04 Fix for spawns collapsing to Director fallback (infected_control + anne_spawn_accel 1.5.0)

  • Problem: after 2026-09-02.1 most Special Infected spawned through the Director API fallback and placements were scattered. 5% matrices on two empty cloud servers reproduced it: with default caps the gap was small (cloud8 1.24% vs 0.88%; cloud4 4/480 vs 0/480), but once caps are tightened to the state production reaches when the previous wave's live SI still occupy the ledger it blew up — cloud8 SI_target_limit_manual 2: 12.78% vs baseline 1.94%; cloud4 same config: 80/480 (16.7%) vs baseline 0/480. Single-variable isolation: reverting target_override to 2.27 made it worse (14.44%), reverting only infected_control brought it back to 2.22%. Root cause: the new "pending reservation" in infected_control drained the caps instantly, SpawnAccel_CollectEligibleTeamTargets() hard-excluded owners by capacity until the set was empty, and spawn_core.inc returned SpawnSearch_Exhausted silently for an empty owner set (no log, ownerCapacity always 0); TryNormalSpawnOnce then advanced one band per frame and reached the Director stage within 10 frames. The 3-second inf_spawn_nav_band_timeout path was never involved.
  • Extension anne_spawn_accel 1.5.0: team snapshots are still built and cached for the whole team, but now also keep one directed distance layer per survivor; new AnneSpawn_NavCandidatesCollectTeamEx(..., excludedClients, excludedCount) takes the survivors that already carry their cap and re-resolves every row against the nearest remaining survivor at collect time, skipping rows only the excluded ones can reach; changing the exclusion set never rebuilds the snapshot or goes Pending. Excluding everyone degenerates to no exclusion. The 32-bit Linux .so was built on pve in Docker (Ubuntu 22.04 + hl2sdk-l4d2 + SM 1.12 + AMBuild); nav_graph_test passes. The first build dynamically linked the new GCC libgcc (needed symbol version GCC_7.0.0); srcds forces Valve's old bin/libgcc_s.so.1, so dlopen failed on the real server and sm exts unload/load silently kept the resident 1.4.4 image. Rebuilt with -static-libgcc in AMBuilder (the .so no longer needs libgcc_s and readelf -V shows no GCC_*/GLIBCXX_* requirements); always verify with sm exts info after swapping the extension, restarting the container if needed. The Windows .dll was not rebuilt (the plugin falls back to "no exclusion" on an old extension).
  • infected_control.smx 2026-09-04.1: team owner membership depends only on alive/mobile, never on capacity; the exclusion list is computed in EnsureCursor and only restarts paging when it changes; the hot loop skips only explicitly excluded owners instead of consulting IsAtCapacity; IsAtCapacity (hard) reads the real target_override ledger only, reservations only feed the ChooseTargetSurvivor weighting (TTL 2.5→1.5 s, control classes only); commits no longer invalidate the team set cache; the early-exit path now logs [FIND EARLY-EXHAUSTED] (throttled) with a counter.
  • Validation: cloud4 (4 maps × 10 progress points, 12 SI) — 480/480 directed-Nav spawns and 0 Director fallbacks under both default and tightened caps, mean wave 245 ms (baseline 249 ms), while the live version produced 80 Director fallbacks; cloud8 full 1083-cell matrix (docs/infected_control_refactor/NAV_WAVE_MATRIX_20260904_TEAM_EXCLUDE_FULL.md) — 12984/12984 with zero missing, Nav 99.445%, Director Range 118→72, [FIND EARLY-EXHAUSTED] and ownerCapacity 0 throughout, mean 364.6→332.4 ms. Because of the loading issue above both rounds actually ran on extension 1.4.4, i.e. they validate the plugin-side fix on the "no exclusion" fallback path. The rebuilt extension 1.5.0 was then verified on cloud8 (docs/infected_control_refactor/NAV_WAVE_MATRIX_20260904_TEAM_EXCLUDE_EXT150.md): sm exts info reported 1.5.0 both when loaded under a temporary filename and after overwriting the real path plus docker restart; the tightened-cap repro gave 480/480 directed-Nav spawns and 0 Director fallbacks (the live version: 80/480), and the full 1083-cell matrix gave 12906 Nav / 77 Director, mean 359.8 ms — within noise of the 1.4.4 round and keeping the whole gain over the 08-26 baseline. With default caps every survivor has 5 slots and nobody fills up, so the exclusion list stays empty; this matrix proves 1.5.0 is regression-free but cannot measure the fast path. Under 1.5.0 the hot-loop ownerCapacity counter is 0 by design (excluded owners are re-attributed inside the native); use the new [TEAM EXCLUDE] log instead. Cloud4/cloud8 were restored afterwards (production files hash-verified, cvars reset).
  • Wave timing (kill phase + independent 16 s countdown + Anti-Bait) was not touched in this or the 09-02 change; the perceived "fixed 24 s" came from far fallback spawns dying late so the early-start conditions were rarely met.
  • scripts/run_nav_wave_matrix.py: after a watchdog srcds restart it only reloaded infected_control and the fixture and silently dropped SI_Target_limit/l4d_target_override (the first full run on cloud8 was invalidated by this and rerun). They are now tracked as companion plugins: whichever was running when the matrix started is reloaded in load order before infected_control on recovery.
  • Recompiled: infected_control.smx; rebuilt: anne_spawn_accel.ext.2.l4d2.so (-static-libgcc).

2026-09-03 SI bunny-hop: first-hop impulse ratio + ladder-top false landing fix

  • New "first-hop ratio" cvars: ai_charger3_bhop_first_hop_ratio, ai_tank3_bhop_first_hop_ratio, ai_BoomerBhopFirstHopRatio, ai_SpitterBhopFirstHopRatio, range 0.0–1.0. The first hop taken straight from running gets impulse × ratio; a hop right after landing still gets the full impulse. 0 is the previous "no gain on the first hop", 1 equals the legacy behavior. The shared AIPathMovement_GetHopImpulse gained an optional firstHopRatio parameter (default 0, backward compatible).
  • Why this knob: the chain-start lag from a non-accelerating first hop is ≈ 0.75 s × (1 − ratio) × (bhop cap − run speed), independent of the impulse value, so BhopSpeed / impulse cannot compensate for it. Higher tiers have higher caps and therefore lost the most (Neri Charger 0.48 s, Boomer 0.62 s, versus 0.28 / 0.53 s on Easy), inverting the difficulty ladder.
  • Dynamic AI difficulty dynamic_ai_difficulty.cfg, six tiers: Charger, Boomer and Spitter share 0.2 / 0.3 / 0.4 / 0.5 / 0.6 / 0.8; Tank 0 / 0.2 / 0.4 / 0.6 / 0.8 / 1.0 (Tank is the least affected class, its chain only shifts by one hop, so the Neri tier keeps the legacy full-impulse first hop). The corrected lag now decreases monotonically with tier (Neri: Charger 0.10 s, Tank 0 s, Boomer 0.12 s, Spitter 0.12 s). Except for Tank, the top tier deliberately stays below 1.0 so the "sudden first-hop burst" of the 250 Boomer / Spitter push does not return. Plugin defaults follow the Neri tier (0.8 / 1.0 / 0.8 / 0.8), matching the convention that the other cvar defaults of these four plugins sit near the top tier.
  • Fixed ladder-top dismount being counted as a landing: AIPathMovement_NotifyGrounded(client, onLadder) now remembers whether the previous registration was on a ladder; the engine's brief dismount (neither on ladder nor on ground) at the top of a ladder no longer counts as a landing, so the first hop after a ladder gets no free impulse. All four plugins pass onLadder.
  • ai_boomer_3 to 3.0.10: ground/ladder registration moved ahead of the m_customAbility early return, otherwise that skipped frame could mark a Boomer standing on the ground as an airborne landing; round_start also resets the hop chain.
  • ai_spitter_3 to 3.0.9: hooks player_spawn to reset the hop chain, aligned with Boomer / Charger.
  • ai_charger3 to 1.0.1.13, ai_tank3 to 1.0.0.13: Path / Direct / bait back-hop (Charger) and normal / Path bhop (Tank) take-offs now call AIPathMovement_MarkHopStart to consume the landing grant, consistent with Boomer / Spitter.
  • Recompiled: ai_charger3.smx, ai_tank3.smx, ai_boomer_3.smx, ai_spitter_3.smx.

2026-09-03 SI bunny-hop: no speed gain on the first hop

  • Diagnosis: the shared air-correction code in ai_path_movement.inc never adds speed (it only floors the speed at or below the take-off speed and deducts for turning). The "first hop suddenly speeds up in mid-air" feel on all four SI came entirely from the take-off frame: the Path / Direct take-off replaced the velocity with run speed + a full impulse (Charger 250→350, +40%; Boomer 175→325, +86%, and ClientPush additionally forced any speed ≤250 up to 251) instead of gaining speed on landing like a human bunny-hop.
  • ai_path_movement.inc gains hop-chain tracking: AIPathMovement_NotifyGrounded is called as early as possible on every grounded (or ladder) frame, and touching the ground after being airborne for at least 0.1 s counts as a landing regardless of whether the bot left the ground via a plugin hop, an engine jump or by walking off a drop along its path; falls from height count too, with no airborne time cap, so the speed carried out of a fall is kept and the landing hop still gets its impulse. AIPathMovement_GetHopImpulse returns the full impulse only when the bot re-jumps within 0.15 s of a landing; the first hop taken straight from the ground gets 0 and only changes direction. AIPathMovement_MarkHopStart consumes the landing grant on every take-off (called automatically by BeginPathHop); if the engine refuses the jump the bot is still standing on the next frame without ever leaving the ground, which does not count as a landing, so repeated ground attempts can no longer collect an impulse every frame. Ladders count as ground, so the first hop after climbing gets no impulse.
  • ai_charger3 to 1.0.1.12: Path and Direct ground take-offs use the landing impulse; the final pre-charge hop registers a take-off so the chain continues if the Charger drops back to approach; the chain is reset for a new Charger / on respawn. Debug logs now include ground speed and whether the hop was a landing hop.
  • ai_tank3 to 1.0.0.12: the normal hop (forward/back/side impulse) and the Path look-ahead hop both switch to the landing impulse; chain reset on round start and client connect.
  • ai_boomer_3 to 3.0.9: push force applied on landing only; the take-off velocity is built first and shared by the route check and the Teleport (the route check previously looked only at the push vector, which would have degenerated to a zero-length check on a push-less first hop); the 251 minimum hop speed now applies only on hops that actually add the push, so it can no longer sneak speed into the first hop.
  • ai_spitter_3 to 3.0.8: push force applied on landing only; spitterDoBhop writes back the already route-checked take-off velocity instead of recomputing one before the Teleport.
  • The impulse cvar descriptions of all four plugins now read "added when re-jumping right after landing; the first hop taken straight from running gets none". The acceleration chain is simply shifted one hop later; caps, air correction and turn speed loss are unchanged.
  • Recompiled: ai_charger3.smx, ai_tank3.smx, ai_boomer_3.smx, ai_spitter_3.smx.

2026-09-02 SI target-limit integration fixes (infected_control / SI_Target_limit / l4d_target_override)

  • Upgraded infected_control.smx to 2026-09-02.1. Fixed a regression where the runner (rush-man) state was only announced when it turned on: clearing it (runner rejoined, wave/round reset, scripted event) now fires OnDetectRushman(0), so SI_Target_limit no longer stays in "everyone's cap lifted" mode for the rest of the map after a single runner detection. Clearing has a confirmation window: new inf_antibait_runner_off_confirm (default 2.3 s) requires the runner to stay below the runner thresholds (gap shrank, a teammate caught up) for that long before the state is dropped, so a teammate briefly catching up and falling behind again does not toggle the target caps; death/incap/pin/disconnect of the runner still clears immediately. Wave resets no longer clear gAB.runner while leaving gST.bPickRushMan set, keeping both runner states consistent.
  • Spawn-side capacity now uses "remaining headroom": a freshly spawned SI that has not run ChooseVictim yet reserves a slot on its directed target (released when the ledger registers it or after 2.5 s), so a same-frame burst no longer directs every SI at one survivor; random target selection is weighted by headroom, so the flow leader's ×2 cap naturally attracts more pressure.
  • A teleport respawn whose class ran out of alive capacity (normal queue or traitor reservation took it) is substituted with a class that still has room; when every class is full the teleport queue backs off for 1 s instead of spinning every frame.
  • Upgraded SI_Target_limit.smx to 1.9. New SI_target_rushman_scope (default 1): while a runner is detected only the runner's per-survivor cap is lifted (SetTargetedCap), everyone else keeps normal protection; 0 restores the old behaviour (all caps raised to the SI limit). SI_target_enable now actually works (disabling clears target_override's targeted options and makes all natives report "unlimited"). Recomputation is compute-once/push-once instead of pushing the class-level options 66 times; manual mode also pushes on init. Added survivor_rescued / defibrillator_used / player_team / survivor player_spawn triggers plus a 1-second verification that corrects drift from mobile-count changes or a target_override config reload (sm_to_reload resets targeted to 0). No longer double-hooks l4d_infected_limit after an infected_control reload; the library name in si_target_limit.inc now matches RegPluginLibrary (lowercase).
  • Upgraded l4d_target_override.smx to 2.28. Added a post detour on ChooseVictim: when the pre stage leaves the decision to the game (no visible candidates with type 1, class not overridden, forward returned Handled) the engine's victim is still registered in the targeted ledger, so GetValue(VALUE_INDEX_TOTAL) no longer systematically under-reports; victims registered via SetLastVictim keep wait/dist semantics instead of being dropped and re-picked on the next call; fixed an ArrayList handle leak when the forward returned Plugin_Handled.
  • Recompiled: infected_control.smx, SI_Target_limit.smx, l4d_target_override.smx.

2026-09-02 spechud tank HUD fixes

  • spechud upgraded to 3.10.1. The tank HUD panel is sent to spectators and infected players only; the survivor recipient branch added earlier for versus_coop is gone. Also dropped the code that tried to "withdraw" the panel with CancelClientMenu on team change: that SourceMod call only clears server-side state and sends nothing to the client, so a radio panel still stays until its 3-second timeout, making the code a no-op.
  • Fixed the AI tank target line being stuck on "Searching": the HUD only read the victim recorded by l4d_target_override, but tank retargeting actually goes through Left4DHooks' L4D2_OnChooseVictim (used by ai_tank3 and SI_Target_limit), so target_override's record was often empty. spechud now listens to L4D2_OnChooseVictim itself and caches each tank's latest victim by userid, still preferring target_override's record when it has one.
  • All tank HUD panel text now goes through the translation file and the panel is built per recipient in their client language; the server default language is chi, so players without a matching language see Chinese. New phrases added to all seven spechud.phrases.txt files (en/chi/zho/jp/ko/vi/es).
  • scripts/spcomp-docker.sh now streams the scripting tree to the remote side via tar and the .smx back over stdout instead of relying on bind mounts. By default it runs spcomp64 natively over SSH on the pve-yma host (SPCOMP_SSH_HOST to change); SPCOMP_BACKEND=docker uses a container instead. Works from a Mac with no local Docker daemon.
  • Recompiled: spechud.smx.

2026-09-02 Scripted HUD display language menu

  • Upgraded l4d2_scripted_hud to 1.7.0. The !hud menu gained a Language entry, also reachable directly through !lang / !hudlang, switching between Follow game language, English, Simplified Chinese, Traditional Chinese, Japanese, Korean, and Vietnamese. Only languages the server actually knows from languages.cfg are listed, so a missing entry is hidden instead of silently doing nothing when picked.
  • The choice is stored per SteamID in the new lang_code column of scripted_hud_prefs plus a separate l4d2_scripted_hud_lang_v1 cookie, reusing the revision merge rules of the other HUD preferences so it survives server hops, reconnects, and a temporarily unavailable database.
  • The language is applied with SetClientLanguage, so the HUD, the HUD menus, and chat translations other plugins send to that player all follow it. SourceMod re-reads cl_language whenever a client changes its settings, so the override is re-checked and re-applied each time that player's HUD is built.
  • "Reset to defaults" in !hud deliberately leaves the language alone: that setting decides which language the menu itself is drawn in, and clearing it can leave a player unable to read their way back. Use "Follow game language" in the language menu to revert.
  • Known limitation: the HUD2 mode tag ([普通药役] and friends) and the AI difficulty names are still globally shared hard-coded Chinese and do not follow this setting.
  • Added sm_hudlang / sm_lang to docs/plugin_commands.md.
  • Recompiled: l4d2_scripted_hud.smx.

2026-09-01 AI Smoker instant-clear cooldown and retreat fix

  • Upgraded ai_smoker3 to 1.0.1.4. When a landed tongue is cut or instantly cleared, the Actions extension ends the stale SmokerTongueVictim, preserves native SmokerRetreatToCover, and issues a Nav move away from the victim outside the action callback stack; control returns to native AI when the tongue is ready. The event waits one frame and verifies that m_tongueVictim is clear, so transition into a normal choke is not mistaken for an instant clear.
  • Anne, Hardcore, Shotgun, and Alone now load Zonemod's l4d2_tongue_timer.smx. Survivor instant clears use at least 4 seconds. Tank-damage clears are lowered to 1 second on the Anne-family configs (upstream Zonemod keeps 8 seconds) so an AI Tank's punch or rock no longer heavily penalises its own Smoker. Misses still use tongue_miss_delay: 7 seconds from Easy through Expert and 3 seconds on Extreme/Neri; a normal landed release still uses tongue_hit_delay 13.
  • Upgraded l4d2_tongue_timer to 1.3-anne.1. It now records the actual pulling Smoker per victim, so Tank instant clears no longer assign the 8-second cooldown to the first Smoker found; releasing one tongue clears only its own pair, supporting concurrent pulls by multiple Smokers.
  • Recompiled: ai_smoker3.smx, l4d2_tongue_timer.smx.

2026-09-01 Charger claw-combo button and bait fixes

  • Upgraded ai_charger3 to 1.0.1.11. Close-range claws and punching a pinned victim now use IN_ATTACK2 again (primary attack is the charge ability). The previous IN_ATTACK press left the Charger idle for up to 2.5 seconds without landing a hit.
  • The gun-target claw combo now starts only when the survivor is not looking at the Charger, the Charger is above the melee-kill health line, and the survivor is not reloading or on shove cooldown, so a 500-HP Charger no longer stands still in front of a gun.
  • Two-hit prediction now uses only the 0.85s window for the second swing. The guaranteed charge floor is capped at 110 and is not raised when lateral speed is ≥ 150.
  • Claw counting now tracks whether a valid m_flNextPrimaryAttack baseline exists, so an initial value of 0 or a temporarily unavailable claw weapon no longer loses the first swing. Failed claw-range loads retry at most three times per map with a delay, avoiding per-frame native calls and Left4DHooks error-log spam.
  • Recompiled: ai_charger3.smx.

2026-09-01 2026-08 AI balance adjustments

  • Upgraded ai_charger3 to 1.0.1.10. Added the Charger-only cvar ai_charger3_air_speed_floor_ratio (default 0.50): Path / Direct takeoff now registers air-correction hopSpeed as actual takeoff speed times this ratio, while m_flLastHopSpeed still stores the real takeoff speed.
  • Nerfed Charger across the six dynamic AI difficulty tiers: impulse 30/40/50/60/80/100, cap 400/400/400/500/700/700, air lerp 0.10/0.15/0.20/0.25/0.30/0.40, turn speed loss 0.50/0.39/0.325/0.26/0.195/0.13, speed-floor ratio 0.50/0.60/0.65/0.70/0.80/0.80. Easy uses the existing hard cap of 0.50 for turn speed loss.
  • Upgraded ai_spitter_3 to 3.0.7. Added ai_spitter3_air_spit (default 0): only Extreme and Neri set it to 1, allowing AI Spitters to spit in the air. Easy through Expert forbid airborne spits; ground spits still work and are not hopped by the plugin. Player Spitters are not restricted.
  • Smoker tongue_fly_speed is now explicitly 1000 on all six tiers (Extreme/Neri lowered from 1200).
  • Recompiled: ai_charger3.smx, ai_spitter_3.smx.

2026-08-31 Pre-commit review fixes

  • Rebuilt ai_charger3.smx. The shipped binary was still 1.0.1.8, so none of 1.0.1.9 made it in: the claw-combo-then-charge flow, the guaranteed-hit band in resolveChargeCommitDist, the control-SI target-cap gate, and the bhop speed cap registration. It was still running the probabilistic charge that the source had already deleted.
  • Fixed a divide-by-zero in the ai_boomer_3 / ai_spitter_3 push normalisation. When the horizontal component is zero (target directly overhead or below, view pitch at ±90) normalising degenerates into a pure vertical vector, which launches the SI straight up — the very "flying" behaviour this batch set out to fix, reached by another path. Added a shared AIPathMovement_NormalizeHorizontal() that skips the push on a zero vector.
  • getChargerClawRange() now invalidates its cache on map change and refuses to cache an out-of-range read. The previous function-local static cached forever, so a first call before the weapon scripts loaded would pin the 70.0 fallback for the life of the process.
  • spechud's FillTankInfo uses a fixed-size array, dropping the new int[MaxClients] heap allocation from every panel build on the HUD draw path.
  • sm_navtest falls back to GetTargetAnchorPos when the target survivor is unusable, instead of comparing the candidate's height against itself (which always scored 0). Also fixes an uninitialised targetEye.
  • Five SI AI plugins restore #define REQUIRE_PLUGIN after #include <si_target_limit>, matching ai_tank3.sp, so the optional dependency is scoped to that one include.
  • si_target_limit.inc preserves the caller's REQUIRE_PLUGIN state when transitively including l4d_target_override, so third-party plugins are not silently given a hard dependency.
  • Removed the five unreferenced macros left in infected_control.sp after the sector system was deleted, and corrected the PEN_LIMIT_SCALE_LO comment.
  • Deduplicated .gitignore and dropped the nine explicit paths already covered by /test_results.
  • Added the missing jp / ko / vi translations for anne_server_redirect, matching the six-language coverage of the other Anne plugins.
  • Documentation corrections: the two dispersion bullets in README_SPAWN_ARCHITECTURE.md; the reaction-time row, Tank row and the two new bhop-cap cvars in annehappy_dynamic_ai_difficulty.md; and the four places in ai_charger3/readme.md still describing the probabilistic charge. Also corrected the Charger bhop speeds in the tier table (the doc said 45/60/75/90/150; the config is 40/50/60/70/100).

2026-08-30 Uniform AI reaction time across all tiers

  • In dynamic_ai_difficulty.cfg, z_acquire_far_time / z_acquire_near_time changed from 5.0 / 0.5 to 0.0 / 0.0 for Easy through Expert, matching Extreme/Neri which were already 0.0 / 0.0. Target-acquisition delay is no longer a difficulty axis; every tier difference now comes from per-class behaviour cvars.
  • Config-only change, no recompile needed. The "reaction time" row in docs/annehappy_dynamic_ai_difficulty.md was updated to match.

August 30, 2026 Loosen high-SI post-spawn repulsion

  • Upgraded infected_control.smx to 2026-08-30.1. At high SI counts the back half of a wave was being pushed out of the near band by post-placement repulsion: the kernel treated SI already in the survivors' faces as occupancy and ignored the SI-limit scale, while hard separation scaled down with the cap and so tightened exactly when it should have loosened.
  • Live SI closer than 400 to any survivor are no longer kernel sources. The SI-limit scale moved off the penalty value and onto the kernel radius (inf_spawn_kernel_radius × scale): at low SI it pushes candidates apart, at high SI it only prevents stacking, instead of flattening the whole curve and losing resolution. The inf_spawn_kernel_points default rose from 30 to 50.
  • The sector dispersion axis was removed entirely (recentSectors, the per-wave sector quota, and inf_spawn_sector_quota_bonus); directional preference is now expressed solely by the continuous tactical-geometry score, leaving kernel density as the only dispersion term.
  • Hard separation is now a fixed radius that no longer scales with the SI cap or with wave occupancy — it only answers "may we spawn here". The inf_spawn_sep_radius default dropped from 120 to 100. Wave timing is unchanged.

August 30, 2026 Charger gun-range bait: two claws then charge

  • Upgraded ai_charger3 to 1.0.1.9. After hopping into ai_charger3_bhop_min_dist (default 100), a gun target is treated as a guaranteed hit: no more probabilistic waiting, and the charge window is no longer clamped to 36–95.
  • If two claws can land before the target leaves claw range, the Charger punches twice then charges; if claws cannot connect, the target is backing out, or clawing takes more than 2.5 seconds, it charges immediately. Shotguns still charge as soon as they enter 100. Melee bait is unchanged.
  • Recompiled: ai_charger3.smx.

August 30, 2026 Cancellable traitor registration; traitor Tank removed

  • Upgraded infected_control.smx to 2026-08-30. Added !itcancel (alias of !neiguicancel): a traitor registration can be cancelled any time before its wave releases, and while a registration exists !it opens a menu with a single "cancel registration" entry; a cancelled slot is no longer immediately backfilled with an AI. Once the release window opens (SpawnAllowed), cancelling is refused with a "window closed" hint.
  • Removed the Tank from traitor classes: selectable classes are now decided by the active spawn pool and the Tank can no longer be registered. The inf_traitor_class_mask default and ceiling stay at 127 (bit 64 is kept only for rollback compatibility and never takes effect), and z_max_player_zombies no longer reserves an extra slot for a traitor Tank.
  • Selectable traitor classes now intersect with the active spawn pool: classes disabled via inf_*_limit 0 cannot be queued, and the allcharger / allhunter modes only allow Charger / Hunter respectively.
  • The effective inf_traitor_max_slots is clamped to l4d_infected_limit - 1, keeping at least one AI spawn budget per wave. Traitor auto-bhop moved to the OnPlayerRunCmd channel (internal refactor, same behavior). Usage and menu texts updated in all six languages.
  • Version rollback configs Anne26-07 / Anne25-11 / Anne25-10 now reload smart_ai_rock.smx (previously unloadall.cfg unloaded it and rollback versions silently lost smart Tank rock throws).

August 30, 2026 Tank bhop cap lowered per difficulty

  • Lowered ai_tank3_bhop_max_speed by 200 across the dynamic AI difficulty tiers with an 800 ceiling: Easy 500, Normal 600, Hard 700, Expert 800, Extreme 800 (850 hit the ceiling); the Neri tier stays at 1000. Config-only change (dynamic_ai_difficulty.cfg), no recompile needed; the plugin default (used without dynamic difficulty) remains 1000.

August 29, 2026 AI SI path-bhop "sky-launch mega jump" fix

  • Fixed path bhop occasionally producing a single absurdly long, flying-looking jump. Three root causes: ai_spitter_3 / ai_boomer_3 applied their hop impulse additively onto the current velocity with no cap at all, so on long open stretches the speed snowballed by +100/+150 every hop; the shared air correction kept the recorded hop speed as a floor for the entire airborne phase and even restored the spike after the engine slowed the SI on a wall clip; and the push vector carried a vertical component (Boomer toward elevated path goals, Spitter along its eye pitch), which stacked on top of the jump's own Z velocity into a literal sky launch.
  • Upgraded ai_spitter_3 to 3.0.6 with the new ai_SpitterBhopMaxSpeed and ai_boomer_3 to 3.0.8 with the new ai_BoomerBhopMaxSpeed. The additive push is now clamped to that horizontal cap, the push vector keeps only its horizontal component (vertical height stays the jump's job), and the pre-jump safety check now traces with the same velocity that actually gets applied.
  • Both new caps follow a "Charger cap +200" rule: the plugin default is 1000 (= ai_charger3_bhop_max_speed default 800 + 200), and the six dynamic AI difficulty tiers in dynamic_ai_difficulty.cfg are set to 600 / 600 / 700 / 800 / 1000 / 1000 (matching the Charger tiers 400 / 400 / 500 / 600 / 800 / 800).
  • Shared module ai_path_movement.inc: AIPathMovement_BeginPathHop gained a max-speed argument, so the air correction never sustains more than each SI's own bhop cap and externally induced speed spikes are no longer preserved through the whole airborne phase.
  • Upgraded ai_smoker3 to 1.0.1.3 and ai_tank3 to 1.0.0.11: the takeoff frame is now clamped by ai_smoker3_bhop_max_speed / ai_tank3_bhop_max_speed too (previously only airborne frames were clamped, so the takeoff instant could exceed the limit). Upgraded ai_charger3 to 1.0.1.8; its ground/direct bhop already had a cap, and it now registers that cap with the shared air correction.
  • Recompiled: ai_smoker3.smx, ai_boomer_3.smx, ai_spitter_3.smx, ai_tank3.smx, ai_charger3.smx. The Jockey already caps each hop at 400+80 and the Hunter has no bhop logic, so neither needed changes.

August 30, 2026 Jockey post-shove suppression scaled by AI difficulty

  • ai_JockeyShovedCooldown is now driven by the dynamic AI difficulty system: the six tiers in dynamic_ai_difficulty.cfg use Easy 3.0, Normal 2.5, Hard 2.0, Expert 1.5, and Extreme/Neri 1.0 seconds. annehappy_dynamic_ai_difficulty.smx applies the value per tier and restores the baseline on tier changes, so nothing lingers.
  • The plugin default of 3.0 equals the Easy tier, so modes that do not run the dynamic difficulty plugin automatically get the most lenient setting.

August 29, 2026 AI Jockey post-shove suppression fix

  • Upgraded ai_jockey_2.smx to 2026.08.29. Fixed the loophole where shoving the Jockey away was exactly what armed its punish branch: if the survivor's latest swing is the one that shoved the Jockey itself, the "instant leap during shove cooldown" punish no longer fires. The punish still applies to genuinely wasted shoves (whiffs or shoves spent on other SI).
  • The post-shove suppression no longer reuses z_jockey_leap_time (0.4s in Anne configs). A new dedicated cvar ai_JockeyShovedCooldown (default 3.0s, matching the competitive-standard jumpcap block window for shoved Jockeys) governs it. During suppression the plugin applies no bhop/leap enhancements and strips the native AI's leap button, so the Jockey can no longer re-pounce mid-air the moment its 0.9s stagger ends; it can still move and retreat normally. The jumpcap patch's AI exemption and the stagger cvars are untouched.

August 28, 2026 Per-team HUD slot visibility

  • Upgraded Scripted HUD to 1.6.0. Every slot in !hudmenu gains a "show for team" submenu accepting any combination of survivor/spectator/infected (at least one must stay selected), so a slot can, for example, appear only while spectating. Choices persist to cookies and the database (new slot_team_masks column, migrated automatically); old preferences default to all teams.
  • The team whitelist for sensitive content sources moved from hard-coded rules to the l4d2_scripted_hud_source_teams cvar (source:teammask pairs; bits 1=spectator, 2=survivor, 4=infected). The default 2:5,7:5,13:5 matches the old behavior—SI HP, next-wave countdown, and the spawn queue remain spectator/infected only—while 13:1 style entries narrow a source to spectators. Menu tags like "(spec/SI)" are now generated from the live configuration, with all six languages updated.
  • Slots hidden by team gating no longer transmit their text to the client at all (previously the string still traveled over the network and was merely not rendered). Team changes refresh visibility immediately.

August 29, 2026 Independent control-SI target cap

  • Upgraded SI_Target_limit.smx to 1.7. Smoker / Hunter / Jockey / Charger now share one lock cap: control-SI budget / mobile survivors + 1. The budget is the sum of enabled control-class z_* / inf_* limits, clamped to l4d_infected_limit. Boomer / Spitter / Tank do not occupy this pool.
  • Upgraded l4d_target_override.smx to 2.26 with targeted_mask, so only control SI count toward the cap. Flow-leader ×2 and rushman uncapping keep their previous meaning.
  • Anne mode shared_settings.cfg files now pin SI_enable_option 53.
  • Upgraded l4d_target_override.smx to 2.27: added L4D_TargetOverride_SetLastVictim so AI plugins that rewrite targets through the L4D2_OnChooseVictim channel register their choice in the same targeted ledger; also fixed the ledger not syncing when the L4D_OnTargetOverride forward rewrites the victim.
  • Upgraded SI_Target_limit.smx to 1.8: added the IsClassTargetLimited native and the sm_targetlimit_status admin debug command; fixed a 1.7 changelog comment that terminated early and broke compilation.
  • Unified the ledger across SI AI plugins: ai_hunter_2 / ai_jockey_2 / ai_charger3 now respect the control-SI cap before retargeting (no switch when the new target is full), while ai_spitter_3 / ai_boomer_3 / ai_tank3 register their rewrites; ai_smoker3 never rewrites targets so it needs no hookup.
  • Player-count adaptive: the formula scales across 4-player, 5-8 player multiplayer and solo endgames — total capacity is always "control-SI budget + mobile survivors", strictly greater than the control SI on the field; with one or few survivors the cap relaxes automatically, so SI never lack a legal target.
  • Version rollback configs Anne25-11 / Anne25-10 / Anne24-5 / Anne23-1 now reload SI_Target_limit.smx (previously unloadall.cfg unloaded it and these versions silently lost the target cap).

August 19, 2026 Tank head-ride and ladder drop

  • Upgraded ai_tank3 to 1.0.0.9. Head-block is now two cases: a survivor standing on the Tank hull, and an unreachable overhead stall.
  • Riding no longer waits for a 2-second standstill and no longer blacklists the rider. A punch also SweepFists upward at the rider; if they are still on the head after 3 seconds the Tank force-throws in place, without walking 250 units away. Close-range rocks aim straight at the target so the ballistic formula cannot divide by a near-zero horizontal distance. Jump-rocks are disabled while someone is riding.
  • Overhead stalls still use the 2-second movement window. If another survivor exists, the Tank switches (standing first, then pinned, then downed). With no alternative it throws in place and no longer CommandABot RESETs the same unreachable target, which was restarting the climb-and-fall loop. RESET is also skipped on or near ladders.
  • While on a ladder the Tank flattens pitch and locks yaw to m_climbableNormal, reducing the vanilla look-at-victim drop.

August 20, 2026 Tank ride detection for embedded riders

  • Upgraded ai_tank3 to 1.0.0.10. Riding still treats m_hGroundEntity == Tank as the first proof, so adjacent ledges and stairs are not counted as riding.
  • If the ground entity is not the Tank, riding now requires overlapping XY hulls plus a downward hull trace from the survivor's feet that actually hits the Tank. That covers riders stuck in the Tank model whose ground entity is still the world, without using a center ray that merely misses world as proof.

August 23, 2026 Dual-layer hat preference persistence

  • Upgraded l4d_hats.smx to 1.47.0. Hat type, wear toggle, first-person self view, third-person self view, and visibility of other players' hats are all stored in clientprefs cookies and load even when l4d_hats_save is 0.
  • Upgraded rpg.smx to 2.1.0. When the rpg MySQL entry exists in databases.cfg, the four toggles use the RPG table as primary storage; unavailable MySQL or failed column migration falls back to clientprefs SQLite. Existing accounts migrate NULL columns only after cookies are ready, preventing defaults from overwriting saved choices.
  • Every rebuilt hat entity keeps its per-viewer SetTransmit filter, so hiding other players' hats remains effective across rounds. Added the idempotent 20260823_hat_preferences.sql migration; the plugin can also add the same columns automatically.

August 23, 2026 Shared sound catalog and unrestricted per-event choices

  • Upgraded l4d2_hitsound.smx to 2.8.0. The 25 non-empty preset references are deduplicated by path into 22 stable shared sounds S01..S22; the three duplicate samples in presets 1 and 3 are stored once. Downloads and precaching now also run once per unique sound.
  • The headshot, hit, kill, and headshot-kill pickers all show the complete shared catalog, so any sound can be assigned to any event and previewed immediately. Preset labels expose their exact mapping; preset 9 is [head:S21 hit:off kill:off head-kill:S22].
  • Existing positive DB/KV preset-source values require no data migration. New shared choices use -1..-127, so the four sound columns must be signed integers. The repository schema already is signed; the idempotent 20260823_hitsound_sound_catalog.sql fixes production tables that were created as UNSIGNED.

August 23, 2026 Per-sound hit-feedback refactor

  • Upgraded l4d2_hitsound.smx to 2.7.0. Sound sets are now presets that apply only members with real samples; missing members are turned off. Set 9 therefore explicitly enables headshot and dedicated headshot-kill sounds while leaving hit/kill off.
  • Players can independently choose the actual source for headshot, hit, kill, and headshot-kill sounds. Each picker lists only sources that provide that sound and previews it immediately. Headshot-kill value 0 still follows the regular headshot sound.
  • Fixed the pre-damage infected_hurt fatal-shot check, the admin single-item close action clearing all three items, preference writes before initial DB load, authorization timing, out-of-order rapid saves, and the ineffective dynamic master switch. Existing DB/KV values are normalized on load. This refactor adds no columns; databases still missing hitsound_stack_mode / hitsound_headkill must run the existing idempotent 20260820_hitsound_personal_prefs.sql migration once.

August 20, 2026 Hit-feedback ding set closer to old killsound

  • Upgraded l4d2_hitsound.smx to 2.6.0. Sound-set optional keys are down to three: headshot_kill (dedicated headshot-kill sound), stack (same-frame stack vs merge), and apply_special_only (write sound range on select). A kill plays one death-event sound only; no extra hurt ping for overkill. Playback is always SNDCHAN_AUTO. Fire, inferno, DMG_DIRECT, blast, and weapon_id==0 filters are unchanged.
  • A sound package now sets four items together: headshot, hit, kill, and headshot-kill. Players may independently choose headshot-kill set 0..N (0 falls back to the regular headshot sound). That set ID and playback mode are stored in the RPG table and migrate once from the old SoundSelect.txt. The redundant overlay quick toggle and sm_hitui were removed; icon set 0 already disables icons.
  • Set 9 "original ding vote plugin": ding on headshot hits, bell on headshot kills, silence otherwise; picking it sets the range to all infected.

August 19, 2026 Scripted HUD restores survivor SI count/interval

  • Upgraded Scripted HUD to 1.5.1. Version 1.4.2 hid HUD2's special-infected count/interval together with live SI HP, next-wave countdown, and the spawn queue. [N SI / M sec] is public room config (the same tag as the hostname), not live intel. Survivors see that field again, and it coexists with the built-in CS kill feed, which still uses only that player's engine slots 8-14.
  • SI HP, next-wave countdown, and the next-wave queue remain spectator/infected only.

August 19, 2026 Anti-Bait pauses on low HP with no medkits

  • Upgraded infected_control.smx to 2026-08-19.1. Anti-Bait now enters Paused when every standing survivor has permanent health below inf_antibait_lowhp (default 40) and no living survivor is carrying a first-aid kit, defibrillator, pain pills, or adrenaline. Incap health is not permanent health. Set the threshold to 0 to disable this gate.

August 15, 2026 Removed the corpse-dissolve vote

  • Removed the "dissolve corpses" vote from every mode menu and deleted cfg/vote/dissolve_on.cfg and cfg/vote/dissolve_off.cfg.

August 15, 2026 Scripted HUD integrates the CS kill feed

  • Upgraded Scripted HUD to 1.5.0. Removed the standalone l4d2_cs_kill_hud.smx and its old kill-feed votes; the CS-style feed is now built in. It is off for each player by default and can be toggled independently in !hudmenu, without a server-wide enable switch.
  • For a player who enables it, SendProxy takes over engine slots 8-14 (shown as HUD 9-15 in menus) only for that recipient, and those slots disappear from that player's configurable-slot menu. Players who leave it off continue to receive the native kill list and keep their own slot preferences.
  • Reworked !hudmenu into CS kill feed, Core HUD (1-4), Extra HUD (5-8), Extended HUD (9-15), screen layout, and HUD 2 field groups. Kill-feed and slot preferences are stored per player.

August 15, 2026 Scripted HUD next-wave state fix

  • Upgraded infected_control.smx to 2026-08-15.2 with a wave-status native that distinguishes the kill phase, independent base countdown, and Anti-Bait hold. The legacy ETA native remains compatible.
  • Upgraded Scripted HUD to 1.4.4. Exact seconds are shown only during the base countdown; the kill phase and Anti-Bait hold use explicit localized states. Version 2026-07 and older providers show -- because they lack the state API, instead of exposing an estimate that can be off by about eight seconds.

August 15, 2026 New-player traitor state cleanup

  • Upgraded infected_control.smx to 2026-08-15.1. A player entering the server now explicitly clears traitor registration from the occupied client slot. Even if cleanup for the previous occupant did not finish, the new player starts as a non-traitor and join.smx can move an accidental infected-team assignment back to spectator. Registering as a traitor afterward is unaffected.

August 15, 2026 Actions crash fix

  • Fixed actions.ext reinserting missing user-data keys while destroying Actions and never removing three Action-pointer caches. The Linux extension is now 3.7.6-anne.2 and erases user-data, identity-data, and actor cache entries on destruction.
  • Fixed Actions method propagation creating empty pre/post listener hash entries for every Action even when no listener existed, then retaining the outer Action-pointer key after destruction. Candidate Action pending markers are now cleaned up as well.
  • Updated ai_charger3 to 1.0.1.7. ChargerEvade now defers CommandABot(MOVE) to the next frame instead of synchronously rebuilding the NextBot behavior tree inside an Actions OnUpdate callback.

August 14, 2026 Player-facing text alignment

  • The last !xx status field is now "Version", not a telecom-line label. English Tank stall / HP return and Japanese Expert / bunnyhop wording now match the source meaning.
  • Traitor and Anti-Bait hints distinguish stalling, hold active, and wave released. Japanese, Korean, and Vietnamese fill in leftover English and use a single traitor name.
  • Default Scripted HUD survivor status is built in the client's language. The header now says revive count instead of a current-downed label.
  • Black-and-white hints use phrases and say the next down is death. The !ht menu adds Japanese, Korean, and Vietnamese. Chinese skill-detect class names now match traitor/stats wording.

August 14, 2026 Network Quality Reports Prefer Loss

  • Upgraded network_quality_hint.smx to 1.1.5. Website incidents now focus on packet loss: choke below nqh_report_choke_limit (default 20%) is no longer reported as an incident. Local chat warnings still use nqh_choke_limit (default 5%).
  • Incident reason codes: timeouts stay timing_out; otherwise prefer loss, then ping, and choke only when above the report threshold.

August 13, 2026 Spawn Dispersion and Class Variety

  • Upgraded infected_control.smx to 2026-08-13.11 to push Hunter / Jockey / Charger even further off the survivor rear. Hard-reject -5→-4 (inf_score_behind_reject_gap_hjc), behind-penalty scale 2.0→2.5, flow back-anchors -4/-3. Hunter rear 0.15→0.05; Jockey no longer uses rear. Same-flow candidates still score. Smoker / Spitter / Boomer and wave timing are unchanged.
  • Upgraded infected_control.smx to 2026-08-13.10 to further cut Hunter / Jockey / Charger spawns behind the survivor flow. Those three now hard-reject at -5 (inf_score_behind_reject_gap_hjc) instead of the global -8, and their behind-flow penalty is multiplied by inf_score_behind_hjc_scale (default 2.0). Their flow back-anchors move from -10/-9 to -5/-4; same-flow candidates still score. Hunter tactical quality leans flank/edge instead of rear; Jockey rear weight 0.15→0.05. Smoker / Spitter / Boomer keep the global behind rules. Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.9 to ease hard separation another step. inf_spawn_sep_radius 150→120, inf_spawn_sep_ttl 2.5→1.5; same-Nav cooldown NAV_CD_SECS stays 1.0 s. Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.8 so Hunters have a bit more pounce-setup distance. The preferred band moves from 350–750 to 350–850, and the sweet spot from 38% (~500) to 48% (~590), still in the mid third rather than the old 950 far band. Charger / Jockey bands are unchanged. Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.7 to pull Charger / Jockey / Hunter off overly far landings. Hunter’s preferred band shrinks from 350–950 to 350–750, the distance sweet spot moves from 45% to 38% of the ring (~500), high-sort scale 0.90→0.95, and high-ground range compensation 800→450 so distant perches are no longer discounted into “mid” range. Jockey’s preferred band is 250–550, Charger 250–600, with slightly closer sweets; their dispersion weights drop from 1.15/1.00 to 0.75/0.65. Global hard-sep radius 180→150, kernel points 40→30, and the scoring budget changes from near/mid/far 6/4/2 to 6/5/1. Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.6. Distance bands go back from 10 to 6 (stage 0 is still the full preferred interval; stages 1–5 interpolate from the preferred max to the final max). Ten narrow rings left near/mid/far with only a couple hundred units to split; six wider rings make the tertiles meaningful. Near/mid/far are now thirds of the current band’s search ring (scanMin..maxRange), not the current page’s min/max, so a first page that only covers the near end of the ring is not re-split into a fake near/mid/far. Sector quota still constrains the first 2/3 of the bands (0–3) and exempts the last third (4–5). Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.5. The scoring budget is no longer “the first 8 qualified points this tick”: each directed candidate page is split by path-distance tertiles and scored as near 6 / mid 4 / far 2 (12 total). Consumption interleaves 3:2:1; a full tertile skips expensive visibility/path checks, and unused tertile slots overflow so an empty band does not stall the spawn. inf_spawn_nav_expensive_per_slice defaults to 24 (was 16). CVars: inf_spawn_budget_near/mid/far (all 0 disables tertiles). The ranked high-ground channel keeps in-group order and only interleaves tertiles. Wave timing is unchanged.
  • Upgraded infected_control.smx to 2026-08-13.4. This release reworks spawn dispersion so a wave no longer lands in one small cluster, and adds controlled randomness to the class lineup. The wave timing contract (kill phase, independent 16-second base countdown, Anti-Bait) is unchanged.
  • Distance bands are refined from 6 to 10 stages: each class keeps its full preferred band at stage 0 (e.g. Smoker 500–1200) so spawns do not get closer on average, while the failure-driven widening now interpolates linearly between the preferred and final maximum (Smoker +200 per stage up to 3000). Each escalation scans a smaller ring, distances ramp up more smoothly, and a ring is more likely to fit into one shuffled page. The sector quota accordingly constrains the first 2/3 of the bands and exempts the final third.
  • Directed candidate pages are now shuffled after fetching (inf_spawn_candidate_shuffle, default on). The old flow consumed candidates in ascending path-distance order and accepted at most 8 qualified points per attempt, so scoring only ever saw the nearest little cluster; after shuffling, the scored set spans the whole distance band. The ranked high-ground channel keeps its ordering and is not shuffled.
  • Hard separation is now configurable and stronger: inf_spawn_sep_radius (default 180, previously hard-coded 80) and inf_spawn_sep_ttl (default 2.5 s, previously 0.5 s), still scaled by the SI limit.
  • The dispersion score gains a continuous kernel penalty: alive SI and recent spawn points subtract exp(-d/r)-weighted points (inf_spawn_kernel_radius default 280, inf_spawn_kernel_points default 40), complementing the coarse "last 3 sectors" memory, which is kept.
  • New per-wave sector quota: spawns per sector per wave ≤ ceil(SI limit / sectors) + inf_spawn_sector_quota_bonus (default +1, -1 disables). Only the first four distance bands are constrained; far bands and the Director fallback are exempt so narrow maps cannot starve.
  • High-ground priority now uses the extension's ranked directed candidates: inf_nav_high_sort_scale is re-enabled (new defaults Smoker 0.85 / Hunter 0.90). Candidates at least inf_nav_high_sort_min_height (64) above the target are front-loaded by discounted distance into the limited scoring budget, instead of relying on after-the-fact score bonuses inside the near-ground cluster.
  • Class variety: class scarcity picks now apply a small random jitter of ±inf_class_scarcity_jitter (default 0.06) to break the fixed per-wave rotation among similarly occupied classes, and the support gate (Boomer/Spitter unlocking after pressure classes) is randomly waived per wave with probability inf_support_gate_waive_prob (default 0.15); breach waves never waive it.
  • The [SCORE-CHOSEN] debug log and the sm_navdebug score breakdown now include the kernel term (kern).

August 13, 2026 Scripted HUD uses all 15 engine slots

  • Upgraded extend/l4d2_scripted_hud.smx to 1.4.0. All 15 EMS HUD slots can be assigned a content source in !hudmenu → "All 15 HUD slots" (server default / unused / survivor status / SI HP / kills / ping / tank damage / team items / next wave / speed / round timer / Witch warning / progress-tank-witch / server status).
  • HUD 1-4 keep their old defaults. Slots 5-8 (engine 4-7) stay hidden until a source is chosen, then they appear in a left column. Slots 9-15 (engine 8-14) keep passing through the engine kill list until you bind content to that slot, which then covers that kill line and moves it to the lower right so it does not sit on the crosshair.
  • Preferences are stored in the cookie and scripted_hud_prefs (hud_mask widened to smallint, new slot_sources column; old 6-field cookies and hud3/hud4 columns still load). Slot names and menus are translated in all six languages.
  • 1.4.1: Slot coordinates follow the screen aspect. !hudmenu → "Screen layout" offers 16:9 / 16:10 / 4:3 / 21:9. Extra slots sit in a left column (4-9) and a right column (10-14) with spacing that clears HUD 1-4, the kill feed, the crosshair, and the weapon tray. 4:3 narrows to avoid right-edge clipping; 21:9 pulls into a centered 16:9 safe area. Each player gets their own layout through SendProxy.
  • 1.4.2: SI HP, next-wave countdown, the next-wave spawn queue, and HUD 2's "SI count / interval" are shown only to spectators and infected; survivors hide the whole slot. The menu marks those sources with " (spec/SI)". New "Next SI queue" source reads InfectedControl_GetSpawnQueue from infected_control (older plugins show --). Extra slots in the same column stack by their real line count; extra slots cap at 4 lines and HUD 3/4 at 6 so long text no longer overlaps.

August 13, 2026 Scripted HUD extra content sources

  • Upgraded extend/l4d2_scripted_hud.smx to 1.3.0. HUD 3 / HUD 4 content menus gain six extra sources, still chosen per slot and saved to cookies/database.
  • Tank damage board: ranks survivors by damage this tank fight (top 4, with percent). The board stays after the Tank dies until the next Tank; "No Tank" when none is in play.
  • Team items: one compact line per survivor for throwable / heal / temp (molotov/pipe/bile, kit/defib/incendiary/explosive, pills/adrenaline; empty is -).
  • Next SI wave: reads infected_control's GetNextSpawnTime native (shows -- if the plugin is not loaded) plus live small-SI count vs the limit.
  • Speed / bhop: horizontal speed of yourself (or your spectated survivor) and the current bhop peak; the peak clears after about 0.25s of grounded walking.
  • Round timer + horde: mm:ss from round_start; "ON" during the 60-second rolling create_panic_event window, otherwise the director horde countdown (L4D2CT_MobSpawnTimer).
  • Witch distance warning: meters to the nearest Witch and state (calm / near / alert / enraged); "NEAR" inside 15 m. Extra Witches are counted separately. New phrases are translated in all six languages.

August 13, 2026 Scripted HUD custom slots and white-box hardening

  • Upgraded extend/l4d2_scripted_hud.smx to 1.2.0. HUD 3 / HUD 4 now support player-selectable content: !hudmenu gains "HUD 3/4 content" submenus offering Server default / Survivor status / Special infected HP / Kill stats / Player ping. Choices persist in cookies and the scripted_hud_prefs database (old tables are ALTER-migrated automatically).
  • The Special infected HP page shows the alive SI count vs the limit, each small SI's current HP (Smoker/Boomer/Hunter/Spitter/Jockey/Charger), Tank current/max HP, and the Witch count. Kill stats track per-survivor SI/common kills for the round; the ping page lists human players' latency (survivors first). All text uses translation phrases (Simplified/Traditional Chinese, English, Japanese, Korean, Vietnamese).
  • Hardened the occasional "three empty white-outlined rectangles" seen around the hudmenu: those frames are EMS scripted HUD slots rendered in a transient "visible but neither NOBG nor TEXT" flag state. Per-client HUD flags are now rebuilt from the authoritative server flags so the ResetHUD refresh window can no longer send flags of 0, and unused EMS slots 4-7 are explicitly hidden on map start so stale slot data cannot draw empty background frames.
  • Panel height is personalized per client from the actual line count (new m_fScriptedHUDHeight sendproxy), so multi-line custom content is no longer clipped. The l4d2_scripted_hud_hud3_team default changed from 2 (infected only) to 0 (everyone), so survivors can actually see their chosen HUD 3 content.
  • HUD 2 gains a "Team wipe count" field: it reads anne_round_wipe_count published by server.smx (wipes on the current map, reset on transition), and shows [Wipes xN] at the end of HUD 2 once at least one wipe happened. HUD only - the room name is untouched. Toggle it under !hudmenu -> "Configure HUD 2 fields". Players with previously saved HUD 2 preferences start with the field off and need to enable it once; new players get it on by default.

August 13, 2026 server_name leftover AI difficulty tag

  • Upgraded server_name.smx to 1.4.8. Room-name tags such as [AI:Expert] are shown only while annehappy_dynamic_ai_difficulty.smx is actually loaded and a tier has been chosen.
  • Unloading the dynamic-difficulty plugin leaves ah_ai_dynamic_current_level behind in SourceMod. The old hostname logic kept writing a leftover 1-6 value into hostname / sn_main_name. It now checks whether the plugin is running and strips any [AI:...] already baked into the room name.
  • Upgraded annehappy_dynamic_ai_difficulty.smx to 2026.08.13.1. It registers the annehappy_dynamic_ai_difficulty library and resets current_level / current_mode / current_ppm / current_locked to 0 on unload, so HUD, !xx, and spawn strategy do not keep reading a stale tier.

August 13, 2026 Water Slowdown Chat Translations

  • Fixed l4d2_slowdown_control chat lines for water slowdown while Tank is in play and after Tank dies. English no longer uses Chinese word-for-word phrasing such as "tank generation", Vietnamese no longer mistranslates Tank as "xe tăng", and Japanese no longer uses two different plugin names.
  • Simplified Chinese, Traditional Chinese, English, Japanese, Korean, and Vietnamese now match the upstream meaning: water slowdown is reduced while Tank is in play, then restored to normal.

August 13, 2026 Anne Tank Announce Translations

  • Upgraded optional/AnneHappy/l4d2_tank_announce.smx to 1.0.1.1. Chat, hint, and center text now all use i18n phrases; Player/AI name prefixes are built in the client's language, so English players no longer see a hard-coded Chinese "(玩家)".
  • English no longer uses Chinese word-for-word phrasing ("has been generated / controller"), and the hint line is translated. Spanish now includes the previously missing Anne-specific keys.
  • Also aligned the versus Spawned phrases in Japanese, Korean, Vietnamese, and Traditional Chinese, keeping Tank as a proper noun.

August 13, 2026 l4d_stats Chat i18n

  • Upgraded l4d_stats.smx to 1.5.6. Remaining hardcoded English chat fragments now use translation phrases; all 315 Simplified/Traditional Chinese, English, Japanese, Korean, and Vietnamese phrases are aligned, and the per-language files are no longer limited to the 13 map-record strings.
  • Special Infected kill, Smoker/Hunter save, Charger rescue, and Tank rock announcements now show class names in the viewer's language. Human players still appear by nickname.
  • Map-record team fallback names and the console [RANK] prefix are translated. Upgraded global_chat.smx to 1.1.1 so global map-record broadcasts use the same team-name phrases.

August 13, 2026 Survivor MVP Translations

  • Upgraded survivor_mvp.smx to 0.3.4. Round MVP/LVP lines, personal ranks, and bot prefixes no longer hard-code Chinese; they are printed in the client's language, so English players no longer see "特感/小僵尸/友伤/[机器人]".
  • Japanese, Korean, and Vietnamese no longer mistranslate SI/FF/Anne compact stats as "special sense", "black gun", or "pharmacy". Spanish now includes the previously missing Tag and Anne compact-stat keys.

August 13, 2026 text Plugin Status Translations

  • optional/AnneHappy/text.smx now prints !xx / join / round-start status lines in the client's language, so English players no longer see hard-coded Chinese such as "特感", "连跳", "动态难度", or "回血".
  • English no longer uses Chinese word-for-word phrasing such as "does not exceed 10 special". Japanese no longer mistranslates AnneHappy as "unhappy".

August 13, 2026 All-Newbie Team Bonus

  • Upgraded l4d_stats.smx to 1.5.6. When every human survivor is a new player and there are at least four of them, the whole team earns a 1.5x newbie-team multiplier instead of nobody getting the mentoring bonus.
  • One to three new players still use the old rule: only experienced survivors receive 1.1 / 1.3 / 1.5.
  • Added l4d_stats_newbie_bonus_all_newbie_multiplier (default 1.5) and l4d_stats_newbie_bonus_all_newbie_min (default 4). Saferoom, finale, and round-start chat explain the team bonus in each language.

August 13, 2026 Hat/Skin Save Fixes

  • Upgraded l4d_hats.smx to 1.46.1: added Hats_SetClientHat / Hats_GetClientHat natives so RPG can restore hats without going through the sm_hatclient console command.
  • L4D_OnHatLoadSave now reports -1 when a hat is removed, instead of colliding with the first hat's 0-based index. Cookie deletion and saved-hat restore now follow menu access (including top 100) and wait for rank data, so saved hats are no longer wiped.
  • Upgraded rpg.smx to 2.0.1: hats, skins, and glows are reapplied after the database load finishes; hats/skins are no longer cleared to 0 while the scoreboard cache is still loading.
  • The first hat is stored as -2 so HAT=0 no longer means both "no hat" and "first hat". Selecting a hat before the old row is read no longer overwrites the new choice.
  • Skins had the same "not restored after async DB load / cleared before rank data is ready" hole and are fixed together. Titles, recoil, damage HUD, and hit sounds already restore from their own load callbacks or separate cookie/database paths.

August 13, 2026 Network Quality Reports

  • Upgraded network_quality_hint.smx to 1.1.4. The plugin ConVar still defaults to nqh_report_enable "0", so local ping/loss/choke checks do not depend on reporting. This repository now sets nqh_report_enable "1" in cfg/sourcemod/network_quality_hint.cfg for Anne game servers on the website whitelist.
  • Reporting requires SteamWorks. Without SteamWorks the plugin still loads and shows local hints, but it will not POST to nqh_report_url.
  • Community servers that are not on the website whitelist should keep or restore nqh_report_enable "0".

August 13, 2026 Hit-Feedback Sound Set Customization

  • l4d2_hitsound.smx sound sets gain two optional config keys to customize when they ring: headshot_kill assigns a dedicated sound for headshot kills (empty = fall back to the headshot sound, i.e. the old behavior), and stack controls whether the set plays every hit/kill event immediately and independently (same-frame stacking). stack defaults to 1 (stacking); only an explicit stack 0 enables the one-sound-per-frame priority merge (kill > headshot > hit).
  • Set 9 "original ding vote plugin" uses these to replicate the old killsound (Dingshot) feel: headshot hit = ding (littlereward), headshot kill = bell (bell_normal), non-headshot hits/kills stay silent; shotgun multi-kills ring once per kill again.
  • The !snd main menu now offers two personal playback settings to all players (stored in the per-server KV file, not the database): "headshot-kill sound on/off" (off = headshot kills fall back to the normal headshot sound) and a three-state "sound playback mode" toggle (follow set / force stacking / force merging). The toggle replies use translation phrases (5 new phrases across zh-CN/zh-TW/en/ja/ko/vi) explaining what each mode does.

August 13, 2026 AI3 Plugin Audit Fixes

  • Upgraded ai_tank3 to 1.0.0.8: OnPlayerRunCmd now aggregates sub-logic results before returning, fixing dropped button edits for the rock-throw distance limit, the anti-headblock forced rock, and the bhop jump keys. Punch vision lock now uses the GameTime clock consistently, rock release height is resolved via animation activity names instead of relying on sequence/enum value coincidence, and the tank_swing_range change hook is no longer re-added on every map change.
  • Upgraded ai_charger3 to 1.0.1.6: fixed the state machine transitionTo never running the old state's OnExit (the guard read the static struct before loading it), so air-brake, retreat-hop, and artificial charge-delay flags now reset correctly on state changes. The player_spawn event is now a Post hook, and clearing jump/duck input on ladders now correctly returns Plugin_Changed.
  • Upgraded ai_boomer_3 to 3.0.6: L4D_OnVomitedUpon now validates the attacker, fixing per-hit errors when the attacker is 0 (bile bombs etc.); the vomit-turn queue is no longer rebuilt while one already exists, preventing duplicate target growth from forced-bile recursion; fixed a trace handle leak on early return in victim selection and a wrong-index bile-state flag in the force-bile loop; the closest-survivor lookup is now cached for 0.2 seconds to cut per-frame sorting cost.
  • Upgraded ai_spitter_3 to 3.0.4: fixed the crowded-target policy copy-paste bug that used the pinned target's position (including a -1 index error), fixed getTargetByPriority leaking its ArrayList whenever someone was pinned or only one survivor remained, allowed a lone survivor to be selected by flow, zero-initialized the crowd-distance accumulator cell, and removed the always-false ray filter dead code.
  • Trace-cost optimization: ai_path_movement.inc gains a jump-route failure throttle — safety checks that fail due to terrain (walls, gaps, dangerous landings) are not retried within a 0.1s cooldown. All four AI ground-hop attempts use it, cutting hull/ray trace cost while an SI is terrain-blocked to roughly 1/6 (at 60 tick), with the biggest win in multi-Charger lineups. AIPathMovement_IsJumpRouteSafe also moves the trace-free no-vision angle rejection ahead of the hull trace, so rejected attempts no longer emit any traces.

August 13, 2026 infected_control Audit Fixes

  • Upgraded infected_control.smx to 2026-08-13.2 with fixes from a full code audit. The wave-timing contract (kill phase, independent 16-second base countdown, unbounded Anti-Bait hold) is unchanged.
  • Fixed double counting of alive specials: full-scan recounts such as RecalcSiCapFromAlive now skip clients already in the kick queue, so teleport kicks no longer prematurely fill class/total caps.
  • Fixed a deadlock where a failed progress export after a dynamic Nav topology rebuild (elevators, etc.) kept spawn searches pending forever; the pending flag is now cleared and legacy flow buckets take over, restoring the Director fallback.
  • Fixed cross-map cache pollution: the survivor-flow fallback value and the per-bucket alive-SI count cache are now reset on round/map cleanup, so a lower GetGameTime on the next map can no longer revive stale data; also freed two ArrayList handles leaked on every bucket build.
  • Hardened the Nav bucket disk cache: a cache whose metadata matches but contains no usable areas is deleted and rebuilt, and a failed export now removes the partial file instead of leaving it for the next load.
  • Fixed two teleport-supervision issues: an unknown spawn time is no longer treated as a permanent spawn grace (specials adopted after a watchdog hard recovery are re-stamped and can be teleported again), and Smokers now accumulate invisibility ticks while their ability is on cooldown instead of restarting the count once ready.
  • Improved the GetNextSpawnTime native: during the kill phase it returns the upper-bound "remaining check window + full base countdown", and while paused it falls back to the frozen wave clock instead of conflating kill-phase time with the base countdown.
  • Anti-Bait state machine redesign: clearing a hold by spreading now requires sustained confirmation — the new inf_antibait_spread_confirm (default 3 seconds) demands the formation stay spread continuously before Pressure demotes. Briefly stepping away and immediately regrouping no longer tricks the intensive hold, and spreading no longer resets the stall clock. A team that regroups while still stalled inside the arm window re-enters Pressure directly as a continuation of the same hold.
  • Decoupled the Anti-Bait progress snapshot: a survivor flow-read failure (elevator, bad Nav) no longer counts as a "vulnerable" member that wrongly blocks or releases holds; such members still participate in formation statistics and are only excluded from flow statistics. Flow baselines rebuild when readings recover, also without resetting the stall clock.
  • Traitor materialization is no longer capped by totalSI: a traitor in the SpawnAllowed state whose release wave has arrived (their slot was already deducted from that wave's AI budget) may materialize even when leftover AI temporarily fills the limit; AI backfilling pauses on its own while over cap.
  • create_panic_event now uses a rolling 60-second window refreshed by every new panic event and cleared immediately by panic_event_finished; this prevents custom maps that never send the finish event from silently bypassing Anti-Bait for the whole round, while continuous/infinite horde scripts keep refreshing the window unaffected.
  • sm_wavestatus and the Nav debug command replies now use translation phrases (12 new phrases across zh-CN/zh-TW/en/ja/ko/vi) instead of hard-coded Chinese.
  • The "healthy survivors + no Tank" mercy gates on the early countdown trigger are confirmed as intentional design and are now documented in the AGENTS.md behavioral contract (together with the spread-confirmation and traitor-exemption semantics).

August 10, 2026 Boomer/Spitter Path-Follow Bhop

  • Upgraded the native anne_nextbot extension to 1.3.0. It remains the single PathFollower::Update detour and now copies bounded path-segment values in native code. Tank, Charger, Boomer, and Spitter read snapshots through natives; SourcePawn no longer receives or reads PathFollower/PathSegment pointers. Shared path signatures and vtable offsets now live only in anne_nextbot.games.txt.
  • Upgraded Tank ai_tank3 to 1.0.0.4 and Charger ai_charger3 to 1.0.1.4, migrating their existing path-follow bhop logic to the same native snapshots. Their gamedata now retains only class-specific claw, charge, and locomotion calls instead of duplicating common PathFollower definitions.
  • Upgraded Boomer to ai_boomer_3.smx 3.0.4 and Spitter to ai_spitter_3.smx 3.0.2. Their bhop movement now prefers a valid path look-ahead goal and falls back to native/direct behavior when the path is stale or blocked, or when a climb, gap jump, or other special traversal is ahead.
  • All four AI plugins now share ai_path_movement.inc for airborne-time prediction, wall/landing/local-gap safety checks, and gradual airborne velocity correction. Charger-specific charge, repath, and lateral-duel decisions remain in its state machine.
  • Boomer can keep approaching around corners while direct sight is temporarily lost when a valid PathFollower route remains available, while retaining the 26-07 AI2 vomit-pitch fix.
  • The active 2026-08 AnneHappy, Hardcore, and Shotgun configs load AI3. Anne26-07.cfg continues to load the unchanged ai_boomer_2.smx and ai_spitter_2.smx as the 26-07 rollback build.

July 26, 2026 Map Records v2

  • l4d_stats.smx now records a general ruleset_key, ruleset version, record policy, and timing/difficulty profiles. Anne modes retain fastest-time leaderboards; co-op and survival rulesets are isolated; versus, realism versus, and scavenge use round history instead of misleading fastest-time comparisons.
  • Added map_runs, map_run_players, and legacy_map_bests, plus repeatable migration and rollback scripts. Existing timedmaps and timedmap_runs stay intact, with legacy per-player bests kept distinct from real runs.
  • NewAnneWeb now reads v2 records across the player profile, map list, and detail pages, with filters for ruleset, version, difficulty/AI level, team size, and Anne spawn settings. Deployment steps are documented in docs/map_records_v2_migration.md.

July 23, 2026 FastDL Map-Transition Stability Fix

  • Removed the l4d2_blackscreen_fix.smx call to the private engine function CNetworkStringTable::DeleteAllStrings(), preventing the downloadables table from being cleared during map loading and potentially crashing or watchdog-restarting the server.
  • Download scheduling is now append-only: each map adds the priority feedback files, while a real transition uses SourceMod's public AddFileToDownloadsTable() API to add the three dance model files and one shuffled song group. No string-table entries are deleted, overwritten, or restored.
  • fornite_l4d.smx retains its original full dance-asset download registration behind the new default-off sm_emotes_add_downloads switch. This server uses the globally loaded l4d2_blackscreen_fix.smx for staged downloads, while other servers can enable the switch to restore one-shot downloads.

July 22, 2026 Dynamic Difficulty PPM Fix

  • Fixed automatic difficulty locking its team PPM snapshot too early at round_start, which could leave players who subsequently switched to spectator in the round's tier calculation.
  • The round-start tier is now provisional. Survivor-roster changes inside the saferoom invalidate that PPM, and the plugin recalculates and locks from the real players still on the Survivor team when the first Survivor leaves. Spectators and bots are excluded.

July 16, 2026 New-Player Download Optimization

  • Reworked FastDL scheduling in l4d2_blackscreen_fix.smx. New players now download hit-feedback icons and non-built-in feedback sounds first, while dance assets are filled in during real map transitions.
  • restrict_strings.cfg is now the first-connect priority list, containing 44 feedback material files and 17 deduplicated feedback sound files. Dance models are no longer downloaded on the initial connection.
  • Added deferred_strings.cfg for the 60 dance songs, split into eight groups by default. Each actual changelevel offers one shuffled group; no group repeats until all eight have been offered, after which the order is shuffled again. sm_fixscreen_deferred_group_count controls the number of groups, and files already present in the client cache are skipped automatically.
  • The three interdependent dance model files are downloaded together on the player's first map transition to prevent partial-model errors. The same transition also offers one random song group, and later transitions continue in shuffled, non-repeating order.
  • All 124 feedback, model, and dance resources are now stored as path-preserving .bz2 files in the NewAnneWeb file manager. FastDL now uses http://anne.trygek.com/fastdl/left4dead2 with Cloudflare edge caching.

July 15, 2026 Dynamic Difficulty Fix

  • Fixed controlled-CVar baseline discovery potentially missing later difficulty sections. The plugin now scans level1 through level6 explicitly, preventing Extreme- or Neri-only values from remaining after switching back to Expert or below.
  • Fixed the legacy ai_boomer_2 retaining a downward pitch after dynamic tiers enabled aim lift/turning, causing Boomers to vomit toward the ground. Initial aiming and multi-target turns now retain at least five degrees of upward pitch without changing bhop or jump-vomit parameters.
  • Removed four obsolete CVars not registered by the active AI plugins. Three hidden/launcher CVars that SourceMod can resolve and modify remain configured.
  • Fixed switching from a fixed tier back to automatic mode retaining Neri/high-tier CVars while PPM data was temporarily unavailable. The plugin now applies Easy as a safe baseline, then selects the automatic tier once statistics are ready.
  • Fixed server-console/RCON sm_aidiff commands being deferred by the saferoom lock, which made operational tier changes appear not to apply. Console and RCON changes now apply immediately, while player-issued admin commands after leaving the saferoom still take effect next round.
  • Added opt-in airborne ability switches for Boomer2 and Smoker3. Both default to off and are enabled only by Neri. Existing bhop behavior continues normally; the switches do not force an extra jump, and only let an AI that is already airborne use vomit or tongue. Ground attacks and activation distances remain unchanged.

July 14, 2026 Dynamic Difficulty Hotfix

  • Fixed the hard-coded level0 profile overriding mode-specific values in 1vHunters, Alone, WitchParty, and other modes. The plugin now captures each mode's effective CVar baseline after configs execute and restores it before tier changes, map changes, and unloads.
  • Removed invalid low-tier reset values such as z_lunge_up 0, z_lunge_interval 0.08, and z_lunge_cooldown 0, fixing Hunters repeatedly crouching or pouncing in place with the legacy ai_hunter_2 plugin and preventing unintended Jockey, Tank, and Boomer overrides.
  • Extreme and Neri retain their explicit overrides; leaving those tiers now restores the active mode's captured baseline.

June 12-July 3, 2026 Update Log

Anne Modes And Spawn System

  • Fixed third-person support in Anne versus-style modes. !tp and related third-person cvars are now applied through the Anne shared mode configs.
  • Continued the infected_control.smx refactor and optimization work: added Left4DHooks PVS/visibility helpers, spawn-performance settings, queue tuning, and wave-decision cleanup to reduce abnormal spawns, double Smoker tongue cases, and overly fast refill waves.
  • Added anne_cvar_shield.smx to protect key Anne cvars, preventing plugin or vote leftovers from leaking one mode's difficulty into another.
  • Added the 26-7 test config Anne25-11.cfg, kept infected_control25-11.smx as a rollback build, and updated the dynamic difficulty notes.
  • Fixed extreme-difficulty values affecting other difficulty levels; dynamic difficulty switches now reset Tank, Hunter, and related tier parameters more explicitly.
  • Fixed cases where saferoom melee weapons might fail to appear, and cleaned up MeleeInTheSafeRoom logic.
  • Reverted spawn_vote_menu.smx to the classic SourceMod menu and removed the external menu dependency. Anne and campaign spawn count, interval, auto mode, and teleport checks now start one vote per selected setting, while presets still vote on the full preset.
  • Added and reorganized the not0721 coop/community/mutation modes, dirspawn presets, weapon configs, and SI limit configs.
  • Added traitor-mode logic, spawn preset tables, and multilingual phrases to infected_control for future gameplay extensions.

Player Experience, Votes And Feedback

  • Adjusted l4d_stats.smx custom-map scoring: ordinary custom maps award saferoom and strong-clear bonuses only after more than five minutes of actual map time, official maps retain their existing scoring, and custom finales do not receive team completion points while map timing, map records, and Special Infected statistics continue to be saved normally.
  • Replaced the old hextags plugin with hextags_lite.smx, fixed title colors, hid admin commands, and moved the config to hextags_lite.cfg.
  • Added more multilingual phrases for global_chat.smx, join.smx, and related messages, including global-chat status and LFG receive-state text.
  • Fixed l4d_stats.smx quarterly ranking and persistence details, added more round-state tracking, and reduced some database/high-peak status write pressure.
  • Merged the old killsound and ding sound voting behavior into l4d2_hitsound.smx; removed the old sound_on/off vote files and unified hit, kill, headshot sounds and icons under the feedback plugin.
  • Updated spechud.sp: added ping display, adjusted bonus percentage breakdowns, and fixed spectator delay display.
  • Disabled basevotes.smx and removed the no-charge Hunter direct vote entry to reduce conflicts with Anne dynamic difficulty and the new vote menu.
  • server_name.smx no longer rewrites the SourceBans description directly; that display is now handled by the external proxy flow.
  • Lowered high-peak unload and NPC-manager polling/prompt frequency to reduce extra load during busy periods.
  • Hid some admin command output to reduce player-facing chat noise.

Upstream Sync, Maps And Core Dependencies

  • Updated Left4DHooks, gamedata, includes, and test sources; added new natives/forwards and rebuilt the affected plugins.
  • Updated ai_tank3.smx, fixed RPG score permission handling and extreme-difficulty callback/parameter issues, and adjusted Tank behavior configs.
  • Synced upstream map and stripper fixes for Dead Center 2025, City 17, No Echo m3, Carried Off cwm1_intro, and more. Applicable zonemod changes were mirrored into zonemod_anne.
  • Synced cwm1_intro hittable/clipwall fixes and updated mapinfo.txt for multiple modes.
  • Removed the Rust server-browser tool and Docker deployment README from this repo; those workflows are now maintained through external tooling/web paths.
  • Updated the basevotes.smx location, SourceMod configs, database fields, and several docs to keep the plugin pack structure cleaner.