mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-23 04:32:50 -04:00
Add clickable text in bots.
This commit is contained in:
parent
5458a34f58
commit
2c05d65247
@ -38,17 +38,7 @@ namespace MCGalaxy.Blocks.Extended {
|
||||
message = message.Replace("@p", p.name);
|
||||
|
||||
if (message != p.prevMsg || (alwaysRepeat || ServerConfig.RepeatMBs)) {
|
||||
string text;
|
||||
List<string> cmds = GetParts(message, out text);
|
||||
if (text != null) Player.Message(p, text);
|
||||
|
||||
if (cmds.Count == 1) {
|
||||
string[] parts = cmds[0].SplitSpaces(2);
|
||||
p.HandleCommand(parts[0], parts.Length > 1 ? parts[1] : "");
|
||||
} else if (cmds.Count > 0) {
|
||||
p.HandleCommands(cmds);
|
||||
}
|
||||
p.prevMsg = message;
|
||||
Execute(p, message);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
@ -56,10 +46,56 @@ namespace MCGalaxy.Blocks.Extended {
|
||||
}
|
||||
}
|
||||
|
||||
public static void Execute(Player p, string message) {
|
||||
string text;
|
||||
List<string> cmds = GetParts(message, out text);
|
||||
if (text != null) Player.Message(p, text);
|
||||
|
||||
if (cmds.Count == 1) {
|
||||
string[] parts = cmds[0].SplitSpaces(2);
|
||||
p.HandleCommand(parts[0], parts.Length > 1 ? parts[1] : "");
|
||||
} else if (cmds.Count > 0) {
|
||||
p.HandleCommands(cmds);
|
||||
}
|
||||
p.prevMsg = message;
|
||||
}
|
||||
|
||||
public static bool Validate(Player p, string message, bool allCmds) {
|
||||
string text;
|
||||
List<string> cmds = MessageBlock.GetParts(message, out text);
|
||||
foreach (string cmd in cmds) {
|
||||
if (!CheckCommand(p, cmd, allCmds)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CheckCommand(Player p, string message, bool allCmds) {
|
||||
string[] parts = message.SplitSpaces(2);
|
||||
string alias = parts[0], cmdArgs = "";
|
||||
Command.Search(ref alias, ref cmdArgs);
|
||||
|
||||
foreach (Command cmd in Command.all.commands) {
|
||||
if (p.group.CanExecute(cmd) && (allCmds || !cmd.type.Contains("mod"))) continue;
|
||||
|
||||
if (IsCommand(message, cmd.name) || IsCommand(alias, cmd.name)) {
|
||||
Player.Message(p, "You cannot use %T/{0} %Sin a messageblock.", cmd.name); return false;
|
||||
}
|
||||
if (cmd.shortcut.Length > 0 && IsCommand(message, cmd.shortcut)) {
|
||||
Player.Message(p, "You cannot use %T/{0} %Sin a messageblock.", cmd.name); return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsCommand(string message, string cmd) {
|
||||
return message.CaselessEq(cmd) || message.CaselessStarts(cmd + " ");
|
||||
}
|
||||
|
||||
|
||||
static string[] sep = new string[] { " |/" };
|
||||
const StringSplitOptions opts = StringSplitOptions.RemoveEmptyEntries;
|
||||
static List<string> empty = new List<string>();
|
||||
internal static List<string> GetParts(string message, out string text) {
|
||||
static List<string> GetParts(string message, out string text) {
|
||||
if (message.IndexOf('|') == -1) return ParseSingle(message, out text);
|
||||
|
||||
string[] parts = message.Split(sep, opts);
|
||||
|
@ -60,16 +60,7 @@ namespace MCGalaxy.Bots {
|
||||
foreach (BotProperties props in SavedBots) {
|
||||
if (lvl.name != props.Level) continue;
|
||||
PlayerBot bot = new PlayerBot(props.Name, lvl);
|
||||
bot.Pos = new Position(props.X, props.Y, props.Z);
|
||||
|
||||
bot.SetYawPitch(props.RotX, props.RotY);
|
||||
Orientation rot = bot.Rot;
|
||||
rot.RotX = props.BodyX; rot.RotZ = props.BodyZ;
|
||||
bot.Rot = rot;
|
||||
|
||||
bot.SkinName = props.Skin; bot.Model = props.Model; bot.color = props.Color;
|
||||
bot.AIName = props.AI; bot.hunt = props.Hunt; bot.kill = props.Kill;
|
||||
bot.DisplayName = props.DisplayName;
|
||||
props.ApplyTo(bot);
|
||||
|
||||
bot.ModelBB = AABB.ModelAABB(bot.Model, lvl);
|
||||
LoadAi(props, bot);
|
||||
@ -124,7 +115,7 @@ namespace MCGalaxy.Bots {
|
||||
|
||||
SavedBots.RemoveAt(i);
|
||||
removed++; i--;
|
||||
}
|
||||
}
|
||||
if (removed > 0) Save();
|
||||
}
|
||||
}
|
||||
@ -137,7 +128,7 @@ namespace MCGalaxy.Bots {
|
||||
BotProperties props = SavedBots[i];
|
||||
if (!props.Level.CaselessEq(srcLevel)) continue;
|
||||
|
||||
props.Level = dstLevel;
|
||||
props.Level = dstLevel;
|
||||
moved++;
|
||||
}
|
||||
if (moved > 0) Save();
|
||||
@ -187,6 +178,7 @@ namespace MCGalaxy.Bots {
|
||||
public string Skin { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string Color { get; set; }
|
||||
public string ClickedOnText { get; set; }
|
||||
|
||||
public string AI { get; set; }
|
||||
public bool Kill { get; set; }
|
||||
@ -208,20 +200,37 @@ namespace MCGalaxy.Bots {
|
||||
Kill = bot.kill; Hunt = bot.hunt;
|
||||
DisplayName = bot.DisplayName;
|
||||
CurInstruction = bot.cur;
|
||||
ClickedOnText = bot.ClickedOnText;
|
||||
|
||||
X = bot.Pos.X; Y = bot.Pos.Y; Z = bot.Pos.Z;
|
||||
RotX = bot.Rot.RotY; RotY = bot.Rot.HeadX;
|
||||
BodyX = bot.Rot.RotX; BodyZ = bot.Rot.RotZ;
|
||||
}
|
||||
|
||||
public void ApplyTo(PlayerBot bot) {
|
||||
bot.Pos = new Position(X, Y, Z);
|
||||
bot.SetYawPitch(RotX, RotY);
|
||||
Orientation rot = bot.Rot;
|
||||
rot.RotX = BodyX; rot.RotZ = BodyZ;
|
||||
bot.Rot = rot;
|
||||
|
||||
bot.SkinName = Skin; bot.Model = Model; bot.color = Color;
|
||||
bot.AIName = AI; bot.hunt = Hunt; bot.kill = Kill;
|
||||
bot.DisplayName = DisplayName;
|
||||
|
||||
bot.cur = CurInstruction;
|
||||
bot.ClickedOnText = ClickedOnText;
|
||||
}
|
||||
|
||||
public BotProperties Copy() {
|
||||
BotProperties copy = new BotProperties();
|
||||
copy.DisplayName = DisplayName; copy.Name = Name;
|
||||
copy.Level = Level; copy.Skin = Skin;
|
||||
copy.Model = Model; copy.Color = Color;
|
||||
|
||||
copy.AI = AI; copy.Kill = Kill;
|
||||
copy.Hunt = Hunt; copy.CurInstruction = CurInstruction;
|
||||
copy.AI = AI; copy.Kill = Kill; copy.Hunt = Hunt;
|
||||
copy.CurInstruction = CurInstruction;
|
||||
copy.ClickedOnText = ClickedOnText;
|
||||
|
||||
copy.X = X; copy.Y = Y; copy.Z = Z;
|
||||
copy.RotX = RotX; copy.RotY = RotY;
|
||||
|
@ -29,6 +29,7 @@ namespace MCGalaxy {
|
||||
|
||||
public string AIName = "", color;
|
||||
public string name, DisplayName;
|
||||
public string ClickedOnText;
|
||||
public string ColoredName { get { return color + DisplayName; } }
|
||||
|
||||
public byte id;
|
||||
|
119
MCGalaxy/Commands/Bots/CmdBot.cs
Normal file
119
MCGalaxy/Commands/Bots/CmdBot.cs
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2010 MCSharp team (Modified for use with MCZall/MCLawl/MCGalaxy)
|
||||
|
||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||
not use this file except in compliance with the Licenses. You may
|
||||
obtain a copy of the Licenses at
|
||||
|
||||
http://www.opensource.org/licenses/ecl2.php
|
||||
http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the Licenses are distributed on an "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the Licenses for the specific language governing
|
||||
permissions and limitations under the Licenses.
|
||||
*/
|
||||
using MCGalaxy.Blocks.Extended;
|
||||
using MCGalaxy.Bots;
|
||||
|
||||
namespace MCGalaxy.Commands.Bots {
|
||||
public sealed class CmdBot : Command {
|
||||
public override string name { get { return "Bot"; } }
|
||||
public override string type { get { return CommandTypes.Moderation; } }
|
||||
public override bool museumUsable { get { return false; } }
|
||||
public override LevelPermission defaultRank { get { return LevelPermission.Admin; } }
|
||||
public override bool SuperUseable { get { return false; } }
|
||||
public override CommandAlias[] Aliases {
|
||||
get { return new[] { new CommandAlias("BotAdd", "add"), new CommandAlias("BotRemove", "remove") }; }
|
||||
}
|
||||
|
||||
public override void Use(Player p, string message) {
|
||||
if (message.Length == 0) { Help(p); return; }
|
||||
string[] args = message.SplitSpaces(3);
|
||||
if (args.Length < 2) { Help(p); return; }
|
||||
if (!Formatter.ValidName(p, args[1], "bot")) return;
|
||||
|
||||
if (args[0].CaselessEq("add")) {
|
||||
AddBot(p, args[1]);
|
||||
} else if (args[0].CaselessEq("remove")) {
|
||||
RemoveBot(p, args[1]);
|
||||
} else if (args[0].CaselessEq("text")) {
|
||||
string text = args.Length > 2 ? args[2] : null;
|
||||
SetBotText(p, args[1], text);
|
||||
} else {
|
||||
Help(p);
|
||||
}
|
||||
}
|
||||
|
||||
void AddBot(Player p, string botName) {
|
||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
||||
Player.Message(p, "Hence, you cannot add bots to this map.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (BotExists(p.level, botName)) {
|
||||
Player.Message(p, "A bot with that name already exists."); return;
|
||||
}
|
||||
|
||||
PlayerBot bot = new PlayerBot(botName, p.level);
|
||||
bot.Pos = p.Pos;
|
||||
bot.SetYawPitch(p.Rot.RotY, 0);
|
||||
|
||||
Player.Message(p, "You added the bot " + bot.ColoredName );
|
||||
PlayerBot.Add(bot);
|
||||
}
|
||||
|
||||
static bool BotExists(Level lvl, string name) {
|
||||
PlayerBot[] bots = lvl.Bots.Items;
|
||||
foreach (PlayerBot bot in bots) {
|
||||
if (bot.name.CaselessEq(name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RemoveBot(Player p, string botName) {
|
||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
||||
Player.Message(p, "Hence, you cannot remove bots from this map.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (botName.CaselessEq("all")) {
|
||||
PlayerBot.RemoveAllFromLevel(p.level); return;
|
||||
} else {
|
||||
PlayerBot bot = Matcher.FindBots(p, botName);
|
||||
if (bot == null) return;
|
||||
|
||||
PlayerBot.Remove(bot);
|
||||
Player.Message(p, "Removed bot {0}", bot.ColoredName);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBotText(Player p, string botName, string text) {
|
||||
PlayerBot bot = Matcher.FindBots(p, botName);
|
||||
if (bot == null) return;
|
||||
|
||||
if (text == null) {
|
||||
Player.Message(p, "Removed text shown when bot {0} %Sclicked on", bot.ColoredName);
|
||||
bot.ClickedOnText = null;
|
||||
} else {
|
||||
if (!MessageBlock.Validate(p, text, false)) return;
|
||||
Player.Message(p, "Set text shown when bot {0} %Sis clicked on to {1}", bot.ColoredName, text);
|
||||
bot.ClickedOnText = text;
|
||||
}
|
||||
BotsFile.UpdateBot(bot);
|
||||
}
|
||||
|
||||
public override void Help(Player p) {
|
||||
Player.Message(p, "%T/Bot add [name]");
|
||||
Player.Message(p, "%HAdds a new bot at your position.");
|
||||
Player.Message(p, "%T/Bot remove [name]");
|
||||
Player.Message(p, "%HRemove a bot on the same level as you");
|
||||
Player.Message(p, "%HIf [name] is \"all\", all bots on your map are removed");
|
||||
Player.Message(p, "%T/Bot text [name] <text>");
|
||||
Player.Message(p, "%HSets the text shown when a player clicks on this bot");
|
||||
Player.Message(p, "%HSee %T/help mb %Hfor more details on <text>");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
Copyright 2010 MCSharp team (Modified for use with MCZall/MCLawl/MCGalaxy)
|
||||
|
||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||
not use this file except in compliance with the Licenses. You may
|
||||
obtain a copy of the Licenses at
|
||||
|
||||
http://www.opensource.org/licenses/ecl2.php
|
||||
http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the Licenses are distributed on an "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the Licenses for the specific language governing
|
||||
permissions and limitations under the Licenses.
|
||||
*/
|
||||
namespace MCGalaxy.Commands.Bots {
|
||||
public sealed class CmdBotAdd : Command {
|
||||
public override string name { get { return "BotAdd"; } }
|
||||
public override string type { get { return CommandTypes.Moderation; } }
|
||||
public override bool museumUsable { get { return false; } }
|
||||
public override LevelPermission defaultRank { get { return LevelPermission.Admin; } }
|
||||
public override bool SuperUseable { get { return false; } }
|
||||
|
||||
public override void Use(Player p, string message) {
|
||||
if (message.Length == 0) { Help(p); return; }
|
||||
|
||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
||||
Player.Message(p, "Hence, you cannot add bots to this map.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Formatter.ValidName(p, message, "bot")) return;
|
||||
if (BotExists(p.level, message)) {
|
||||
Player.Message(p, "A bot with that name already exists."); return;
|
||||
}
|
||||
|
||||
PlayerBot bot = new PlayerBot(message, p.level);
|
||||
bot.Pos = p.Pos;
|
||||
bot.SetYawPitch(p.Rot.RotY, 0);
|
||||
|
||||
Player.Message(p, "You added the bot " + bot.ColoredName + "%S.");
|
||||
PlayerBot.Add(bot);
|
||||
}
|
||||
|
||||
static bool BotExists(Level lvl, string name) {
|
||||
PlayerBot[] bots = lvl.Bots.Items;
|
||||
foreach (PlayerBot bot in bots) {
|
||||
if (bot.name.CaselessEq(name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Help(Player p) {
|
||||
Player.Message(p, "%T/BotAdd [name]");
|
||||
Player.Message(p, "%HAdds a new bot at your position.");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright 2010 MCSharp team (Modified for use with MCZall/MCLawl/MCGalaxy)
|
||||
|
||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||
not use this file except in compliance with the Licenses. You may
|
||||
obtain a copy of the Licenses at
|
||||
|
||||
http://www.opensource.org/licenses/ecl2.php
|
||||
http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the Licenses are distributed on an "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the Licenses for the specific language governing
|
||||
permissions and limitations under the Licenses.
|
||||
*/
|
||||
using System;
|
||||
namespace MCGalaxy.Commands.Bots {
|
||||
public sealed class CmdBotRemove : Command {
|
||||
public override string name { get { return "BotRemove"; } }
|
||||
public override string type { get { return CommandTypes.Moderation; } }
|
||||
public override bool museumUsable { get { return false; } }
|
||||
public override LevelPermission defaultRank { get { return LevelPermission.Admin; } }
|
||||
public override bool SuperUseable { get { return false; } }
|
||||
|
||||
public override void Use(Player p, string message) {
|
||||
if (message.Length == 0) { Help(p); return; }
|
||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
||||
Player.Message(p, "Hence, you cannot change remove bots from this map.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.CaselessEq("all")) {
|
||||
PlayerBot.RemoveAllFromLevel(p.level); return;
|
||||
} else {
|
||||
PlayerBot bot = Matcher.FindBots(p, message);
|
||||
if (bot == null) return;
|
||||
|
||||
PlayerBot.Remove(bot);
|
||||
Player.Message(p, "Removed bot {0}%S", bot.ColoredName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Help(Player p) {
|
||||
Player.Message(p, "%T/BotRemove [name]");
|
||||
Player.Message(p, "%HRemove a bot on the same level as you");
|
||||
Player.Message(p, "%HIf [name] is \"all\", all bots on your map are removed");
|
||||
}
|
||||
}
|
||||
}
|
@ -55,11 +55,8 @@ namespace MCGalaxy.Commands.Building {
|
||||
data.Message = args[1];
|
||||
}
|
||||
|
||||
string text;
|
||||
List<string> cmds = MessageBlock.GetParts(data.Message, out text);
|
||||
foreach (string cmd in cmds) {
|
||||
if (!CheckCommand(p, cmd)) return;
|
||||
}
|
||||
bool allCmds = HasExtraPerm(p, 1);
|
||||
if (!MessageBlock.Validate(p, data.Message, allCmds)) return;
|
||||
|
||||
Player.Message(p, "Place where you wish the message block to go.");
|
||||
p.MakeSelection(1, data, PlacedMark);
|
||||
|
@ -84,8 +84,12 @@ namespace MCGalaxy.Core {
|
||||
|
||||
internal static void HandlePlayerClick(Player p, MouseButton button, MouseAction action, ushort yaw, ushort pitch,
|
||||
byte entity, ushort x, ushort y, ushort z, TargetBlockFace face) {
|
||||
if (p.level.Config.Deletable || action != MouseAction.Pressed || !p.level.IsValidPos(x, y, z)) return;
|
||||
if (action != MouseAction.Pressed) return;
|
||||
|
||||
bool validPos = p.level.IsValidPos(x, y, z);
|
||||
if (entity != Entities.SelfID && !validPos && ClickOnBot(p, entity)) return;
|
||||
|
||||
if (p.level.Config.Deletable || !validPos) return;
|
||||
ExtBlock block = p.level.GetBlock(x, y, z);
|
||||
bool isMB = p.level.BlockProps[block.Index].IsMessageBlock;
|
||||
bool isPortal = p.level.BlockProps[block.Index].IsPortal;
|
||||
@ -94,19 +98,36 @@ namespace MCGalaxy.Core {
|
||||
if (isPortal) { Portal.Handle(p, x, y, z); }
|
||||
}
|
||||
|
||||
static bool ClickOnBot(Player p, byte entity) {
|
||||
PlayerBot[] bots = p.level.Bots.Items;
|
||||
for (int i = 0; i < bots.Length; i++) {
|
||||
if (bots[i].EntityID != entity) continue;
|
||||
if (bots[i].ClickedOnText == null) return false;
|
||||
|
||||
Vec3F32 delta = p.Pos.ToVec3F32() - bots[i].Pos.ToVec3F32();
|
||||
float reachSq = p.ReachDistance * p.ReachDistance;
|
||||
if (delta.LengthSquared > (reachSq + 1)) return false;
|
||||
|
||||
string message = bots[i].ClickedOnText;
|
||||
MessageBlock.Execute(p, message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update rank colors and rank prefixes for online players
|
||||
internal static void HandleGroupLoad() {
|
||||
Player[] players = PlayerInfo.Online.Items;
|
||||
foreach (Player p in players) {
|
||||
p.group = Group.Find(p.group.Permission);
|
||||
if (p.group == null) p.group = Group.standard;
|
||||
if (p.group == null) p.group = Group.standard;
|
||||
p.SetPrefix();
|
||||
|
||||
string dbCol = PlayerData.FindDBColor(p);
|
||||
if (dbCol.Length == 0 && p.color != p.group.Color) {
|
||||
p.color = p.group.Color;
|
||||
Entities.GlobalRespawn(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ namespace MCGalaxy {
|
||||
/// <summary> Z fixed-point location in the world. </summary>
|
||||
public int Z;
|
||||
|
||||
public Vec3F32 ToVec3F32() { return new Vec3F32(X / 32.0f, Y / 32.0f, Z / 32.0f); }
|
||||
|
||||
|
||||
public Position(int x, int y, int z) { X = x; Y = y; Z = z; }
|
||||
|
||||
@ -50,7 +52,7 @@ namespace MCGalaxy {
|
||||
public int BlockY { get { return Y >> 5; } }
|
||||
|
||||
/// <summary> Z block coordinate of this position. </summary>
|
||||
public int BlockZ { get { return Z >> 5; } }
|
||||
public int BlockZ { get { return Z >> 5; } }
|
||||
|
||||
|
||||
public override bool Equals(object obj) { return (obj is Position) && Equals((Position)obj); }
|
||||
|
@ -118,9 +118,8 @@
|
||||
<Compile Include="Chat\LineWrapper.cs" />
|
||||
<Compile Include="Chat\ProfanityFilter.cs" />
|
||||
<Compile Include="Commands\Alias.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBotAdd.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBot.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBotAI.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBotRemove.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBots.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBotSet.cs" />
|
||||
<Compile Include="Commands\Bots\CmdBotSummon.cs" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user