diff --git a/README.md b/README.md index 4f176ef..af5d720 100644 --- a/README.md +++ b/README.md @@ -97,10 +97,14 @@ addons/sourcemod/logs/custom_rounds.log ## Версия -`1.3.1` — Автор: deidara.dev +`1.3.2` — Автор: deidara.dev ### Changelog +- **1.3.2** + - Фикс: ТТшник в режиме 1 vs All теперь гарантированно телепортируется на T-спавн (раньше `CS_RespawnPlayer` в CSGO мог оставить уже-живого игрока на CT-спавне, что ломало возможность покупки) + - Фикс: после окончания 1vAll команды восстанавливаются физически — добавлен `Timer_FinalTeamRestore`, который телепортирует игроков на спавны их исходных команд + - Добавлен хелпер `TeleportToTeamSpawn` (использует `info_player_terrorist` / `info_player_counterterrorist` энтити) - **1.3.1** - Фикс компиляции: `CSRoundEnd_RoundDraw` → `CSRoundEnd_Draw` (правильное имя константы в `cstrike.inc`) - **1.3.0** diff --git a/scripting/ArcaneGame_CustomRounds_Core.sp b/scripting/ArcaneGame_CustomRounds_Core.sp index 3b227e2..5b5ab8b 100644 --- a/scripting/ArcaneGame_CustomRounds_Core.sp +++ b/scripting/ArcaneGame_CustomRounds_Core.sp @@ -33,7 +33,7 @@ public Plugin myinfo = name = "ArcaneGame Custom Rounds Core", author = "deidara.dev", description = "Core plugin for custom rounds with AG Coin integration", - version = "1.3.1", + version = "1.3.2", url = "https://deidara.dev" }; @@ -618,7 +618,9 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) ResetAllPlayerStats(); - // Восстанавливаем команды после предыдущего 1vAll (до спавна игроков) + // Восстанавливаем команды после предыдущего 1vAll + // CS_SwitchTeam меняем сразу (чтобы спавн использовал нужную команду), + // а телепорт делаем через таймер после того как игроки заспавнились if (g_PendingTeamRestore) { for (int i = 1; i <= MaxClients; i++) @@ -632,10 +634,11 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { CS_SwitchTeam(i, g_OriginalTeam[i]); } - g_OriginalTeam[i] = 0; } - g_PendingTeamRestore = false; - g_OneVsAllTUserId = 0; + + // Через 0.4с телепортируем всех на спавны их исходных команд + // (к этому моменту CSGO уже отработает игровой спавн) + CreateTimer(0.4, Timer_FinalTeamRestore, _, TIMER_FLAG_NO_MAPCHANGE); } if (g_PendingRound != CR_None) @@ -921,6 +924,18 @@ public Action Timer_OneVsAll_ApplyLoadouts(Handle timer, any tUserId) continue; } + int targetTeam = (i == chosenT) ? CS_TEAM_T : CS_TEAM_CT; + + // Гарантированно убеждаемся что игрок на правильной команде + if (GetClientTeam(i) != targetTeam) + { + CS_SwitchTeam(i, targetTeam); + } + + // Принудительно телепортируем на спавн нужной команды + // (CS_RespawnPlayer в CSGO не переносит уже живого игрока) + TeleportToTeamSpawn(i, targetTeam); + StripPlayerWeapons(i); GivePlayerItem(i, "weapon_knife"); @@ -954,6 +969,44 @@ public Action Timer_OneVsAll_ApplyLoadouts(Handle timer, any tUserId) return Plugin_Stop; } +public Action Timer_FinalTeamRestore(Handle timer) +{ + if (!g_PendingTeamRestore) + { + return Plugin_Stop; + } + + for (int i = 1; i <= MaxClients; i++) + { + if (!IsClientInGame(i) || IsFakeClient(i)) + { + continue; + } + if (g_OriginalTeam[i] < CS_TEAM_T) + { + continue; + } + + // Дополнительная проверка команды + if (GetClientTeam(i) != g_OriginalTeam[i]) + { + CS_SwitchTeam(i, g_OriginalTeam[i]); + } + + // Телепортируем на спавн исходной команды если игрок жив + if (IsPlayerAlive(i)) + { + TeleportToTeamSpawn(i, g_OriginalTeam[i]); + } + + g_OriginalTeam[i] = 0; + } + + g_PendingTeamRestore = false; + g_OneVsAllTUserId = 0; + return Plugin_Stop; +} + int ResolveOneVsAllTarget() { // 1) Если админ выбрал конкретного — пробуем его @@ -998,6 +1051,58 @@ int CountActivePlayers() return count; } +// Телепортирует игрока на случайный спавн нужной команды. +// CS_RespawnPlayer в CSGO не всегда переносит уже живого игрока, поэтому делаем это вручную. +bool TeleportToTeamSpawn(int client, int team) +{ + if (!IsClientInGame(client) || !IsPlayerAlive(client)) + { + return false; + } + if (team != CS_TEAM_T && team != CS_TEAM_CT) + { + return false; + } + + char classname[64]; + if (team == CS_TEAM_T) + { + strcopy(classname, sizeof(classname), "info_player_terrorist"); + } + else + { + strcopy(classname, sizeof(classname), "info_player_counterterrorist"); + } + + int spawns[128]; + int count = 0; + int ent = -1; + while ((ent = FindEntityByClassname(ent, classname)) != -1 && count < sizeof(spawns)) + { + // Пропускаем отключённые spawn points (если у энтити есть такой проп) + if (HasEntProp(ent, Prop_Data, "m_bDisabled") && GetEntProp(ent, Prop_Data, "m_bDisabled") != 0) + { + continue; + } + spawns[count++] = ent; + } + + if (count == 0) + { + return false; + } + + int spawnEnt = spawns[GetRandomInt(0, count - 1)]; + + float pos[3], ang[3]; + GetEntPropVector(spawnEnt, Prop_Data, "m_vecAbsOrigin", pos); + GetEntPropVector(spawnEnt, Prop_Data, "m_angRotation", ang); + + float zeroVel[3]; + TeleportEntity(client, pos, ang, zeroVel); + return true; +} + public Action Timer_ApplyModeToClient(Handle timer, any userid) { int client = GetClientOfUserId(userid);