Иконка ресурса

FREE Orphans2PVP : 0.2.2 0.2.2

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;

/*
TODO:
Some visual way to know a base is PVP
*/

namespace Oxide.Plugins
{
    [Info("Orphans2PVP", "obsol", "0.2.2")]
    [Description("Ties into TruePVE to turn abandoned buildings into raidable, pvp zones after a delay.Supports Clans as well")]
    class Orphans2PVP : RustPlugin
    {
		long minuteMultiplier = 60*1000;
		long day = 86400;
		string[] sanityPhases = {"existingBuildings","cleanClans","stateSwitching"};
		int phaseIdx = -1;
		
		bool doPhase(){
			phaseIdx = (phaseIdx+1)%sanityPhases.Length;
			return doPhase(sanityPhases[phaseIdx]);
		}
		bool doPhase(string s){
			switch(s){
				case "existingBuildings":
					Puts("Cleaning out destroyed buildings");
				
					return existingBuildings();
					break;
				case "cleanClans":
					Puts("Updating clan states");
				
					return cleanClans();
					break;
				case "stateSwitching":
					Puts("Advising of newly protected and PVP buildings");
				
					return stateSwitching();
					break;
				default:
					return false;
					break;
			}
		}
		
		bool existingBuildings(){
			foreach (string s in config.protectionTimeouts.Keys.ToArray()){
				uint id = 0;
				UInt32.TryParse ( s.Split(':')?[1], out id);
				if(id!=0&&BuildingManager.server.GetBuilding(id)==null){
					config.protectionTimeouts.Remove(s);
				}
			}
			foreach (string s in config.protectionCooldowns.Keys.ToArray()){
				uint id = 0;
				UInt32.TryParse ( s.Split(':')?[1], out id);
				if(id!=0&&BuildingManager.server.GetBuilding(id)==null){
					config.protectionCooldowns.Remove(s);
				}
				
			}
			foreach (string s in config.protectionState.Keys.ToArray()){
				uint id = 0;
				UInt32.TryParse ( s.Split(':')?[1], out id);
				if(id!=0&&BuildingManager.server.GetBuilding(id)==null){
					config.protectionState.Remove(s);
				}
				
			}
			foreach (string s in config.buildingRefreshers.Keys.ToArray()){
				uint id = 0;
				UInt32.TryParse ( s.Split(':')?[1], out id);
				if(id!=0&&BuildingManager.server.GetBuilding(id)==null){
					config.buildingRefreshers.Remove(s);
				}
				
			}
			foreach (string s in config.refresherBuildings.Keys){					
				foreach (string t in config.refresherBuildings[s].ToArray()){
					uint id = 0;
					UInt32.TryParse ( t.Split(':')?[1], out id);
					if(id!=0&&BuildingManager.server.GetBuilding(id)==null){
						config.refresherBuildings[s].Remove(t);//
					}
					
				}
			}
			return true;
		}
		bool cleanClans(){
			
			foreach (string s in config.buildingRefreshers.Keys.ToArray()){
				uint id = 0;
				UInt32.TryParse ( s.Split(':')?[1], out id);
				BuildingManager.Building bldg = BuildingManager.server.GetBuilding(id);
				if(id!=0&&bldg!=null){
					BuildingPrivlidge bp = bldg.GetDominatingBuildingPrivilege();
					if(bp!=null){
						ulong owner = bp.OwnerID;						
						foreach (string t in config.buildingRefreshers[s].ToArray()){
							ulong id2 = 0;
							UInt64.TryParse( t, out id2);
							if(id2==0){
								config.buildingRefreshers[s].Remove(t);
								if(config.refresherBuildings.ContainsKey(t)){
									if(config.refresherBuildings[t].Contains(s)){
										config.refresherBuildings[t].Remove(s);
									}
								}
							}
						}
						string u = (string)Interface.CallHook("GetClanOf", new object[] {owner});
						if(u!=null&&u!=""){
							config.buildingRefreshers[s].Add(u);
							if(!config.refresherBuildings.ContainsKey(u)){
								config.refresherBuildings.Add(u, new List<string>());
							}
							if(!config.buildingRefreshers[s].Contains(u))
								config.buildingRefreshers[s].Add(u);
							if(!config.refresherBuildings[u].Contains(s))
								config.refresherBuildings[u].Add(s);
							
						}
					}
				}
			}
			return true;
		}
		bool stateSwitching(){
			foreach(string s in config.protectionTimeouts.Keys.ToArray()){
				if(config.protectionTimeouts[s]> System.DateTime.Now.Ticks){
					if(config.protectionCooldowns.ContainsKey(s)&&
					config.protectionCooldowns[s]<System.DateTime.Now.Ticks){
						if(!config.protectionState.ContainsKey(s)){
							config.protectionState.Add(s,true);
						}
						if(config.protectionState[s]){
							
							uint id = 0;
							UInt32.TryParse ( s.Split(':')?[1], out id);
							if(id!=0&&BuildingManager.server.GetBuilding(id)!=null){		
								BuildingPrivlidge bp = BuildingManager.server.GetBuilding(id).GetDominatingBuildingPrivilege();
								if(bp!=null){
									ConVar.Chat.Broadcast("Building with ID "+(s.Split(':')[1])+" at position "+getGrid(bp.transform.position)+" turned PVP", "Orphans2PVP", "#ca7", 0UL);
								}
							}
						}
						config.protectionState[s]=false;
					}else{						
						if(!config.protectionState.ContainsKey(s)){
							config.protectionState.Add(s,false);
						}
						if(!config.protectionState[s]){
							
							uint id = 0;
							UInt32.TryParse ( s.Split(':')?[1], out id);
							if(id!=0&&BuildingManager.server.GetBuilding(id)!=null){		
								BuildingPrivlidge bp = BuildingManager.server.GetBuilding(id).GetDominatingBuildingPrivilege();
								if(bp!=null){
									ConVar.Chat.Broadcast("Building with ID "+(s.Split(':')[1])+" at position "+getGrid(bp.transform.position)+" protected", "Orphans2PVP", "#ca7", 0UL);
								}
							}
						}
						config.protectionState[s]=true;
					}
				}else{
					if(!config.protectionState.ContainsKey(s)){
						config.protectionState.Add(s,true);
					}
					if(config.protectionState[s]){
						
						uint id = 0;
						UInt32.TryParse ( s.Split(':')?[1], out id);
						if(id!=0&&BuildingManager.server.GetBuilding(id)!=null){		
							BuildingPrivlidge bp = BuildingManager.server.GetBuilding(id).GetDominatingBuildingPrivilege();
							if(bp!=null){
								ConVar.Chat.Broadcast("Building with ID "+(s.Split(':')[1])+" at position "+getGrid(bp.transform.position)+" protected", "Orphans2PVP", "#ca7", 0UL);
							}
						}
					}
					config.protectionState[s]=false;
					
				}
			}
			return true;
		}
	
		
		
		Dictionary<BasePlayer, long> pvpTimeouts = new Dictionary<BasePlayer, long>();
		private Configuration config;
			class Configuration{
				[JsonProperty("timeoutTime", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public long timeoutTime = 30;
				[JsonProperty("cooldownTime", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public long cooldownTime = 5;
				
				[JsonProperty("protectionTimeouts", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public Dictionary<string,long> protectionTimeouts = new Dictionary<string,long>();
				[JsonProperty("protectionCooldowns", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public Dictionary<string,long> protectionCooldowns = new Dictionary<string,long>();
				[JsonProperty("protectionState", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public Dictionary<string,bool> protectionState = new Dictionary<string,bool>();
				[JsonProperty("buildingRefreshers", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public Dictionary<string, List<string>> buildingRefreshers = new Dictionary<string, List<string>>();
				[JsonProperty("refresherBuildings", ObjectCreationHandling = ObjectCreationHandling.Replace)]
				public Dictionary<string, List<string>> refresherBuildings = new Dictionary<string, List<string>>();					
				
				
				public Dictionary<BaseCombatEntity.Faction, Dictionary<string,Vector3>> newFactionSpawns = new Dictionary<BaseCombatEntity.Faction, Dictionary<string,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();
			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)){
						SaveConfig();}
				}
				catch{
					LoadDefaultConfig();}
			}
			protected override void SaveConfig(){
				Config.WriteObject(config, true);
			}
		
		string getGrid(Vector3 pos) {
			char letter = 'A';
			var x = Mathf.Floor((pos.x+(ConVar.Server.worldsize/2)) / 146.3f);
			var z = (Mathf.Floor(ConVar.Server.worldsize/146.3f))-Mathf.Floor((pos.z+(ConVar.Server.worldsize/2)) / 146.3f) - 1;
			letter = (char)(((int)letter)+(x%26));
			if(x>25){
				return $"{letter}{letter}{z}";
			}else{
				return $"{letter}{z}";
			}
		}
		object CanEntityTakeDamage(BaseCombatEntity entity, HitInfo hitinfo){
			if(hitinfo.Initiator == null || hitinfo.HitEntity == null) return null;
			BuildingPrivlidge bp = hitinfo.Initiator.GetBuildingPrivilege(hitinfo.Initiator.WorldSpaceBounds());
			bool? result = null;
			if(bp!=null){
				string s = "Building:"+bp.GetBuilding().ID.ToString();
				if(config.protectionTimeouts.ContainsKey(s) &&
					(config.protectionTimeouts[s]< System.DateTime.Now.Ticks || (
					config.protectionCooldowns.ContainsKey(s) && config.protectionCooldowns[s] > System.DateTime.Now.Ticks
					)
				)){
					if(hitinfo.Initiator is BasePlayer){
						if(pvpTimeouts.ContainsKey(hitinfo.Initiator as BasePlayer)){
							pvpTimeouts[hitinfo.Initiator as BasePlayer]= System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
						}else{							
							pvpTimeouts.Add(hitinfo.Initiator as BasePlayer, System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime));
							ConsoleNetwork.SendClientCommand((hitinfo.Initiator as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"You are in a PVP zone - PVP timeout enabled for you","Orphans2PVP", "#ca7", 1f });
						}
					}
					if(hitinfo.HitEntity is BasePlayer){
						if(pvpTimeouts.ContainsKey(hitinfo.HitEntity as BasePlayer)){
							pvpTimeouts[hitinfo.HitEntity as BasePlayer]= System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
						}else{							
							pvpTimeouts.Add(hitinfo.HitEntity as BasePlayer, System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime));
							ConsoleNetwork.SendClientCommand((hitinfo.HitEntity as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"You are in a PVP zone - PVP timeout enabled for you","Orphans2PVP", "#ca7", 1f });
						}
					}
					result=true;
				}
				if(config.protectionCooldowns.ContainsKey(s) && config.protectionCooldowns[s] < System.DateTime.Now.Ticks && config.protectionCooldowns[s]!=0){			
		
					ConVar.Chat.Broadcast("Building with ID "+(s.Split(':')[1])+" at position "+getGrid(bp.transform.position)+" protected", "Orphans2PVP", "#ca7", 0UL);
					config.protectionCooldowns[s]=0;
				}
			}
			if(hitinfo.HitEntity is BasePlayer && pvpTimeouts.ContainsKey(hitinfo.HitEntity as BasePlayer)){
				if(pvpTimeouts[hitinfo.HitEntity as BasePlayer]>System.DateTime.Now.Ticks){							
					if(hitinfo.Initiator is BasePlayer){
						if(pvpTimeouts.ContainsKey(hitinfo.Initiator as BasePlayer)){
							pvpTimeouts[hitinfo.Initiator as BasePlayer]= System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
						}else{							
							pvpTimeouts.Add(hitinfo.Initiator as BasePlayer, System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime));
							ConsoleNetwork.SendClientCommand((hitinfo.Initiator as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"That player was PVP, and now so are you","Orphans2PVP", "#ca7", 1f });

						}
					}
					result=true;
				}else{
					pvpTimeouts.Remove(hitinfo.HitEntity as BasePlayer);
					ConsoleNetwork.SendClientCommand((hitinfo.HitEntity as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"You are no longer PVP","Orphans2PVP", "#ca7", 1f });
					ConsoleNetwork.SendClientCommand((hitinfo.Initiator as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"That player is no longer PVP - hit player","Orphans2PVP", "#ca7", 1f });
				}
			
			}					
			if(hitinfo.Initiator is BasePlayer){
				if(pvpTimeouts.ContainsKey(hitinfo.Initiator as BasePlayer) && pvpTimeouts[hitinfo.Initiator as BasePlayer]<System.DateTime.Now.Ticks){
					pvpTimeouts.Remove(hitinfo.Initiator as BasePlayer);
					ConsoleNetwork.SendClientCommand((hitinfo.Initiator as BasePlayer).net.connection, "chat.add2", new object[] { 0, 0,"You are no longer PVP","Orphans2PVP", "#ca7", 1f });
				}
			}
			return result;
		}
		
		void OnServerInitialized()
		{
			///clans = (Clans)Manager.GetPlugin("Clans");	
			//*
			timer.Every(100f, () =>
			{
				doPhase();//
			});		//*/
            Subscribe(nameof(CanEntityTakeDamage));
			doPhase();
			doPhase();
			doPhase();
		}
		
		void addRefresher(BuildingPrivlidge entity, BasePlayer player)
		{
			string bID = "Building:"+entity.GetBuilding().ID.ToString();
			if(config.protectionTimeouts.ContainsKey(bID)){
				config.protectionTimeouts[bID]= System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime);
				
			}else{
				config.protectionTimeouts.Add(bID, System.DateTime.Now.Ticks+ (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime));
			}
			string clan = //*(
			(string)Interface.CallHook("GetClanOf", new object[] {player});
			/*/
			"";
			//*/
			if(config.buildingRefreshers.ContainsKey(bID)){
				if(! config.buildingRefreshers[bID].Contains(player.userID.ToString())){
					config.buildingRefreshers[bID].Add(player.userID.ToString());
				}
				if(clan!=null&&clan!=""){
					if(!config.buildingRefreshers[bID].Contains(clan)){
						config.buildingRefreshers[bID].Add(clan);
					}					
				}
				
			}else{
				config.buildingRefreshers.Add(bID, new List<string>());
				config.buildingRefreshers[bID].Add(player.userID.ToString());
				if(clan!=null&&clan!=""){
					config.buildingRefreshers[bID].Add(clan);
				}
			}
			if(config.refresherBuildings.ContainsKey(player.userID.ToString())){
				if(! config.refresherBuildings[player.userID.ToString()].Contains(bID)){
					config.refresherBuildings[player.userID.ToString()].Add(bID);
				}
				
			}else{
				config.refresherBuildings.Add(player.userID.ToString(), new List<string>());
				config.refresherBuildings[player.userID.ToString()].Add(bID);
			}
			
			if(clan!=null&&clan!=""){
				if(config.refresherBuildings.ContainsKey(clan)){
					if(! config.refresherBuildings[clan].Contains(bID)){
						config.refresherBuildings[clan].Add(bID);
					}
					
				}else{
					config.refresherBuildings.Add(clan, new List<string>());
					config.refresherBuildings[clan].Add(bID);
				}
			}
		}
		object OnConstructionPlace(BuildingPrivlidge entity, Construction component, Construction.Target constructionTarget, BasePlayer player)
		{
			if(player!=null){
				addRefresher(entity,player);
			}
			return null;
		}
		
		
		void Loaded(){	
			LoadConfig();
		}
		void Unload(){
			SaveConfig();
		}
				
		void OnPlayerDisconnected(BasePlayer player, string reason)
		{
			List<string> buildings = new List<string>();
			string clan = //*
			(string)Interface.CallHook("GetClanOf", new object[] {player});
			/*/
			"";
			//*/
			if(clan!=null&&clan!=""){
					
				if(config.refresherBuildings.ContainsKey(clan)){
					buildings.AddRange(config.refresherBuildings[clan]);
				}
			}
			if(config.refresherBuildings.ContainsKey(player.userID.ToString())){
				buildings.AddRange(config.refresherBuildings[player.userID.ToString()]);
			}	
			foreach(string s in buildings){
				if(config.protectionTimeouts.ContainsKey(s)){
					config.protectionTimeouts[s]= System.DateTime.Now.Ticks;
					
				}else{
					config.protectionTimeouts.Add(s, System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime));
				}
			}
		}
		void OnPlayerConnected(BasePlayer player)
		{
			List<string> buildings = new List<string>();
			string clan = //*
			(string)Interface.CallHook("GetClanOf", new object[] {player});
			/*/
			"";
			//*/
			if(clan!=null&&clan!=""){
					
				if(config.refresherBuildings.ContainsKey(clan)){
					buildings.AddRange(config.refresherBuildings[clan]);
				}
			}
			if(config.refresherBuildings.ContainsKey(player.userID.ToString())){
				buildings.AddRange(config.refresherBuildings[player.userID.ToString()]);
			}			
			
			foreach(string s in buildings){
				if(config.protectionTimeouts.ContainsKey(s)){
					if(config.protectionTimeouts[s]< System.DateTime.Now.Ticks){
						if(config.protectionCooldowns.ContainsKey(s)){
							config.protectionCooldowns[s]=System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
						}else{
							config.protectionCooldowns.Add(s,System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime));							
						}
					}
					config.protectionTimeouts[s]= System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime);
					
				}else{
					config.protectionTimeouts.Add(s, System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime));
				}
			}
			
		}
		
		
		void OnEntityKill(BuildingPrivlidge entity)
		{
			string bID = "Building:"+entity.GetBuilding().ID.ToString();
			if(config.buildingRefreshers.ContainsKey(bID)){
				foreach(string s in config.buildingRefreshers[bID].ToArray()){
					if(config.refresherBuildings.ContainsKey(s)){
						if(config.refresherBuildings[s].Contains(bID)){
							config.refresherBuildings[s].Remove(bID);
						}
					}
					config.buildingRefreshers[bID].Remove(s);
				}		
			}
			if(config.protectionTimeouts.ContainsKey(bID)){	
				config.protectionTimeouts[bID]=0;
			}
			if(config.protectionCooldowns.ContainsKey(bID)){	
				config.protectionCooldowns[bID]=System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
			}
		}
		
		object OnCupboardClearList(BuildingPrivlidge privilege, BasePlayer player)
		{
			string bID = "Building:"+privilege.GetBuilding().ID.ToString();
			if(config.buildingRefreshers.ContainsKey(bID)){
				foreach(string s in config.buildingRefreshers[bID].ToArray()){
					if(config.refresherBuildings.ContainsKey(s)){
						if(config.refresherBuildings[s].Contains(bID)){
							config.refresherBuildings[s].Remove(bID);
						}
					}
					config.buildingRefreshers[bID].Remove(s);
				}		
			}	
			if(config.protectionCooldowns.ContainsKey(bID)){	
				config.protectionCooldowns[bID]=System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime);
			}else{
				config.protectionCooldowns.Add(bID,System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.cooldownTime));
			}
			if(!config.protectionState.ContainsKey(bID)){
				config.protectionState.Add(bID,false);
			}else{
				config.protectionState[bID]=false;
			}
			ConsoleNetwork.SendClientCommand(player.net.connection, "chat.add2", new object[] { 0, 0,"You have PVP'd this house","Orphans2PVP", "#ca7", 1f });
			ConVar.Chat.Broadcast("Building with ID "+(bID.Split(':')[1])+" at position "+getGrid(privilege.transform.position)+" was made PVP", "Orphans2PVP", "#ca7", 0UL);
			return null;
		}
		
		object OnCupboardAuthorize(BuildingPrivlidge privilege, BasePlayer player)
		{//
			
			string bID = "Building:"+privilege.GetBuilding().ID.ToString();
			addRefresher(privilege,player);
			if(config.protectionTimeouts.ContainsKey(bID)){	
				config.protectionTimeouts[bID]=System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime);
			}else{	
				config.protectionTimeouts.Add(bID,System.DateTime.Now.Ticks + (System.TimeSpan.TicksPerMillisecond * minuteMultiplier *  config.timeoutTime));
			}
			ConsoleNetwork.SendClientCommand(player.net.connection, "chat.add2", new object[] { 0, 0,"You have refreshed this house - PVP will be disabled in "+config.cooldownTime.ToString("0")+" minutes","Orphans2PVP", "#ca7", 1f });
			return null;
		}
		/*
		void UpdateZoneTypes(){
			foreach(string s in config.protectionTimeouts.Keys){
				string bID = s;
				if(!config.protectionState.ContainsKey(s)){		
					config.protectionState.Add(s,false);
				}
				if(config.protectionTimeouts[s]< System.DateTime.Now.Ticks && config.protectionState[s]){	
						config.protectionState[s]=false;
						ConVar.Chat.Broadcast("Building with ID "+(bID.Split(':')[1])+" no longer protected", "Orphans2PVP", "#ca7", 0UL);
				}else{
					if((!config.protectionCooldowns.ContainsKey(s) || config.protectionCooldowns[s]< System.DateTime.Now.Ticks)){
						config.protectionState[s]=true;
						ConVar.Chat.Broadcast("Building with ID "+(bID.Split(':')[1])+" protected", "Orphans2PVP", "#ca7", 0UL);
						
					}
				}
			}
		}*/
	}
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху