C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//Requires: EventHelper
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/*
* Add command to create crate location like in SurvivalArena.*/
/* 1.1.18
* Updated pooling.*/
namespace Oxide.Plugins {
[Info("HungerGames", "imthenewguy", "1.1.18")]
[Description("Hunger games")]
class HungerGames : RustPlugin {
[PluginReference]
private Plugin EventHelper, NightVision, Economics, ServerRewards;
#region Config
private Configuration config;
public class Configuration {
[JsonProperty("Time after initiation before game begins (spent in the lobby) [seconds]")]
public int Delay_Before_Start = 300;
[JsonProperty("Delay after Hungergames starts before initiating the circle [seconds]")]
public float Delay_Before_Circle_Start = 60;
[JsonProperty("How often should we check if a player is outside the zone.")]
public float rad_Delay = 3f;
[JsonProperty("How much should the rads increase each time?")]
public float rad_amount = 3f;
[JsonProperty("Ring shrink percentage")]
public float ring_shrink_dist_amount = 0.5f;
[JsonProperty("Time after circle spawn before it starts building radiation [seconds]?")]
public float radiation_start_delay = 20f;
[JsonProperty("Seconds between ring shrinking events")]
public float ring_shrink_timer = 1f;
[JsonProperty("Outer distance")]
public float max_distance = 230f;
[JsonProperty("Dome darkness value")]
public int dome_Darkness = 8;
[JsonProperty("Minimum ring size")]
public int minimum_ring_size = 20;
[JsonProperty("Announce player deaths?")]
public bool report_deaths = true;
[JsonProperty("Heal a player when they kill another player?")]
public bool heal_player_on_kill = true;
[JsonProperty("How much health?")]
public float health_on_kill = 50f;
[JsonProperty("Minimum players to start a game?")]
public int minimum_players = 2;
[JsonProperty("Command to join the game.")]
public string join_command = "hg";
[JsonProperty("Command to leave the game.")]
public string leave_command = "leave";
[JsonProperty("How often should we run Hunger Games (set to 0 if you do not want auto start)? [seconds]")]
public float auto_start_time = 3600;
[JsonProperty("Despawn bodies when the game ends?")]
public bool despawn_on_game_end = true;
[JsonProperty("Should we teleport or kill players who are in the lobby after the game has started [true = teleport, false = kill]")]
public bool teleport_afk = true;
[JsonProperty("Remove players from their teams when the game starts?")]
public bool remove_teams = false;
[JsonProperty("How many rolls on the reward should a winner get?")]
public int prize_quantity = 1;
[JsonProperty("Clear data on new wipe?")]
public bool clear_data_on_wipe = true;
[JsonProperty("Maximum time for a game to run (seconds)")]
public float max_run_time = 2700f;
[JsonProperty("Broadcast when a player joins the event?")]
public bool broadcast_join = true;
[JsonProperty("Commands to prevent when a player is in hungergames")]
public string[] prevent_commands = {
"kit"
}
;
[JsonProperty("Slay modifier.Default 0.7.Change to 0.5 if players who are not in the lobby when the bubble spawns are being slain")]
public double slay_y_modifier = 0.7;
[JsonProperty("Remove animals that spawn on the island?")]
public bool remove_animals = true;
[JsonProperty("Prevent mineable resource nodes from spawning on the island")]
public bool remove_nodes = true;
[JsonProperty("Minimum number of loot items per container")]
public int min_loot_amount = 3;
[JsonProperty("Maximum number of loot items per container")]
public int max_loot_amount = 3;
[JsonProperty("Loot respawn delay min [seconds]")]
public float respawn_delay_min = 60;
[JsonProperty("Loot respawn delay max [seconds]")]
public float respawn_delay_max = 180;
[JsonProperty("Run the game automatically with EventHelper?")]
public bool use_event_helper = true;
[JsonProperty("Give items back on respawn [set to false if you are running kits on spawn]")]
public bool give_items_back_on_spawn = true;
[JsonProperty("Delete all backpacks after the game ends?")]
public bool delete_all_backpacks = false;
[JsonProperty("[Requires Economics] Award players with economic dollars for winning?")]
public EconomicsInfo give_economics = new EconomicsInfo();
[JsonProperty("Use the loot list from the config?")]
public bool use_config_loot = true;
[JsonProperty("[Requires Server Rewards] Award players with server reward points for winning?")]
public SRPInfo give_srp = new SRPInfo();
[JsonProperty("Commands to fire when a player wins the event [{id} {name} can be used to target the winner]")]
public List<string> commands_on_win = new List<string>();
[JsonProperty("Prize information")]
public List<PrizesInfo> prizes = new List<PrizesInfo>();
[JsonProperty("Game loot profiles")]
public Dictionary<string, Dictionary<string, int>> loot_profiles = new Dictionary<string, Dictionary<string, int>>();
[JsonProperty("Loot crate spawn positions")]
public List<Vector3> loot_spawn_locations = new List<Vector3>();
[JsonProperty("AFK Spawn locations")]
public List<Vector3> afk_spawn_locations = new List<Vector3>();
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
protected override void LoadDefaultConfig() {
config = new Configuration();
config.loot_spawn_locations = default_loot_spawns;
config.loot_profiles = default_loot_profiles;
config.prizes = default_prizes;
config.afk_spawn_locations = defaultAFKSpawns;
}
public class SRPInfo {
public bool enabled = false;
public int min_srp = 100;
public int max_srp = 200;
}
public class EconomicsInfo {
public bool enabled = false;
public double min_economics = 100;
public double max_economics = 200;
}
List<PrizesInfo> default_prizes {
get {
return new List<PrizesInfo>() {
new PrizesInfo("scrap", 0, 250, 400), new PrizesInfo("wood", 0, 4000, 6000), new PrizesInfo("stones", 0, 4000, 6000), new PrizesInfo("sulfur", 0, 1000, 2000)
}
;
}
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) {
throw new JsonException();
}
if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) {
PrintToConsole("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch {
PrintToConsole($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
}
protected override void SaveConfig() {
PrintToConsole($"Configuration changes saved to {Name}.json");
Config.WriteObject(config, true);
}
#endregion
#region Localization
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["hg-prefix"]
= "<color=#DFF008>[Hunger Games]</color>", ["hg1"] = "<color=#DFF008>[Hunger Games]</color> {0} has won the event!", ["hg2"] = "You have won the game!\nType <color=#9CF817>/claimprize</color> to receive your reward.", ["hg3"] = "<color=#DFF008>[Hunger Games]</color> Everyone died!", ["hg4"] = "You have <color=#9CF817>{0}</color> prizes left to claim.\nType <color=#9CF817>/claimprize</color> to receive it.", ["hg5"] = "{0} Failed to setup.Reload the plugin and try again.", ["hg6"] = "{0} Will be starting shortly.The game will begin in <color=#D1FE07>{1}</color> seconds if there are enough players.Type <color=#F85CFF>/{2}</color> in chat to join.Your items will be stored until the event ends.", ["hg7"] = "{0} Game starting in <color=#F85CFF>{1}</color> seconds (<color=#F85CFF>{2}/{3}</color>).Type <color=#F85CFF>/hg</color> in chat to join.", ["hg8"] = "{0} Joining closed.", ["hg9"] = "Game starting in 10 seconds.", ["hg10"] = "{0} Hungergames cancelled due to lack of players in the event ({1}/{2}).", ["hg11"] = "Game starting - find weapons and be the last man standing.\nMay the odds be ever in you favor.", ["hg12"] = "{0} You have been slain for staying in the lobby.", ["hg13"] = "{0} You were removed from your team.", ["hg14"] = "Particinapnts:{0}", ["hg15"] = "Added position {0} as a potential end zone.", ["hg16"] = "{0} You do not have any oustanding prizes to claim.", ["hg17"] = "{0} You cannot use this command while an event is running.", ["hg18"] = "{0} Your item was dropped to the ground as there is no room in your inventory.", ["hg25"] = "{0} There is already an event running.", ["hg26"] = "{0} There are no games running.", ["hg27"] = "{0} You are no longer allowed to join this event.", ["hg28"] = "{0} Could not join event.Check print out from Inventory Manager for more info.", ["hg29"] = "{0} Ending Hungergames", ["hg30"] = "{0} <color=#27E5FC>{1}</color> joined the event.There are now (<color=#F85CFF>{2}/{3}</color>) on the roster.", ["hg31"] = "{0} You have been added to the Hunger Games roster.Get to an elevator quick!", ["hg32"] = "The command {0} is not available while playing Hunger Games.", ["hg33"] = "{0} You left the game.", ["hg34"] = "<color=#15F5E4>{0}</color> has been eliminated.Only <color=#F5AA15>{1}</color> players remain!", ["hg35"] = "Game has gone on too long...", ["hg36"] = "Circle radiation has started.", ["hg37"] = "The circle final circle has been created.", ["hg38"] = "The circle has started to close.", ["hg39"] = "{0} You were awarded {1}x {2} to {3}.", ["hg40"] = "{0} No item definition found with the shortname: {1}.", ["hg41"] = "{0} You have been teleport for staying in the lobby.", ["hg42"] = "{0} You are still in the lobby and will be {1} in {2} seconds.", ["hg43"] = "removed from the game", ["hg44"] = "teleported into the game", ["GivenEconomics"] = "You received <color=#fbff00>{0}</color> economic dollars.", ["GivenSRP"] = "You received <color=#fbff00>{0}</color> server reward points.", ["InRadZone"] = "You are in the radiation zone!"
}
, this);
}
#endregion
#region Data
PlayerEntity pcdData;
private DynamicConfigFile PCDDATA;
private TimerInfo TimerData;
private List<BasePlayer> Participants = new List<BasePlayer>();
public bool IsRunning = false;
public string loot_profile;
List<ulong> OutofBounds = new List<ulong>();
public string Prefix;
private bool AllowJoin;
private List<PlayerCorpse> Corpses = new List<PlayerCorpse>();
void Init() {
PCDDATA = Interface.Oxide.DataFileSystem.GetFile("HungerGames");
permission.RegisterPermission("hungergames.admin", this);
permission.RegisterPermission("hungergames.list", this);
TimerData = new TimerInfo();
Unsubscribe("OnPlayerCorpseSpawned");
Unsubscribe("CanEntityTakeDamage");
Unsubscribe("OnElevatorButtonPress");
Unsubscribe("OnPlayerDeath");
Unsubscribe("OnPlayerMetabolize");
Unsubscribe("OnPlayerDisconnected");
Unsubscribe("OnPlayerCommand");
Unsubscribe("CanRedeemKit");
Unsubscribe("OnEntityKill");
Unsubscribe("OnLootSpawn");
Unsubscribe("CanPopulateLoot");
Unsubscribe("OnContainerPopulate");
}
void Unload() {
destroyspheres();
EndHungerGames();
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, "BleedingPanel");
}
if (config.delete_all_backpacks) DeleteBackpacks();
EventHelper.Call("EMRemoveEvent", this.Name);
SaveData();
cmd.RemoveChatCommand(config.join_command, this);
cmd.RemoveChatCommand(config.leave_command, this);
cmd.RemoveChatCommand("starthg", this);
cmd.RemoveChatCommand("addprize", this);
cmd.RemoveChatCommand("removeprize", this);
cmd.RemoveChatCommand("listprizes", this);
cmd.RemoveChatCommand("claimprize", this);
cmd.RemoveChatCommand("endhg", this);
cmd.RemoveChatCommand("addfinalpoint", this);
cmd.RemoveChatCommand("hgplayers", this);
cmd.RemoveChatCommand("resethgdata", this);
}
void Loaded() {
LoadData();
LoadConfig();
}
void SaveData() {
PCDDATA.WriteObject(pcdData);
}
void LoadData() {
try {
pcdData = Interface.Oxide.DataFileSystem.ReadObject<PlayerEntity>("HungerGames");
}
catch {
Puts("Couldn't load player data, creating new Playerfile");
pcdData = new PlayerEntity();
}
}
class PlayerEntity {
public List<Vector3> LobbyLocations = new List<Vector3>();
public Vector3 Top_Centre_Point;
public Dictionary<ulong, PlayerInfo> pentity = new Dictionary<ulong, PlayerInfo>();
public Dictionary<string, ulong> ButtonIDs = new Dictionary<string, ulong>();
public bool GameStarted;
public List<Vector3> centre_points = new List<Vector3>();
public Vector3 chosen_centre;
public List<Vector3> afkSpawnLocations = new List<Vector3>();
}
class PlayerInfo {
public int prizes = 0;
}
class TimerInfo {
public Timer circleShrinkTimer;
public bool circleShrinkTimerPause = false;
public Timer circleDamageTimer;
public Timer LobbyTimer;
public Timer RecruitTimer;
public Timer StartTimer;
public Timer RosterTimer;
public Timer AutoStartTimer;
public Timer RadsDelayTimer;
public Timer MaxRunTimer;
}
public class PrizesInfo {
public string shortName;
public ulong skinID;
public int min_amount;
public int max_amount;
public string displayName;
public PrizesInfo(string shortname, ulong skinID, int min_amount, int max_amount, string displayName = null) {
this.shortName = shortname;
this.skinID = skinID;
this.min_amount = min_amount;
this.max_amount = max_amount;
this.displayName = displayName;
}
}
PlayerInfo playerData;
#endregion
#region HungerGames IO & Vectors
public PressButton start_button;
public PressButton call_ele_button;
private bool GetLobbyLocations() {
if (pcdData.ButtonIDs.Count != 2) FindButtonIDs();
if (start_button == null || call_ele_button == null) StoreButtonEntities();
pcdData.LobbyLocations.Clear();
var StartButtonLoc = start_button.transform.position;
var x = StartButtonLoc.x - 6.614f;
var y = StartButtonLoc.y - 3.6f;
var z = StartButtonLoc.z - 2.478f;
pcdData.LobbyLocations.Add(new Vector3(x, y, z));
x = StartButtonLoc.x - 3.614f;
y = StartButtonLoc.y - 3.6f;
z = StartButtonLoc.z + 7.722f;
pcdData.LobbyLocations.Add(new Vector3(x, y, z));
x = StartButtonLoc.x + 1.886f;
y = StartButtonLoc.y - 3.6f;
z = StartButtonLoc.z + 8.222f;
pcdData.LobbyLocations.Add(new Vector3(x, y, z));
x = StartButtonLoc.x + 6.286f;
y = StartButtonLoc.y - 3.6f;
z = StartButtonLoc.z + 8.222f;
pcdData.LobbyLocations.Add(new Vector3(x, y, z));
x = StartButtonLoc.x + 1.744f;
y = StartButtonLoc.y + 17.867f;
z = StartButtonLoc.z - 0.326f;
pcdData.Top_Centre_Point = new Vector3(x, y, z);
if (pcdData.centre_points.Count > 0) {
if (Vector3.Distance(pcdData.Top_Centre_Point, pcdData.centre_points.First()) > 100) {
pcdData.centre_points.Clear();
pcdData.centre_points.Add(pcdData.Top_Centre_Point);
}
}
SaveData();
return true;
}
List<Vector3> defaultAFKSpawns {
get {
return new List<Vector3>() {
new Vector3(61.667f, 28.943f, -21.591f), new Vector3(116.372f, 24.06f, 3.967f), new Vector3(146.17f, 23.729f, 21.683f), new Vector3(146.373f, 20.197f, 98.021f), new Vector3(92.711f, 23.37f, 102.071f), new Vector3(-30.303f, 30.53f, 111.723f), new Vector3(-170.404f, 18.663f, 35.467f), new Vector3(-69.87f, 21.25f, -51.549f), new Vector3(28.151f, 21.481f, -124.357f), new Vector3(55.763f, 26.601f, -153.413f), new Vector3(121.507f, 18.978f, -54.501f)
}
;
}
}
private void FindButtonIDs() {
var found = 0;
foreach (var pookie in BaseNetworkable.serverEntities) {
if (!pookie.ShortPrefabName.Contains("pookie")) continue;
foreach (var entity in BaseNetworkable.serverEntities) {
var button = entity as PressButton;
if (button == null) continue;
var dist = Vector3.Distance(pookie.transform.position, button.transform.position);
if (dist <= 0.80) {
start_button = button as PressButton;
pcdData.ButtonIDs.Add("start", button.net.ID.Value);
SaveData();
found++;
Puts($"Found start button at: {button.transform.position}");
continue;
}
if (dist >= 1.20 && dist <= 2.6) {
call_ele_button = button as PressButton;
if (!pcdData.ButtonIDs.ContainsKey("ele")) pcdData.ButtonIDs.Add("ele", button.net.ID.Value);
else pcdData.ButtonIDs["ele"] = button.net.ID.Value;
SaveData();
found++;
Puts($"Found ele button at: {button.transform.position}");
}
}
if (found == 2) {
Puts($"Successfully found and stored both buttons.");
return;
}
}
Puts("Failed to find IO entities");
}
private void StoreButtonEntities() {
foreach (var entity in BaseNetworkable.serverEntities) {
var button = entity as PressButton;
if (button == false) continue;
if (button.net.ID.Value == pcdData.ButtonIDs["start"]) {
start_button = button;
continue;
}
if (button.net.ID.Value == pcdData.ButtonIDs["ele"]) {
call_ele_button = button;
continue;
}
}
if (start_button == null || call_ele_button == null) {
LoadError();
}
}
#endregion
#region Hooks
void OnEntitySpawned(BaseAnimalNPC entity) {
if (!config.remove_animals) return;
if (Vector3.Distance(pcdData.Top_Centre_Point, entity.transform.position) < config.max_distance) {
timer.Once(0.1f, () => {
if (entity != null) entity.KillMessage();
}
);
}
}
void OnEntitySpawned(ResourceEntity entity) {
if (!config.remove_nodes) return;
if ((entity.resourceDispenser.gatherType == ResourceDispenser.GatherType.Ore) && Vector3.Distance(pcdData.Top_Centre_Point, entity.transform.position) < config.max_distance) {
NextTick(() => {
if (entity != null) entity.KillMessage();
}
);
}
}
void OnPlayerCorpseSpawned(BasePlayer player, PlayerCorpse corpse) {
if (!IsRunning) return;
if (Vector3.Distance(corpse.transform.position, pcdData.chosen_centre) < config.max_distance) {
Corpses.Add(corpse);
}
}
object OnElevatorButtonPress(ElevatorLift lift, BasePlayer player, Elevator.Direction direction, bool toTopOrBottom) {
if (pcdData.LobbyLocations.Count == 0) return null;
if (Vector3.Distance(lift.transform.position, pcdData.LobbyLocations.First()) < 25) return false;
return null;
}
[ChatCommand("lootpos")]
void CheckLootPositions(BasePlayer player) {
var loot_positions = ConvertLocalsToWorld(config.loot_spawn_locations);
for (int i = 0; i < loot_positions.Count; i++) {
player.SendConsoleCommand("ddraw.text", 10f, Color.yellow, loot_positions[i], $"Loot[{i}]");
}
}
void OnServerInitialized(bool initial) {
Prefix = lang.GetMessage("hg-prefix", this);
cmd.AddChatCommand(config.join_command, this, "JoinEvent");
cmd.AddChatCommand(config.leave_command, this, "LeaveCMD");
cmd.AddChatCommand("starthg", this, "StartHungerGames");
cmd.AddChatCommand("addprize", this, "AddPrize");
cmd.AddChatCommand("removeprize", this, "RemovePrize");
cmd.AddChatCommand("listprizes", this, "PrintPrizeToConsole");
cmd.AddChatCommand("claimprize", this, "ClaimPrize");
cmd.AddChatCommand("endhg", this, "EndHG");
cmd.AddChatCommand("addfinalpoint", this, "AddEndPoint");
cmd.AddChatCommand("hgplayers", this, "ListParticipants");
cmd.AddChatCommand("resethgdata", this, "ResetData");
if (!config.remove_animals && !config.remove_nodes) Unsubscribe("OnEntitySpawned");
var saveConfig = false;
if (config.afk_spawn_locations.Count == 0) {
config.afk_spawn_locations = defaultAFKSpawns;
saveConfig = true;
}
allowRads = false;
CheckAndSetup();
if (start_button == null || call_ele_button == null) {
Puts("Failed to get button entities.Unloading...");
Interface.Oxide.UnloadPlugin(Name);
return;
}
if (config.loot_spawn_locations == null || config.loot_spawn_locations.Count == 0) {
config.loot_spawn_locations = default_loot_spawns;
saveConfig = true;
}
loot_positions = ConvertLocalsToWorld(config.loot_spawn_locations);
if (loot_positions.Count == 0) {
Puts("Failed to get world positions for our crates.Unloading...");
Interface.Oxide.UnloadPlugin(Name);
return;
}
var boxes = BaseNetworkable.serverEntities.Where(x => x.ShortPrefabName == "crate_normal_2" && Vector3.Distance(x.transform.position, start_button.transform.position) < config.max_distance).ToList();
foreach (var box in boxes.ToList()) {
box.KillMessage();
}
if (config.loot_profiles.Count == 0) {
config.loot_profiles = default_loot_profiles;
saveConfig = true;
}
if (saveConfig) SaveConfig();
EventHelper.Call("EMCreateEvent", this.Name, config.use_event_helper, true, true, true, config.give_items_back_on_spawn, true, pcdData.LobbyLocations.First());
EventHelper.Call("EMExternalPluginSettings", this.Name);
EventHelper.Call("EMBlackListCommands", this.Name, config.prevent_commands);
if (!config.use_event_helper) {
if (config.auto_start_time > 0) TimerData.AutoStartTimer = timer.Every(config.auto_start_time, () => {
Puts("Auto starting Hunger Games");
InitiateGame();
}
);
}
if (config.prizes == null || config.prizes.Count == 0) {
config.prizes = default_prizes;
Puts("Updating prizes");
SaveConfig();
}
}
List<Vector3> loot_positions = new List<Vector3>();
void CheckAndSetup() {
if (pcdData == null || pcdData.LobbyLocations.Count == 0 || pcdData.Top_Centre_Point == null || pcdData.ButtonIDs.Count != 2 || start_button == null || call_ele_button == null) GetLobbyLocations();
if (start_button == null || call_ele_button == null) {
StoreButtonEntities();
}
if (call_ele_button == null) {
LoadError();
return;
}
if (pcdData.afkSpawnLocations.Count == 0) {
GetAFKSpawnLocations();
}
call_ele_button.Press();
if (pcdData.centre_points.Count == 0) {
pcdData.centre_points.Add(pcdData.Top_Centre_Point);
pcdData.chosen_centre = pcdData.centre_points.First();
SaveData();
}
if (config.remove_animals || config.remove_nodes) {
foreach (var entity in BaseNetworkable.serverEntities) {
if (entity is BaseAnimalNPC && config.remove_animals && Vector3.Distance(pcdData.Top_Centre_Point, entity.transform.position) < config.max_distance) NextTick(() => entity.KillMessage());
if (entity is ResourceEntity && config.remove_nodes && (entity as ResourceEntity).resourceDispenser.gatherType == ResourceDispenser.GatherType.Ore && Vector3.Distance(pcdData.Top_Centre_Point, entity.transform.position) < config.max_distance) NextTick(() => entity.KillMessage());
}
}
}
void GetAFKSpawnLocations() {
if (config.afk_spawn_locations == null || config.afk_spawn_locations.Count == 0) {
config.afk_spawn_locations = defaultAFKSpawns;
SaveConfig();
}
foreach (var spawn in config.afk_spawn_locations) {
pcdData.afkSpawnLocations.Add(ConvertLocalsToWorld(spawn));
}
SaveData();
}
Vector3 ConvertLocalsToWorld(BaseNetworkable anchor_entity, Vector3 loc) {
var pos = anchor_entity.transform.localToWorldMatrix.MultiplyPoint3x4(loc);
return pos;
}
void ResetData(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, "hungergames.admin")) return;
pcdData.LobbyLocations.Clear();
pcdData.GameStarted = false;
pcdData.ButtonIDs.Clear();
SaveData();
CheckAndSetup();
}
void CheckWin() {
if (!pcdData.GameStarted || !IsRunning) return;
if (Participants.Count == 1) {
var winner = Participants.First();
PrintToChat(string.Format(lang.GetMessage("hg1", this), winner.displayName));
PrintToChat(winner, lang.GetMessage("hg2", this, winner.UserIDString));
LogToFile("GameRecords", $"[{DateTime.Now}] Winner: {winner.displayName}", this, true);
if (!pcdData.pentity.ContainsKey(winner.userID)) pcdData.pentity.Add(winner.userID, new PlayerInfo());
pcdData.pentity[winner.userID].prizes++;
SaveData();
winner.metabolism.radiation_level.value = winner.metabolism.radiation_level.min;
winner.metabolism.radiation_poison.value = winner.metabolism.radiation_poison.min;
Interface.CallHook("HGWinner", winner);
if (!config.commands_on_win.IsNullOrEmpty()) {
foreach (var command in config.commands_on_win) {
var fixedString = command.Replace("{id}", winner.UserIDString);
fixedString = fixedString.Replace("{name}", winner.displayName);
var allArgs = fixedString.Split(' ');
var c = allArgs[0];
string[] args = null;
if (allArgs.Length > 1) args = allArgs.Skip(1).ToArray();
Server.Command(allArgs[0], args);
}
}
timer.Once(5f, () => {
EndHungerGames();
}
);
}
if (IsRunning) {
if (Participants.Count == 0) {
PrintToChat(lang.GetMessage("hg3", this));
EndHungerGames();
}
}
}
void OnPlayerDeath(BasePlayer player, HitInfo info) {
if (!Participants.Contains(player)) return;
CuiHelper.DestroyUi(player, "BleedingPanel");
if (NightVision != null) {
NightVision.Call("UnlockPlayerTime", player);
}
if (config.heal_player_on_kill && info != null) {
var attacker = info.InitiatorPlayer;
if (attacker != null) {
if (Participants.Contains(attacker) && attacker.IsConnected) attacker.health = (attacker.health + config.health_on_kill);
}
}
Participants.Remove(player);
if (config.report_deaths) {
MessageParticipants(string.Format(lang.GetMessage("hg34", this), player.displayName, Participants.Count));
}
CheckWin();
return;
}
void HandleBan(BasePlayer player) {
if (player.IsConnected) player.Kick("Removed from the event for dying.");
ServerUsers.Set(player.userID, ServerUsers.UserGroup.Banned, string.Empty, string.Empty);
ServerUsers.Save();
}
void OnPlayerRespawned(BasePlayer player) {
CuiHelper.DestroyUi(player, "BleedingPanel");
if (Convert.ToBoolean(EventHelper.Call("EMIsParticipating", player, this.Name))) {
EventHelper.Call("EMPlayerLeaveEvent", player, this.Name);
}
if (pcdData.pentity.TryGetValue(player.userID, out playerData)) {
if (playerData.prizes > 0) PrintToChat(player, string.Format(lang.GetMessage("hg4", this, player.UserIDString), playerData.prizes));
else pcdData.pentity.Remove(player.userID);
}
}
void OnPlayerMetabolize(PlayerMetabolism metabolism, BaseCombatEntity entity, float delta) {
if (Participants.Count == 0) return;
var player = entity as BasePlayer;
if (player == null) return;
if (Participants.Contains(player)) {
if (player.IsDead()) return;
if (PlayerOutOfCircle(player)) {
if (!OutofBounds.Contains(player.userID)) {
CuiHelper.DestroyUi(player, "BleedingPanel");
BleedingPanel(player);
OutofBounds.Add(player.userID);
}
}
else {
if (OutofBounds.Contains(player.userID)) {
CuiHelper.DestroyUi(player, "BleedingPanel");
OutofBounds.Remove(player.userID);
}
}
}
}
void OnPlayerDisconnected(BasePlayer player, string reason) {
if (Participants.Contains(player)) {
player.Hurt(500);
}
}
void OnNewSave(string filename) {
pcdData.LobbyLocations.Clear();
pcdData.GameStarted = false;
pcdData.ButtonIDs.Clear();
pcdData.afkSpawnLocations.Clear();
if (config.clear_data_on_wipe) pcdData.pentity.Clear();
SaveData();
}
#endregion
#region GamePlay
public float CircleDist;
public bool allowRads = false;
public bool CircleSpawned = false;
private void InitiateCircleTimers() {
CircleSpawned = true;
TimerData.MaxRunTimer = timer.Once(config.max_run_time, () => {
MessageParticipants(lang.GetMessage("hg35", this));
EndHungerGames();
}
);
TimerData.RadsDelayTimer = timer.Once(config.radiation_start_delay, () => {
allowRads = true;
MessageParticipants(lang.GetMessage("hg36", this));
}
);
TimerData.circleDamageTimer = timer.Every(config.rad_Delay, () => {
if (allowRads) {
foreach (var player in Participants) {
if (!PlayerOutOfCircle(player)) continue;
IncreaseRadiation(player);
}
}
}
);
CreateSphere(pcdData.chosen_centre, CircleDist, config.dome_Darkness, config.ring_shrink_dist_amount);
TimerData.circleShrinkTimer = timer.Every(config.ring_shrink_timer, () => {
if (TimerData.circleShrinkTimerPause) return;
var shrinkDist = CircleDist - config.ring_shrink_dist_amount;
if ((shrinkDist) > config.minimum_ring_size) {
CircleDist = shrinkDist;
}
else {
TimerData.circleShrinkTimerPause = true;
CircleDist = config.minimum_ring_size;
destroyspheres();
CreateSphere(pcdData.chosen_centre, CircleDist, config.dome_Darkness, 0f);
MessageParticipants(lang.GetMessage("hg37", this));
}
}
);
}
private void InitiateGame(int TimeOverride = 0, string profile = null) {
if (!IsIOSetup()) {
PrintToChat(string.Format(lang.GetMessage("hg5", this), Prefix));
return;
}
if (IsRunning) {
Puts("Attempted to start a game but it is already running.");
return;
}
LogToFile("GameRecords", $"[{DateTime.Now}] Attempting to start a new game", this, true);
var waitTime = 0;
if (TimeOverride > 0) {
waitTime = TimeOverride;
if (config.auto_start_time > 0) {
if (TimerData.AutoStartTimer != null && !TimerData.AutoStartTimer.Destroyed) TimerData.AutoStartTimer.Destroy();
if (config.auto_start_time > 0) TimerData.AutoStartTimer = timer.Every(config.auto_start_time, () => {
Puts("Auto starting Hunger Games");
InitiateGame();
}
);
}
}
else waitTime = config.Delay_Before_Start;
Subscribe("OnPlayerDisconnected");
Subscribe("OnElevatorButtonPress");
Subscribe("OnPlayerDeath");
Subscribe("OnPlayerCorpseSpawned");
Subscribe("OnPlayerCommand");
Subscribe("CanRedeemKit");
Subscribe("OnContainerPopulate");
EventHelper.Call("EMStartEvent", this.Name);
AllowJoin = true;
IsRunning = true;
loot_profile = config.loot_profiles.Keys.ToList().GetRandom();
call_ele_button.Press();
pcdData.chosen_centre = pcdData.centre_points.GetRandom();
PrintToChat(string.Format(lang.GetMessage("hg6", this), Prefix, waitTime, config.join_command));
var time = 0;
CircleDist = config.max_distance;
TimerData.RecruitTimer = timer.Repeat(1f, waitTime, () => {
time++;
if (time == waitTime - 120) PrintToChat(string.Format(lang.GetMessage("hg7", this), Prefix, 120, Participants.Count, config.minimum_players));
if (time == waitTime - 60) PrintToChat(string.Format(lang.GetMessage("hg7", this), Prefix, 60, Participants.Count, config.minimum_players));
if (time == waitTime - 30) PrintToChat(string.Format(lang.GetMessage("hg7", this), Prefix, 30, Participants.Count, config.minimum_players));
if (time == waitTime - 10) {
PrintToChat(string.Format(lang.GetMessage("hg8", this), Prefix));
MessageParticipants(lang.GetMessage("hg9", this));
AllowJoin = false;
}
//RosterPlayers();
}
);
TimerData.LobbyTimer = timer.Once(waitTime + 1, () => {
if (Participants.Count < config.minimum_players) {
PrintToChat(string.Format(lang.GetMessage("hg10", this), Prefix, Participants.Count, config.minimum_players));
LogToFile("GameRecords", $"[{DateTime.Now}] We do not have enough players to continue ({Participants.Count}/{config.minimum_players}).", this, true);
pcdData.GameStarted = false;
EndHungerGames();
return;
}
Subscribe("OnEntityKill");
Subscribe("OnLootSpawn");
Subscribe("CanPopulateLoot");
AddCrates();
MessageParticipants(lang.GetMessage("hg11", this));
AllowJoin = false;
pcdData.GameStarted = true;
LogToFile("GameRecords", $"[{DateTime.Now}] We have enough players to continue.", this, true);
StartElevators();
Subscribe("CanEntityTakeDamage");
Subscribe("OnPlayerMetabolize");
}
);
}
void KillAFKs() {
List<BasePlayer> killList = Pool.Get<List<BasePlayer>>();
foreach (var player in Participants) {
if (player.transform.position.y < (pcdData.Top_Centre_Point.y * config.slay_y_modifier) && player.IsAlive()) killList.Add(player);
}
foreach (var player in killList) {
if (config.teleport_afk) {
if (player.IsConnected) PrintToChat(player, string.Format(lang.GetMessage("hg41", this, player.UserIDString), Prefix));
Player.Teleport(player, pcdData.afkSpawnLocations.GetRandom());
}
else {
if (player.IsConnected) PrintToChat(player, string.Format(lang.GetMessage("hg12", this, player.UserIDString), Prefix));
player.Die();
}
}
Pool.FreeUnmanaged(ref killList);
}
void RemoveTeam() {
if (!config.remove_teams) return;
foreach (var player in Participants) {
if (player.currentTeam != 0UL) {
RelationshipManager.PlayerTeam current = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
current.RemovePlayer(player.userID);
PrintToChat(player, string.Format(lang.GetMessage("hg13", this, player.UserIDString), Prefix));
//PrintToChat(player, $"{Prefix} You were removed from your team.");
}
}
}
private void StartElevators() {
foreach (BasePlayer player in BasePlayer.activePlayerList) {
if (Participants.Contains(player)) continue;
if (IsPlayerAtHungerGames(player)) Participants.Add(player);
}
start_button.Press();
timer.Once(20f, () => {
foreach (var player in Participants) {
if (player.IsAlive() && player.IsConnected && player.transform.position.y < (pcdData.Top_Centre_Point.y * config.slay_y_modifier)) {
PrintToChat(player, string.Format(lang.GetMessage("hg42", this, player.UserIDString), Prefix, config.teleport_afk ? lang.GetMessage("hg44", this, player.UserIDString) : lang.GetMessage("hg43", this, player.UserIDString), config.Delay_Before_Circle_Start));
}
}
}
);
TimerData.StartTimer = timer.Once(20 + config.Delay_Before_Circle_Start, () => {
InitiateCircleTimers();
MessageParticipants(lang.GetMessage("hg38", this));
timer.Repeat(5f, 5, () => {
KillAFKs();
}
);
RemoveTeam();
}
);
}
#endregion
#region Chat Commands
void ListParticipants(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, "hungergames.list")) return;
if (!isGameRunning()) return;
List<string> names = new List<string>();
if (Participants.Count == 0) return;
foreach (var contestant in Participants) {
names.Add(contestant.displayName);
}
PrintToChat(player, string.Format(lang.GetMessage("hg14", this, player.UserIDString), String.Join("\n- ", names)));
}
void AddEndPoint(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, "hungergames.admin")) return;
pcdData.centre_points.Add(player.transform.position);
SaveData();
PrintToChat(player, string.Format(lang.GetMessage("hg15", this, player.UserIDString), player.transform.position));
}
private void ClaimPrize(BasePlayer player, string command, string[] args) {
if (!pcdData.pentity.TryGetValue(player.userID, out playerData) || playerData.prizes < 1) {
PrintToChat(player, string.Format(lang.GetMessage("hg16", this, player.UserIDString), Prefix));
return;
}
if (IsRunning) {
PrintToChat(player, string.Format(lang.GetMessage("hg17", this, player.UserIDString), Prefix));
return;
}
for (int i = 0; i < config.prize_quantity; i++) {
var prize = config.prizes.GetRandom();
var item = ItemManager.CreateByName(prize.shortName, UnityEngine.Random.Range(prize.min_amount, prize.max_amount + 1), prize.skinID);
if (item == null) continue;
if (prize.displayName != null) item.name = prize.displayName;
PrintToChat(player, string.Format(lang.GetMessage("hg39", this, player.UserIDString), Prefix, item.amount, item.name ?? item.info.displayName.english, player.displayName));
LogToFile("ClaimedPrizes", $"[{DateTime.Now}] Awarded {item.amount}x {item.info.displayName.english}", this, true);
if (!player.inventory.containerMain.IsFull() || player.inventory.containerBelt.IsFull()) {
player.GiveItem(item);
}
else {
item.DropAndTossUpwards(player.transform.position);
PrintToChat(player, string.Format(lang.GetMessage("hg18", this, player.UserIDString), Prefix));
}
}
if (Economics != null && config.give_economics.enabled) {
System.Random random = new System.Random();
var amount = random.NextDouble() * (config.give_economics.min_economics - config.give_economics.max_economics) + config.give_economics.min_economics;
if (Convert.ToBoolean(Economics.Call("Deposit", player.UserIDString, amount))) PrintToChat(player, string.Format(lang.GetMessage("GivenEconomics", this, player.UserIDString), Math.Round(amount, 2)));
}
if (ServerRewards != null && config.give_srp.enabled) {
var amount = UnityEngine.Random.Range(config.give_srp.min_srp, config.give_srp.max_srp);
if (Convert.ToBoolean(ServerRewards.Call("AddPoints", player.userID, amount))) PrintToChat(player, string.Format(lang.GetMessage("GivenSRP", this, player.UserIDString), amount));
}
if (playerData.prizes == 1) pcdData.pentity.Remove(player.userID);
else playerData.prizes--;
SaveData();
}
private void StartHungerGames(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, "hungergames.admin")) return;
if (IsRunning) {
PrintToChat(player, string.Format(lang.GetMessage("hg25", this, player.UserIDString), Prefix));
return;
}
if (config.use_event_helper) {
EventHelper.Call("EMManuallyStarted", this.Name);
}
if (args.Length == 0) InitiateGame();
else {
if (!args[0].IsNumeric()) InitiateGame();
else InitiateGame(Convert.ToInt32(args[0]));
}
}
[ConsoleCommand("starthg")]
void ConsoleStartHG(ConsoleSystem.Arg arg) {
if (!arg.IsRcon) return;
if (config.use_event_helper) {
EventHelper.Call("EMManuallyStarted", this.Name);
}
if (arg.Args.Length == 0) InitiateGame();
else {
if (!arg.Args[0].IsNumeric())
// ... [Code preview truncated due to length. Full source in attached file] ...