C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Facepunch;
using Oxide.Core;
using Rust;
using UnityEngine;
namespace Oxide.Plugins {
[Info("PVP Caps", "WhiteDragon", "1.1.5")]
[Description("Applies a PvP damage handicap to players, based on PvP kills/deaths.")]
internal class PVPCaps : RustPlugin {
#region _fields_
internal static PVPCaps instance;
// configuration: change flag
private static bool config_changed = false;
// configuration: data
private static Configuration config;
// attack info
private static Dictionary<ulong, List<AttackInfo>> attacks = new Dictionary<ulong, List<AttackInfo>>();
// online team member counts
private static Dictionary<ulong, ulong> teams = new Dictionary<ulong, ulong>();
// plugin database
private static DB db;
// oxide permissions
private const string PERMISSIONADMIN = "pvpcaps.admin";
// allowed to use admin commands
private const string PERMISSIONEXCLUDED = "pvpcaps.excluded";
// exclude player from handicaps
private const string PERMISSIONUSE = "pvpcaps.use";
// allows player to see handicaps
// prefab names
private const string MAP_LABEL = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
private const string MAP_ICON = "assets/prefabs/tools/map/genericradiusmarker.prefab";
#endregion
#region _weapons_
protected readonly Dictionary<int, WeaponStats> weapons = new Dictionary<int, WeaponStats> {
[174866732]
= new WeaponStats {
displayname = "16x Zoom Scope", shortname = "weapon.mod.8x.scope", accuracy = 0.15, recoil = -0.2, zoom = 16.0, aimcone = -0.3
}
, [567235583] = new WeaponStats {
displayname = "8x Zoom Scope", shortname = "weapon.mod.small.scope", accuracy = 0.15, recoil = -0.2, zoom = 8.0, aimcone = -0.3
}
, [1545779598] = new WeaponStats {
displayname = "Assault Rifle", shortname = "rifle.ak", damage = 50.0, accuracy = 4.0, recoil = 36.4, attack_rate = 450.0 / 60.0, range = 188.0, attachments = 3.0, aimcone = 0.2, capacity = 30.0, reload = 4.4, draw = 1.0
}
, [1840822026] = new WeaponStats {
displayname = "Beancan Grenade", shortname = "grenade.beancan", explosive_dmg = 15.0, lethality = 115.0, throw_distance = 26.0, fuse_length_min = 3.5, fuse_length_max = 4.0, blast_radius = 4.5, dud_chance = 0.15
}
, [-1262185308] = new WeaponStats {
displayname = "Binoculars", shortname = "tool.binoculars"
}
, [1973165031] = new WeaponStats {
displayname = "Birthday Cake", shortname = "cakefiveyear", damage = 15.0, attack_rate = 46.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [1588298435] = new WeaponStats {
displayname = "Bolt Action Rifle", shortname = "rifle.bolt", damage = 80.0, accuracy = 2.0, recoil = 7.0, attack_rate = 35.0 / 60.0, range = 574.0, attachments = 3.0, aimcone = 0.0, capacity = 4.0, reload = 5.0, draw = 1.0
}
, [1711033574] = new WeaponStats {
displayname = "Bone Club", shortname = "bone.club", damage = 12.0, attack_rate = 86.0 / 60.0, attack_size = 0.2, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [1814288539] = new WeaponStats {
displayname = "Bone Knife", shortname = "knife.bone", damage = 16.0, attack_rate = 86.0 / 60.0, attack_size = 0.2, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-194509282] = new WeaponStats {
displayname = "Butcher Knife", shortname = "knife.butcher", damage = 20.0, attack_rate = 86.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1316706473] = new WeaponStats {
displayname = "Camera", shortname = "tool.camera"
}
, [1789825282] = new WeaponStats {
displayname = "Candy Cane Club", shortname = "candycaneclub", damage = 18.0, attack_rate = 60.0 / 60.0, attack_size = 0.4, range = 1.6, draw = 1.0, melee = true, thrown = true
}
, [1104520648] = new WeaponStats {
displayname = "Chainsaw", shortname = "chainsaw", damage = 12.0, attack_rate = 300.0 / 60.0, attack_size = 0.4, range = 1.5, draw = 1.0, melee = true
}
, [2040726127] = new WeaponStats {
displayname = "Combat Knife", shortname = "knife.combat", damage = 35.0, attack_rate = 86.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [884424049] = new WeaponStats {
displayname = "Compound Bow", shortname = "bow.compound", damage = 100.0, accuracy = 0.0, recoil = 9.0, attack_rate = 60.0 / 60.0, range = 60.0, attachments = 0.0, aimcone = 0.2, capacity = 1.0, reload = 1.0, draw = 1.0
}
, [1965232394] = new WeaponStats {
displayname = "Crossbow", shortname = "crossbow", damage = 60.0, accuracy = 3.0, recoil = 9.0, attack_rate = 17.0 / 60.0, range = 34.0, attachments = 2.0, aimcone = 1.0, capacity = 1.0, reload = 3.6, draw = 1.6
}
, [1796682209] = new WeaponStats {
displayname = "Custom SMG", shortname = "smg.2", damage = 30.0, accuracy = 3.0, recoil = 8.0, attack_rate = 600.0 / 60.0, range = 50.0, attachments = 3.0, aimcone = 0.5, capacity = 24.0, reload = 4.0, draw = 1.0
}
, [-765183617] = new WeaponStats {
displayname = "Double Barrel Shotgun", shortname = "shotgun.double", damage = 180.0, accuracy = 15.0, recoil = 30.0, attack_rate = 120.0 / 60.0, range = 10.0, attachments = 2.0, aimcone = 0.5, capacity = 2.0, reload = 5.5, draw = 1.75
}
, [-75944661] = new WeaponStats {
displayname = "Eoka Pistol", shortname = "pistol.eoka", damage = 180.0, accuracy = 17.0, recoil = 7.0, attack_rate = 30.0 / 60.0, range = 10.0, attachments = 0.0, aimcone = 2.0, capacity = 1.0, reload = 2.0, draw = 0.5
}
, [143803535] = new WeaponStats {
displayname = "F1 Grenade", shortname = "grenade.f1", lethality = 100.0, throw_distance = 26.0, fuse_length_min = 3.0, fuse_length_max = 3.0, blast_radius = 6.0, thrown = true
}
, [-1215753368] = new WeaponStats {
displayname = "Flame Thrower", shortname = "flamethrower", damage = 8.0, attack_rate = 240.0 / 60.0, range = 1.0, attachments = 0.0, capacity = 100.0, reload = 3.5, draw = 1.8
}
, [304481038] = new WeaponStats {
displayname = "Flare", shortname = "flare", throw_distance = 26.0, fuse_length_min = 110.0, fuse_length_max = 120.0, thrown = true
}
, [-196667575] = new WeaponStats {
displayname = "Flashlight", shortname = "flashlight.held", damage = 15.0, attack_rate = 60.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true
}
, [999690781] = new WeaponStats {
displayname = "Geiger Counter", shortname = "geiger.counter"
}
, [1569882109] = new WeaponStats {
displayname = "Handmade Fishing Rod", shortname = "fishingrod.handmade"
}
, [200773292] = new WeaponStats {
displayname = "Hammer", shortname = "hammer", damage = 10.0, attack_rate = 120.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1252059217] = new WeaponStats {
displayname = "Hatchet", shortname = "hatchet", damage = 25.0, attack_rate = 67.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [442289265] = new WeaponStats {
displayname = "Holosight", shortname = "weapon.mod.holosight", accuracy = 0.7, zoom = 2.0, aimcone = -0.7, aimcone_hip = -0.7
}
, [1443579727] = new WeaponStats {
displayname = "Hunting Bow", shortname = "bow.hunting", damage = 50.0, accuracy = 2.0, recoil = 9.0, attack_rate = 60.0 / 60.0, range = 15.0, attachments = 0.0, aimcone = 1.0, capacity = 1.0, reload = 1.0, draw = 1.0
}
, [1488979457] = new WeaponStats {
displayname = "Jackhammer", shortname = "jackhammer", damage = 15.0, attack_rate = 400.0, attack_size = 0.3, range = 2.0, draw = 1.0, melee = true
}
, [-778367295] = new WeaponStats {
displayname = "L96 Rifle", shortname = "rifle.l96", damage = 80.0, accuracy = 2.0, recoil = 3.5, attack_rate = 23.0 / 60.0, range = 1125.0, attachments = 3.0, aimcone = 0.0, capacity = 5.0, reload = 3.0, draw = 1.0
}
, [-1469578201] = new WeaponStats {
displayname = "Longsword", shortname = "longsword", damage = 75.0, attack_rate = 30.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1469578201] = new WeaponStats {
displayname = "LR-300 Assault Rifle", shortname = "longsword", damage = 40.0, accuracy = 4.0, recoil = 15.9, attack_rate = 500.0 / 60.0, range = 188.0, attachments = 3.0, aimcone = 0.2, capacity = 30.0, reload = 4.0, draw = 2.0
}
, [-2069578888] = new WeaponStats {
displayname = "M249", shortname = "lmg.m249", damage = 65.0, accuracy = 7.0, recoil = 7.0, attack_rate = 500.0 / 60.0, range = 317.0, attachments = 3.0, aimcone = 0.2, capacity = 100.0, reload = 7.5, draw = 1.8
}
, [28201841] = new WeaponStats {
displayname = "M39 Rifle", shortname = "rifle.m39", damage = 50.0, accuracy = 4.0, recoil = 8.5, attack_rate = 300.0 / 60.0, range = 352.0, attachments = 3.0, aimcone = 0.1, capacity = 20.0, reload = 3.25, draw = 1.0
}
, [-852563019] = new WeaponStats {
displayname = "M92 Pistol", shortname = "pistol.m92", damage = 45.0, accuracy = 4.0, recoil = 9.0, attack_rate = 600.0 / 60.0, range = 90.0, attachments = 3.0, aimcone = 1.0, capacity = 15.0, reload = 2.2, draw = 0.5
}
, [-1966748496] = new WeaponStats {
displayname = "Mace", shortname = "mace", damage = 50.0, attack_rate = 30.0 / 60.0, attack_size = 0.4, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1137865085] = new WeaponStats {
displayname = "Machete", shortname = "machete", damage = 35.0, attack_rate = 60.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [1318558775] = new WeaponStats {
displayname = "MP5A4", shortname = "smg.mp5", damage = 35.0, accuracy = 5.0, recoil = 14.1, attack_rate = 600.0 / 60.0, range = 72.0, attachments = 3.0, aimcone = 0.5, capacity = 30.0, reload = 4.0, draw = 1.6
}
, [-1123473824] = new WeaponStats {
displayname = "Multiple Grenade Launcher", shortname = "multiplegrenadelauncher", damage = 125.0, accuracy = 4.0, recoil = 30.0, attack_rate = 150.0 / 60.0, range = 1.0, attachments = 2.0, aimcone = 2.25, capacity = 6.0, reload = 6.0, draw = 1.0
}
, [-1405508498] = new WeaponStats {
displayname = "Muzzle Boost", shortname = "weapon.mod.muzzleboost", attack_rate = 0.1, velocity = -0.1, damage = -0.1
}
, [1478091698] = new WeaponStats {
displayname = "Muzzle Brake", shortname = "weapon.mod.muzzlebrake", velocity = -0.2, damage = -0.2, accuracy = -0.38, recoil = -0.5, aimcone = 0.5, aimcone_hip = 2.0, aimcone_degrees = true
}
, [1953903201] = new WeaponStats {
displayname = "Nailgun", shortname = "pistol.nailgun", damage = 18.0, accuracy = 4.0, recoil = 7.0, attack_rate = 400.0 / 60.0, range = 6.0, attachments = 0.0, aimcone = 0.75, capacity = 16.0, reload = 3.4, draw = 0.5
}
, [-1302129395] = new WeaponStats {
displayname = "Pickaxe", shortname = "pickaxe", damage = 30.0, attack_rate = 40.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [1090916276] = new WeaponStats {
displayname = "Pitchfork", shortname = "pitchfork", damage = 40.0, attack_rate = 40.0 / 60.0, attack_size = 0.4, range = 2.8, draw = 1.0, melee = true, thrown = true
}
, [795371088] = new WeaponStats {
displayname = "Pump Shotgun", shortname = "shotgun.pump", damage = 210.0, accuracy = 14.0, recoil = 22.0, attack_rate = 55.0 / 60.0, range = 45.0, attachments = 2.0, aimcone = 0.0, capacity = 6.0, reload = 5.5, draw = 1.0
}
, [1373971859] = new WeaponStats {
displayname = "Python Revolver", shortname = "pistol.python", damage = 55.0, accuracy = 6.0, recoil = 18.0, attack_rate = 400.0 / 60.0, range = 72.0, attachments = 3.0, aimcone = 0.5, capacity = 6.0, reload = 3.75, draw = 0.5
}
, [649912614] = new WeaponStats {
displayname = "Revolver", shortname = "pistol.revolver", damage = 35.0, accuracy = 5.0, recoil = 7.0, attack_rate = 343.0 / 60.0, range = 36.0, attachments = 1.0, aimcone = 0.75, capacity = 8.0, reload = 3.4, draw = 0.5
}
, [596469572] = new WeaponStats {
displayname = "RF Transmitter", shortname = "rf.detonator"
}
, [963906841] = new WeaponStats {
displayname = "Rock", shortname = "rock", damage = 10.0, attack_rate = 46.0 / 60.0, attack_size = 0.2, range = 1.3, draw = 1.0, melee = true, thrown = true
}
, [442886268] = new WeaponStats {
displayname = "Rocket Launcher", shortname = "rocket.launcher", damage = 350.0, accuracy = 4.0, recoil = 30.0, attack_rate = 10.0 / 60.0, range = 1.0, attachments = 0.0, aimcone = 2.25, capacity = 1.0, reload = 6.0, draw = 1.0
}
, [-262590403] = new WeaponStats {
displayname = "Salvaged Axe", shortname = "axe.salvaged", damage = 40.0, attack_rate = 48.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1978999529] = new WeaponStats {
displayname = "Salvaged Cleaver", shortname = "salvaged.cleaver", damage = 60.0, attack_rate = 30.0 / 60.0, attack_size = 0.4, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1506397857] = new WeaponStats {
displayname = "Salvaged Hammer", shortname = "hammer.salvaged", damage = 30.0, attack_rate = 60.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1780802565] = new WeaponStats {
displayname = "Salvaged Icepick", shortname = "icepick.salvaged", damage = 40.0, attack_rate = 48.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [1326180354] = new WeaponStats {
displayname = "Salvaged Sword", shortname = "salvaged.sword", damage = 50.0, attack_rate = 48.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [-1878475007] = new WeaponStats {
displayname = "Satchel Charge", shortname = "explosive.satchel", explosive_dmg = 75.0, lethality = 475.0, throw_distance = 20.0, fuse_length_min = 6.0, fuse_length_max = 12.0, blast_radius = 4.0, dud_chance = 0.2
}
, [818877484] = new WeaponStats {
displayname = "Semi-Automatic Pistol", shortname = "pistol.semiauto", damage = 40.0, accuracy = 4.0, recoil = 10.0, attack_rate = 400.0 / 60.0, range = 45.0, attachments = 3.0, aimcone = 0.75, capacity = 10.0, reload = 2.9, draw = 0.5
}
, [-904863145] = new WeaponStats {
displayname = "Semi-Automatic Rifle", shortname = "rifle.semiauto", damage = 40.0, accuracy = 4.0, recoil = 7.0, attack_rate = 343.0 / 60.0, range = 188.0, attachments = 3.0, aimcone = 0.25, capacity = 16.0, reload = 4.4, draw = 1.6
}
, [-1850571427] = new WeaponStats {
displayname = "Silencer", shortname = "weapon.mod.silencer", velocity = -0.25, damage = -0.25, accuracy = 0.33, recoil = -0.2, aimcone = -0.3, aimcone_hip = -0.3, aim_sway = -0.2
}
, [-855748505] = new WeaponStats {
displayname = "Simple Handmade Sight", shortname = "weapon.mod.simplesight", zoom = -0.5
}
, [1263920163] = new WeaponStats {
displayname = "Smoke Grenade", shortname = "grenade.smoke", throw_distance = 20.0, fuse_length_min = 1.0, fuse_length_max = 1.0
}
, [-363689972] = new WeaponStats {
displayname = "Snowball", shortname = "snowball", damage = 30.0, attack_rate = 46.0 / 60.0, attack_size = 0.2, range = 1.3, draw = 1.0, melee = true, thrown = true
}
, [-41440462] = new WeaponStats {
displayname = "Spas-12 Shotgun", shortname = "shotgun.spas12", damage = 137.0, accuracy = 14.0, recoil = 22.0, attack_rate = 180.0 / 60.0, range = 45.0, attachments = 3.0, aimcone = 0.0, capacity = 6.0, reload = 5.8, draw = 1.6
}
, [-1583967946] = new WeaponStats {
displayname = "Stone Hatchet", shortname = "stonehatchet", damage = 15.0, attack_rate = 67.0 / 60.0, attack_size = 0.3, range = 1.5, draw = 1.0, melee = true, thrown = true
}
, [171931394] = new WeaponStats {
displayname = "Stone Pickaxe", shortname = "stone.pickaxe", damage = 17.0, attack_rate = 67.0 / 60.0, attack_size = 0.3, range = 2.8, draw = 1.0, melee = true, thrown = true
}
, [1602646136] = new WeaponStats {
displayname = "Stone Spear", shortname = "spear.stone", damage = 35.0, attack_rate = 40.0 / 60.0, attack_size = 0.2, range = 2.8, draw = 1.0, melee = true, thrown = true
}
, [1397052267] = new WeaponStats {
displayname = "Supply Signal", shortname = "supply.signal", throw_distance = 20.0, fuse_length_min = 4.0, fuse_length_max = 4.0
}
, [1975934948] = new WeaponStats {
displayname = "Survey Charge", shortname = "surveycharge", explosive_dmg = 8.0, lethality = 20.0, throw_distance = 8.0, fuse_length_min = 5.0, fuse_length_max = 5.0, blast_radius = 5.0
}
, [1248356124] = new WeaponStats {
displayname = "Timed Explosive Charge", shortname = "explosive.timed", explosive_dmg = 550.0, lethality = 550.0, throw_distance = 20.0, fuse_length_min = 10.0, fuse_length_max = 10.0, blast_radius = 4.0
}
, [795236088] = new WeaponStats {
displayname = "Torch", shortname = "torch", damage = 5.0, attack_rate = 46.0 / 60.0, attack_size = 0.2, range = 1.5, draw = 1.0, melee = true
}
, [-1758372725] = new WeaponStats {
displayname = "Thompson", shortname = "smg.thompson", damage = 38.0, accuracy = 3.0, recoil = 8.0, attack_rate = 462.0 / 60.0, range = 90.0, attachments = 3.0, aimcone = 0.5, capacity = 20.0, reload = 4.0, draw = 1.0
}
, [1424075905] = new WeaponStats {
displayname = "Water Bucket", shortname = "bucket.water"
}
, [-1367281941] = new WeaponStats {
displayname = "Waterpipe Shotgun", shortname = "shotgun.waterpipe", damage = 180.0, accuracy = 15.0, recoil = 22.0, attack_rate = 13.0 / 60.0, range = 10.0, attachments = 2.0, aimcone = 1.0, capacity = 1.0, reload = 4.5, draw = 2.4
}
, [952603248] = new WeaponStats {
displayname = "Weapon Flashlight", shortname = "weapon.mod.flashlight"
}
, [-132516482] = new WeaponStats {
displayname = "Weapon Laserlight", shortname = "weapon.mod.lasersight", accuracy = 0.44, aimcone = -0.2, aimcone_hip = -0.4, aim_sway = -0.9
}
, [1540934679] = new WeaponStats {
displayname = "Wooden Spear", shortname = "spear.wooden", damage = 25.0, attack_rate = 40.0 / 60.0, attack_size = 0.2, range = 2.6, draw = 1.0, melee = true, thrown = true
}
}
;
#endregion
#region _local_classes_
internal class AddressInfo {
public List<ulong> userid;
public bool whitelist;
}
internal class AttackInfo {
public DateTime time;
public ulong userid;
public double penalty;
}
internal class DB {
public Dictionary<ulong, Handicap> handicaps;
public Dictionary<string, AddressInfo> ip_table;
public DB() {
handicaps = new Dictionary<ulong, Handicap>();
ip_table = new Dictionary<string, AddressInfo>();
}
}
internal class Fire : MonoBehaviour {
public BaseEntity Initiator;
}
internal class Handicap {
public string display_name;
public double damage_amount;
public DateTime decay_timer;
public DateTime online_time;
public ulong warning_count;
public DateTime warning_time;
public ulong cripple_count;
public DateTime cripple_timer;
public bool crippled;
public ulong ban_count;
public DateTime ban_timer;
public bool banned;
public void processDeath(double penalty) {
if(damage_amount < config.handicap_damage_max) {
if((damage_amount *= (1.0 + penalty)) > config.handicap_damage_max) {
damage_amount = config.handicap_damage_max;
}
decay_timer = DateTime.UtcNow;
}
}
public void processKill(double penalty) {
if(damage_amount > config.handicap_damage_min) {
if((damage_amount /= (1.0 + penalty)) < config.handicap_damage_min) {
damage_amount = config.handicap_damage_min;
}
decay_timer = DateTime.UtcNow;
}
}
}
internal class WeaponStats {
public double accuracy;
public double aim_sway;
public double aimcone;
public bool aimcone_degrees;
public double aimcone_hip;
public double attachments;
public double attack_rate;
public double attack_size;
public double blast_radius;
public double capacity;
public double damage;
public string displayname;
public double draw;
public double dud_chance;
public double explosive_dmg;
public double fuse_length_min;
public double fuse_length_max;
public double lethality;
public bool melee;
public double range;
public double recoil;
public double reload;
public string shortname;
public double throw_distance;
public bool thrown;
public double velocity;
public double zoom;
}
#endregion
#region _configuration_
internal class Configuration {
// configuration: version
public ulong config_version;
// configuration: general settings
public bool general_pvp_enabled;
// enables pvp damage on the server
public bool general_pvp_ff_enabled;
// enables pvp friendly fire damage on the server
public bool general_pvp_team_scale;
// scale damage by online team sizes
// configuration: handicap adjustments
public double handicap_assist_scale;
// per-assist penalty scale (0.0, 1.0]
public ulong handicap_ban_count;
// auto-ban ban count, (0 disabled)
public ulong handicap_ban_time;
// auto-ban time, in hours
public double handicap_build_authed;
// build authed penalty scale [0.0, 1.0]
public ulong handicap_cripple_count;
// auto-ban cripple count (0 disabled)
public double handicap_cripple_limit;
// auto-cripple threshold [0.0, 1.0]
public ulong handicap_cripple_time;
// auto-cripple time, in minutes
public double handicap_damage_max;
// maximum damage multiplier [0.0, 1.0]
public double handicap_damage_min;
// minimum damage multiplier (0.0, 1.0]
public double handicap_decay_amount;
// handicap decay amount (0.0, 1.0]
public ulong handicap_decay_time;
// handicap decay time, in seconds
public double handicap_killer_scale;
// per-kill penalty scale (0.0, 1.0]
public double handicap_penalty_head;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_chest;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_arm;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_stomach;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_leg;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_hand;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_foot;
// per-hit penalty [0.0, 1.0]
public double handicap_penalty_generic;
// per-hit penalty [0.0, 1.0]
public double handicap_movement_rate;
// normal movement rate in meters/second
public double handicap_range_normal;
// normal weapon range (0.0, 1.0]
public ulong handicap_warning_count;
// auto-cripple warning count (0 disabled)
public double handicap_warning_limit;
// admin warning threshold [0.0, 1.0]
// configuration: diagnostic outputs
public bool output_console;
// output event details to the server console
public bool output_logfile;
// output event details to the server logfile
public static Configuration Defaults() {
return new Configuration {
config_version = 10101, general_pvp_enabled = true, general_pvp_ff_enabled = true, general_pvp_team_scale = true, handicap_assist_scale = 0.5, handicap_ban_count = 3, handicap_ban_time = 24, handicap_build_authed = 0.5, handicap_cripple_count = 3, handicap_cripple_limit = 0.0625, handicap_cripple_time = 60, handicap_damage_max = 1.0, handicap_damage_min = 0.0001, handicap_decay_amount = 0.5, handicap_decay_time = 180, handicap_killer_scale = 0.75, handicap_penalty_head = 1.00, handicap_penalty_chest = 0.75, handicap_penalty_arm = 0.5, handicap_penalty_stomach = 0.5, handicap_penalty_leg = 0.5, handicap_penalty_hand = 0.25, handicap_penalty_foot = 0.25, handicap_penalty_generic = 0.5, handicap_movement_rate = 2.5, handicap_range_normal = 0.25, handicap_warning_count = 3, handicap_warning_limit = 0.125, output_console = true, output_logfile = true
}
;
}
}
private void CheckRange(ref double value, double min, double max) {
if(value < min) {
value = min;
config_changed = true;
}
else if(value > max) {
value = max;
config_changed = true;
}
}
private void CheckRange(ref ulong value, ulong min, ulong max) {
if(value < min) {
value = min;
config_changed = true;
}
else if(value > max) {
value = max;
config_changed = true;
}
}
protected override void LoadDefaultConfig() {
}
private void LoadConfiguration() {
try {
if(Config["Config", "Version"] is string) {
throw new Exception();
}
config = Config.ReadObject<Configuration>();
}
catch(Exception) {
Puts(lang.GetMessage(c_error_bad_config, this));
config = Configuration.Defaults();
config_changed = true;
}
CheckRange(ref config.handicap_assist_scale, double.Epsilon, 1.0);
CheckRange(ref config.handicap_build_authed, 0.0, 1.0);
CheckRange(ref config.handicap_ban_count, 0, 100);
CheckRange(ref config.handicap_ban_time, 0, 720);
CheckRange(ref config.handicap_cripple_count, 0, 100);
CheckRange(ref config.handicap_cripple_limit, 0.0, 1.0);
CheckRange(ref config.handicap_cripple_time, 0, 10080);
CheckRange(ref config.handicap_damage_max, double.Epsilon, 1.0);
CheckRange(ref config.handicap_damage_min, double.Epsilon, 1.0);
CheckRange(ref config.handicap_decay_amount, double.Epsilon, 1.0);
CheckRange(ref config.handicap_decay_time, 15, 3600);
CheckRange(ref config.handicap_killer_scale, double.Epsilon, 1.0);
CheckRange(ref config.handicap_penalty_head, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_chest, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_arm, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_stomach, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_leg, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_hand, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_foot, 0.0, 1.0);
CheckRange(ref config.handicap_penalty_generic, 0.0, 1.0);
CheckRange(ref config.handicap_movement_rate, 1.5, 4.5);
CheckRange(ref config.handicap_range_normal, double.Epsilon, 1.0);
CheckRange(ref config.handicap_warning_count, 0, 100);
CheckRange(ref config.handicap_warning_limit, 0.0, 1.0);
SaveConfiguration();
}
private void SaveConfiguration() {
if(config_changed) {
Config.WriteObject(config);
config_changed = false;
}
}
#endregion
#region _localization_
// localization string identifiers
private const string c_assist = "CAssist";
private const string c_auto_ban_cripple_count = "CAutoBanCrippleCount";
private const string c_auto_ban_inherited = "CAutoBanInherited";
private const string c_ban_no_reason = "CBanNoReason";
private const string c_error_bad_config = "CErrorBadConfig";
private const string c_killer = "CKiller";
private const string c_pvp_admin_banned_perm = "CPvpAdminBannedPerm";
private const string c_pvp_admin_banned_time = "CPvpAdminBannedTime";
private const string c_pvp_admin_crippled_perm = "CPvpAdminCrippledPerm";
private const string c_pvp_admin_crippled_time = "CPvpAdminCrippledTime";
private const string c_status = "CStatus";
private const string c_victim = "CVictim";
private const string m_error_admin_ipshare = "MErrorAdminIPShare";
private const string m_error_admin_syntax = "MErrorAdminSyntax";
private const string m_error_no_permission = "MErrorNoPermission";
private const string m_handicap_admin = "MHandicapAdmin";
private const string m_prefix = "MPrefix";
private const string m_pvp_admin_ambiguous = "MPvpAdminAmbiguous";
private const string m_pvp_admin_ban = "MPvpAdminBan";
private const string m_pvp_admin_cripple = "MPvpAdminCripple";
private const string m_pvp_admin_disabled = "MPvpAdminDisabled";
private const string m_pvp_admin_disabled_reason = "MPvpAdminDisabledReason";
private const string m_pvp_admin_enabled = "MPvpAdminEnabled";
private const string m_pvp_admin_ff_disabled = "MPvpAdminFfDisabled";
private const string m_pvp_admin_ff_enabled = "MPvpAdminFfEnabled";
private const string m_pvp_admin_ipshare_list = "MPvpAdminIPShareList";
private const string m_pvp_admin_ipshare_off = "MPvpAdminIPShareOff";
private const string m_pvp_admin_ipshare_on = "MPvpAdminIPShareOn";
private const string m_pvp_admin_modifier = "MPvpAdminModifier";
private const string m_pvp_admin_not_crippled = "MPvpAdminNotCrippled";
private const string m_pvp_admin_not_handicapped = "MPvpAdminNotHandicapped";
private const string m_pvp_admin_not_found = "MPvpAdminNotFound";
private const string m_pvp_admin_reset = "MPvpAdminReset";
private const string m_pvp_admin_reset_all = "MPvpAdminResetAll";
private const string m_pvp_admin_team_disabled = "MPvpAdminTeamDisabled";
private const string m_pvp_admin_team_enabled = "MPvpAdminTeamEnabled";
private const string m_pvp_admin_team_list = "MPvpAdminTeamList";
private const string m_pvp_admin_unban = "MPvpAdminUnban";
private const string m_pvp_admin_unban_all = "MPvpAdminUnbanAll";
private const string m_pvp_admin_uncripple = "MPvpAdminUncripple";
private const string m_pvp_admin_uncripple_all = "MPvpAdminUncrippleAll";
private const string m_pvp_admin_warning = "MPvpAdminWarning";
private const string m_pvp_damage_decrease = "MPvpDamageDecrease";
private const string m_pvp_damage_increase = "MPvpDamageIncrease";
private const string m_pvp_damage_modifier = "MPvpDamageModifier";
private const string m_pvp_ff_is_disabled = "MPvpFfIsDisabled";
private const string m_pvp_ff_is_enabled = "MPvpFfIsEnabled";
private const string m_pvp_is_disabled = "MPvpIsDisabled";
private const string m_pvp_is_enabled = "MPvpIsEnabled";
private const string m_pvp_team_is_disabled = "MPvpTeamIsDisabled";
private const string m_pvp_team_is_enabled = "MPvpTeamIsEnabled";
private const string m_suffix = "MSuffix";
// localization strings
private readonly Dictionary<string, string> messages = new Dictionary<string, string> {
[c_assist]
= "assist", [c_auto_ban_cripple_count] = "Too many violations (cripple limit reached)", [c_auto_ban_inherited] = "Alternate account (ban inherited)", [c_ban_no_reason] = "No reason given", [c_error_bad_config] = "Configuration file bad or missing.Loading default configuration.", [c_killer] = "killer", [c_pvp_admin_banned_perm] = " (banned permanently)", [c_pvp_admin_banned_time] = " (banned for {0}h)", [c_pvp_admin_crippled_perm] = " (crippled permanently)", [c_pvp_admin_crippled_time] = " (crippled for {0}m)", [c_status] = "status", [c_victim] = "victim", [m_error_admin_ipshare] = "<color=#ff7f7f>IP address not found for {0}.</color>", [m_error_admin_syntax] = "<color=#ffff7f>Usage:\n /pvp * [ff [off | on] | list | off [message] | on | reset | team [off | on] | unban | uncripple]\n /pvp (steamid | displayname) [ban [hours] [reason] | cripple [minutes] | reset | unban | uncripple]\n /pvp (steamid | displayname) ipshare [ban [hours] [reason] | list | off | on | unban]\n /pvp (steamid | displayname) team [ban [hours] [reason] | cripple [minutes] | reset | unban | uncripple]</color>", [m_error_no_permission] = "<color=#ff0000>You do not have permission to use this command.</color>", [m_handicap_admin] = "{0}", [m_prefix] = "<size=12><color=#ff1f1f>[</color><color=#ff3f1f>P</color><color=#ff5f1f>v</color><color=#ff7f1f>P</color><color=#ff9f1f>C</color><color=#ff7f1f>a</color><color=#ff5f1f>p</color><color=#ff3f1f>s</color><color=#ff1f1f>]</color> ", [m_pvp_admin_ambiguous] = "<color=#ff7f7f>Multiple players found:{0}</color>", [m_pvp_admin_ban] = "<color=#ff7f7f>{0} has BANNED {1}.</color>", [m_pvp_admin_cripple] = "<color=#ff7f7f>{0} has CRIPPLED {1}.</color>", [m_pvp_admin_disabled] = "<color=#ff7f7f>{0} has DISABLED PvP damage on the server.</color>", [m_pvp_admin_disabled_reason] = "<color=#ff7f7f>{0} has DISABLED PvP damage on the server.(Reason: {1})</color>", [m_pvp_admin_enabled] = "<color=#7fff00>{0} has ENABLED PvP damage on the server.</color>", [m_pvp_admin_ff_disabled] = "<color=#ff7f7f>{0} has DISABLED friendly fire damage on the server.</color>", [m_pvp_admin_ff_enabled] = "<color=#7fff00>{0} has ENABLED friendly fire damage on the server.</color>", [m_pvp_admin_ipshare_list] = "<color=#ffff00>Connection history for {0}:{1}</color>", [m_pvp_admin_ipshare_off] = "<color=#ffff00>IP sharing is DISABLED for {0}.</color>", [m_pvp_admin_ipshare_on] = "<color=#ffff00>IP sharing is ENABLED for {0}.</color>", [m_pvp_admin_modifier] = "<color=#ffff00>{0}'s PvP damage modifier is {1}%.</color><color=#ff7f7f>{2}</color>", [m_pvp_admin_not_crippled] = "<color=#ffff00>No crippled players found.</color>", [m_pvp_admin_not_handicapped] = "<color=#ffff00>No handicapped players found.</color>", [m_pvp_admin_not_found] = "<color=#ff7f7f>Player not found: {0}</color>", [m_pvp_admin_reset] = "<color=#ffff00>{0} has RESET the handicap for {1}.</color>", [m_pvp_admin_reset_all] = "<color=#ffff00>{0} has RESET the handicaps for all players.</color>", [m_pvp_admin_team_disabled] = "<color=#ff7f7f>{0} has DISABLED team size scaling on the server.</color>", [m_pvp_admin_team_enabled] = "<color=#7fff00>{0} has ENABLED team size scaling on the server.</color>", [m_pvp_admin_team_list] = "<color=#ffff00>{0}'s team members:</color>", [m_pvp_admin_unban] = "<color=#7fff00>{0} has UNBANNED {1}.</color>", [m_pvp_admin_unban_all] = "<color=#7fff00>{0} has UNBANNED all players.</color>", [m_pvp_admin_uncripple] = "<color=#7fff00>{0} has UNCRIPPLED {1}.</color>", [m_pvp_admin_uncripple_all] = "<color=#7fff00>{0} has UNCRIPPLED all players.</color>", [m_pvp_admin_warning] = "<color=#ffff00>WARNING: {0} received a penalty of -{1}%.</color>", [m_pvp_damage_decrease] = "<color=#ff7f00>Your PvP damage modifier decreased to {0}%.</color>", [m_pvp_damage_increase] = "<color=#7fff00>Your PvP damage modifier increased to {0}%.</color>", [m_pvp_damage_modifier] = "<color=#ffff00>Your PvP damage modifier is {0}%.</color>", [m_pvp_ff_is_disabled] = "<color=#ff7f7f>Friendly fire is disabled.</color>", [m_pvp_ff_is_enabled] = "<color=#7fff00>Friendly fire is enabled.</color>", [m_pvp_is_disabled] = "<color=#ff7f7f>PvP is disabled.</color>", [m_pvp_is_enabled] = "<color=#7fff00>PvP is enabled.</color>", [m_pvp_team_is_disabled] = "<color=#ff7f7f>Team size scaling is disabled.</color>", [m_pvp_team_is_enabled] = "<color=#7fff00>Team size scaling is enabled.</color>", [m_suffix] = "</size>"
}
;
internal static void ChatMessage(BasePlayer player, string key, params object[] args) {
if(player == null) {
instance.Puts(string.Format(ToPlaintext(instance.lang.GetMessage(key, instance)), args));
}
else if(player.IsConnected) {
string prefix = GetMessage(m_prefix, player.UserIDString);
string middle = GetMessage(key, player.UserIDString, args);
string suffix = GetMessage(m_suffix, player.UserIDString);
StringBuilder message = new StringBuilder(prefix.Length + middle.Length + suffix.Length);
message.Append(prefix).Append(middle).Append(suffix);
player.ChatMessage(message.ToString());
}
}
internal static void ChatMessageAdmin(string key, params object[] args) {
foreach(var player in BasePlayer.activePlayerList) {
if(instance.permission.UserHasPermission(player.UserIDString, PERMISSIONADMIN)) {
ChatMessage(player, key, args);
}
}
}
internal static void ChatMessageBroadcast(string key, params object[] args) {
foreach(var player in BasePlayer.activePlayerList) {
ChatMessage(player, key, args);
}
}
internal static string CrippleStatus(ulong userid, string admin_id = null) {
if(db.handicaps[userid].banned) {
if(db.handicaps[userid].ban_timer == DateTime.MinValue) {
return GetMessage(c_pvp_admin_banned_perm, admin_id);
}
else {
double hours = db.handicaps[userid].ban_timer.Subtract(DateTime.UtcNow).TotalMinutes / 60.0;
return GetMessage(c_pvp_admin_banned_time, admin_id, hours.ToString("0.0"));
}
}
else if(db.handicaps[userid].crippled) {
if(db.handicaps[userid].cripple_timer == DateTime.MinValue) {
return GetMessage(c_pvp_admin_crippled_perm, admin_id);
}
else {
double minutes = db.handicaps[userid].cripple_timer.Subtract(DateTime.UtcNow).TotalMinutes;
return GetMessage(c_pvp_admin_crippled_time, admin_id, minutes.ToString("0.0"));
}
}
return string.Empty;
}
internal static string DamagePercent(ulong userid) {
return (db.handicaps[userid].damage_amount * 100.0).ToString("0.00");
}
internal static string HandicapInfoAdmin(ulong userid, string admin_id = null) {
if(admin_id == null) {
string format = ToPlaintext(instance.lang.GetMessage(m_pvp_admin_modifier, instance));
return string.Format(format, NameAndID(userid), DamagePercent(userid), CrippleStatus(userid));
}
else {
return GetMessage(m_pvp_admin_modifier, admin_id, NameAndID(userid), DamagePercent(userid), CrippleStatus(userid, admin_id));
}
}
internal static string HandicapInfoConsole(string key, ulong userid) {
string label = instance.lang.GetMessage(key, instance);
return string.Format("{0}[{1}: {2}%{3}]", label, NameAndID(userid), DamagePercent(userid), CrippleStatus(userid));
}
protected static string GetMessage(string key, string userid, params object[] args) => string.Format(instance.lang.GetMessage(key, instance, userid), args);
protected static string GetMessagePlain(string key) => ToPlaintext(instance.lang.GetMessage(key, instance));
protected override void LoadDefaultMessages() => lang.RegisterMessages(messages, this, "en");
internal static void LogMessage(string key, params object[] args) {
if(config.output_console || config.output_logfile) {
string message = string.Format(GetMessagePlain(key), args);
if(config.output_console) {
instance.Puts(message);
}
if(config.output_logfile) {
string time = DateTime.Now.ToString("T", DateTimeFormatInfo.InvariantInfo);
instance.LogToFile("log", string.Format("{0}: {1}", time, message), instance);
}
}
}
internal static string NameAndID(ulong userid) {
return string.Format("{0}({1})", db.handicaps[userid].display_name, userid.ToString());
}
internal static string ToPlaintext(string message) {
string result = string.Empty;
if(message != null) {
StringBuilder sb_result = new StringBuilder(Regex.Replace(message, "<[^>]+>", ""));
sb_result.Replace("<", "<");
sb_result.Replace(">", ">");
sb_result.Replace("&", "&");
result = sb_result.ToString();
}
return result;
}
#endregion
#region _commands_
[ChatCommand("pvp")]
private void ChatCmdPvP(BasePlayer player, string cmd, string[] args) {
bool console = (player == null);
string admin_id = console ? null : player.UserIDString;
string admin_name = console ? "CONSOLE" : player.displayName;
if((args == null) || args.Length == 0) {
if(console) {
bool entry_found = false;
foreach(var entry in db.handicaps) {
if(entry.Value.crippled || entry.Value.damage_amount != config.handicap_damage_max) {
ChatMessage(player, m_handicap_admin, HandicapInfoAdmin(entry.Key, admin_id));
entry_found = true;
}
}
if(!entry_found) {
ChatMessage(player, m_pvp_admin_not_handicapped);
}
}
else {
if(permission.UserHasPermission(player.UserIDString, PERMISSIONUSE)) {
ChatMessage(player, m_pvp_damage_modifier, DamagePercent(player.userID));
}
else {
ChatMessage(player, m_error_no_permission);
}
}
return;
}
if(!console && !permission.UserHasPermission(player.UserIDString, PERMISSIONADMIN)) {
ChatMessage(player, m_error_no_permission);
return;
}
if(args[0] == "*") {
if(args.Length == 1) {
ChatMessage(player, config.general_pvp_enabled ? m_pvp_is_enabled : m_pvp_is_disabled);
return;
}
if(args.Length > 3) {
goto syntax_error;
}
switch(args[1].ToLower()) {
case "ff" : if(args.Length == 2) {
ChatMessage(player, config.general_pvp_ff_enabled ? m_pvp_ff_is_enabled : m_pvp_ff_is_disabled);
return;
}
switch(args[2].ToLower()) {
case "off" : config.general_pvp_ff_enabled = false;
config_changed = true;
ChatMessageBroadcast(m_pvp_admin_ff_disabled, admin_name);
if(!config.output_console && console) {
ChatMessage(player, m_pvp_admin_ff_disabled, admin_name);
// ... [Code preview truncated due to length. Full source in attached file] ...