Fixed edge cases introduced by recent combat changes
This commit is contained in:
@@ -35,6 +35,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static core.api.ContentAPIKt.*;
|
import static core.api.ContentAPIKt.*;
|
||||||
|
import static core.api.ContentAPIKt.sendMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a familiar.
|
* Represents a familiar.
|
||||||
@@ -258,7 +259,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
if (victim == null) {
|
if (victim == null) {
|
||||||
victim = owner.getAttribute("combat-attacker");
|
victim = owner.getAttribute("combat-attacker");
|
||||||
}
|
}
|
||||||
if (combat.getVictim() != this && victim != null && !victim.isInvisible() && getProperties().isMultiZone() && owner.getProperties().isMultiZone() && isCombatFamiliar() && !isBurdenBeast() && !isPeacefulFamiliar()) {
|
if (combat.getVictim() != this && victim != null && !victim.isInvisible()
|
||||||
|
&& getProperties().isMultiZone() && owner.getProperties().isMultiZone()
|
||||||
|
&& victim.getProperties().isMultiZone() && isCombatFamiliar()
|
||||||
|
&& !isBurdenBeast() && !isPeacefulFamiliar()) { // Are you serious my brotha?
|
||||||
getProperties().getCombatPulse().attack(victim);
|
getProperties().getCombatPulse().attack(victim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,55 +278,117 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
public boolean isAttackable(Entity entity, CombatStyle style, boolean message) {
|
||||||
if (isInvalidTarget(entity, style, message)) return false;
|
if (isAttackableBy(entity, style, message)) return false;
|
||||||
return super.isAttackable(entity, style, message);
|
return super.isAttackable(entity, style, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
public boolean continueAttack(Entity target, CombatStyle style, boolean message) {
|
||||||
return !isInvalidTarget(target, style, message);
|
if (getProperties().getCombatPulse().getVictim() == target) {
|
||||||
|
return canAttackTarget(target, style, message);
|
||||||
|
}
|
||||||
|
if (target == owner) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInvalidTarget(Entity entity, CombatStyle style, boolean message) {
|
/**
|
||||||
if (entity == owner) {
|
* Checks if the familiar can attack the target (familiar as attacker).
|
||||||
|
* Called from continueAttack() when familiar initiates combat.
|
||||||
|
*
|
||||||
|
* @param target The target to attack.
|
||||||
|
* @param style The combat style.
|
||||||
|
* @param message Whether to send messages.
|
||||||
|
*
|
||||||
|
* @return true if the familiar can attack the target.
|
||||||
|
*/
|
||||||
|
private boolean canAttackTarget(Entity target, CombatStyle style, boolean message) {
|
||||||
|
if (target == owner) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (target instanceof Player) {
|
||||||
|
Player targetPlayer = (Player) target;
|
||||||
|
if (!targetPlayer.getSkullManager().isWilderness()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "You have to be in the wilderness to attack a player's familiar.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!owner.getSkullManager().isWilderness()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "This familiar's owner is not in the wilderness.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!owner.isAttackable(target, style, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!owner.getProperties().isMultiZone()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the entity can attack this familiar (familiar as target).
|
||||||
|
* Called from isAttackable() when someone attacks the familiar.
|
||||||
|
*
|
||||||
|
* @param attacker The entity trying to attack the familiar.
|
||||||
|
* @param style The combat style.
|
||||||
|
* @param message Whether to send messages.
|
||||||
|
*
|
||||||
|
* @return true if the attacker cannot attack the familiar (invalid target).
|
||||||
|
*/
|
||||||
|
private boolean isAttackableBy(Entity attacker, CombatStyle style, boolean message) {
|
||||||
|
if (attacker == owner) {
|
||||||
if (message) {
|
if (message) {
|
||||||
sendMessage(owner, "You can't just betray your own familiar like that!");
|
sendMessage(owner, "You can't just betray your own familiar like that!");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (entity instanceof Player) {
|
if (attacker instanceof Player) {
|
||||||
if (!owner.isAttackable(entity, style, message)) {
|
Player attackingPlayer = (Player) attacker;
|
||||||
return true;
|
if (!attackingPlayer.getSkullManager().isWilderness()) {
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!getProperties().isMultiZone()) {
|
|
||||||
if (entity instanceof Player && !((Player) entity).getProperties().isMultiZone()) {
|
|
||||||
if (message) {
|
if (message) {
|
||||||
sendMessage((Player) entity, "You have to be in multicombat to attack a player's familiar.");
|
sendMessage(attackingPlayer, "You have to be in the wilderness to attack a player's familiar.");
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
if (message) {
|
|
||||||
sendMessage((Player) entity, "This familiar is not in the a multicombat zone.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
if (!((Player) entity).getSkullManager().isWilderness()) {
|
|
||||||
if (message) {
|
|
||||||
sendMessage((Player) entity, "You have to be in the wilderness to attack a player's familiar.");
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!owner.getSkullManager().isWilderness()) {
|
if (!owner.getSkullManager().isWilderness()) {
|
||||||
if (message) {
|
if (message) {
|
||||||
sendMessage((Player) entity, "This familiar's owner is not in the wilderness.");
|
sendMessage(attackingPlayer, "This familiar's owner is not in the wilderness.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!owner.isAttackable(attackingPlayer, style, false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!attackingPlayer.getProperties().isMultiZone()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(attackingPlayer, "You have to be in a multicombat zone to attack a player's familiar.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!this.getProperties().isMultiZone()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(attackingPlayer, "This familiar is not in a multicombat zone.");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,6 +509,7 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a familiar hit.
|
* Sends a familiar hit.
|
||||||
|
*
|
||||||
* @param target the target.
|
* @param target the target.
|
||||||
* @param maxHit the max hit.
|
* @param maxHit the max hit.
|
||||||
* @param graphics the graphics.
|
* @param graphics the graphics.
|
||||||
@@ -459,6 +526,13 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
hit = RandomFunction.randomize(maxHit);
|
hit = RandomFunction.randomize(maxHit);
|
||||||
}
|
}
|
||||||
state.setEstimatedHit(hit);
|
state.setEstimatedHit(hit);
|
||||||
|
if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) {
|
||||||
|
state.neutralizeHits();
|
||||||
|
state.setPoisonDamage(-1);
|
||||||
|
if (state.getStyle() == CombatStyle.MAGIC) {
|
||||||
|
state.setEstimatedHit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
target.getImpactHandler().handleImpact(owner, hit, CombatStyle.MELEE, state);
|
target.getImpactHandler().handleImpact(owner, hit, CombatStyle.MELEE, state);
|
||||||
if (graphics != null) {
|
if (graphics != null) {
|
||||||
target.graphics(graphics);
|
target.graphics(graphics);
|
||||||
@@ -495,11 +569,17 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
}
|
}
|
||||||
if (target.getLocation().getDistance(getLocation()) > 8) {
|
if (target.getLocation().getDistance(getLocation()) > 8) {
|
||||||
if (message) {
|
if (message) {
|
||||||
owner.getPacketDispatch().sendMessage("That target is too far.");
|
sendMessage(owner, "That target is too far.");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, true)) {
|
if (target instanceof Player && !owner.getProperties().isMultiZone()) {
|
||||||
|
if (message) {
|
||||||
|
sendMessage(owner, "You have to be in a multicombat zone to attack another player with your familiar.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (target.getLocks().isInteractionLocked() || !target.isAttackable(this, CombatStyle.MAGIC, message)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return isCombatFamiliar();
|
return isCombatFamiliar();
|
||||||
@@ -512,8 +592,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a familiar can perform a combat special attack.
|
* Checks if a familiar can perform a combat special attack.
|
||||||
|
*
|
||||||
* @param target the target.
|
* @param target the target.
|
||||||
* @param message show message.
|
* @param message show message.
|
||||||
|
*
|
||||||
* @return {@code True} if so.
|
* @return {@code True} if so.
|
||||||
*/
|
*/
|
||||||
public boolean canCombatSpecial(Entity target, boolean message) {
|
public boolean canCombatSpecial(Entity target, boolean message) {
|
||||||
@@ -526,6 +608,10 @@ public abstract class Familiar extends NPC implements Plugin<Object> {
|
|||||||
if (getAttribute("special-delay", 0) > GameWorld.getTicks()) {
|
if (getAttribute("special-delay", 0) > GameWorld.getTicks()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// The special move probably should go through but with all harmful effects neutralized, but for now just block the damn thing
|
||||||
|
if (target instanceof NPC && ((NPC) target).getTask() != null && ((NPC) target).getTask().levelReq > owner.getSkills().getLevel(Skills.SLAYER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -450,6 +450,9 @@ public abstract class Entity extends Node {
|
|||||||
if (!entity.getZoneMonitor().continueAttack(this, style, message)) {
|
if (!entity.getZoneMonitor().continueAttack(this, style, message)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!this.getZoneMonitor().continueAttack(entity, style, message)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,11 +229,19 @@ public enum BoltEffect {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the effect can fire.
|
* Checks if the effect can fire.
|
||||||
|
*
|
||||||
* @param state the state.
|
* @param state the state.
|
||||||
|
*
|
||||||
* @return {@code True} if so.
|
* @return {@code True} if so.
|
||||||
*/
|
*/
|
||||||
public boolean canFire(BattleState state) {
|
public boolean canFire(BattleState state) {
|
||||||
return RandomFunction.random(13) == 5;
|
// Never fires on slayer monster that player lacks level requirement for. Inauthentic(?)
|
||||||
|
// and should be considered a TODO: temporary abuse prevention measure (bolt effect on slayer monsters)
|
||||||
|
boolean rollSuccess = RandomFunction.random(13) == 5;
|
||||||
|
if (!(state.getVictim() instanceof NPC)) return rollSuccess;
|
||||||
|
if (!(state.getAttacker() instanceof Player) && ((NPC) state.getVictim()).getTask() == null) return rollSuccess;
|
||||||
|
if (state.getVictim().asNpc().getTask().levelReq > state.getAttacker().asPlayer().getSkills().getLevel(Skills.SLAYER)) return false;
|
||||||
|
return rollSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -391,6 +391,17 @@ public class NPC extends Entity {
|
|||||||
behavior.beforeDamageReceived(this, entity, state);
|
behavior.beforeDamageReceived(this, entity, state);
|
||||||
if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getLevel(Skills.SLAYER)) {
|
if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getLevel(Skills.SLAYER)) {
|
||||||
state.neutralizeHits();
|
state.neutralizeHits();
|
||||||
|
state.setPoisonDamage(-1);
|
||||||
|
if (state.getStyle() == CombatStyle.MAGIC) {
|
||||||
|
state.setEstimatedHit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (task != null && entity instanceof Familiar && task.levelReq > ((Familiar) entity).getOwner().getSkills().getLevel(Skills.SLAYER)) {
|
||||||
|
state.neutralizeHits();
|
||||||
|
state.setPoisonDamage(-1);
|
||||||
|
if (state.getStyle() == CombatStyle.MAGIC) {
|
||||||
|
state.setEstimatedHit(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,8 +415,7 @@ public class NPC extends Entity {
|
|||||||
((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster.");
|
((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!behavior.canBeAttackedBy(this, entity, style, message))
|
if (!behavior.canBeAttackedBy(this, entity, style, message)) return false;
|
||||||
return false;
|
|
||||||
return super.isAttackable(entity, style, message);
|
return super.isAttackable(entity, style, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,13 +424,7 @@ public class NPC extends Entity {
|
|||||||
if (isInvisible()) {
|
if (isInvisible()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (task != null && target instanceof Player && task.levelReq > target.getSkills().getLevel(Skills.SLAYER)) {
|
return true;
|
||||||
if (message) {
|
|
||||||
((Player) target).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster.");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return behavior.canBeAttackedBy(this, target, style, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -762,7 +762,7 @@ public class Player extends Entity {
|
|||||||
return false;
|
return false;
|
||||||
if (skullManager.hasWildernessProtection())
|
if (skullManager.hasWildernessProtection())
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return super.isAttackable(entity, style, message);
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
return super.isAttackable(entity, style, message);
|
return super.isAttackable(entity, style, message);
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package core.game.system.timer.impl
|
package core.game.system.timer.impl
|
||||||
|
|
||||||
|
import content.global.skill.summoning.familiar.Familiar
|
||||||
import core.game.system.timer.*
|
import core.game.system.timer.*
|
||||||
import core.api.*
|
import core.api.*
|
||||||
import core.game.node.entity.Entity
|
import core.game.node.entity.Entity
|
||||||
import core.game.node.entity.player.Player
|
import core.game.node.entity.player.Player
|
||||||
import core.game.node.entity.combat.ImpactHandler
|
import core.game.node.entity.combat.ImpactHandler
|
||||||
|
import core.game.node.entity.npc.NPC
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
import core.game.world.repository.Repository
|
import core.game.world.repository.Repository
|
||||||
import org.json.simple.*
|
import org.json.simple.*
|
||||||
|
|
||||||
@@ -36,8 +39,21 @@ class Poison : PersistTimer (30, "poison", flags = arrayOf(TimerFlag.ClearOnDeat
|
|||||||
sendMessage(entity, "You have been poisoned.")
|
sendMessage(entity, "You have been poisoned.")
|
||||||
entity.debug("[Poison] -> Received for $severity severity.")
|
entity.debug("[Poison] -> Received for $severity severity.")
|
||||||
}
|
}
|
||||||
if (damageSource is Player)
|
if (damageSource is Player) {
|
||||||
(damageSource as? Player)?.debug("[Poison] -> Applied for $severity severity.")
|
(damageSource as? Player)?.debug("[Poison] -> Applied for $severity severity.")
|
||||||
|
// this is ass, and preventing poisoning slayer monster player lacks lv req for should be done but prob not here & not like this. same for familiars
|
||||||
|
if (entity is NPC && entity.task != null && entity.task.levelReq > damageSource.skills.getLevel(Skills.SLAYER)) {
|
||||||
|
(damageSource as? Player)?.debug("[Poison] -> Removed poison from ${entity.name} ${entity.location} due to Slayer reqs!")
|
||||||
|
removeTimer(entity, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (damageSource is Familiar) {
|
||||||
|
val owner : Player? = (damageSource as Familiar).owner
|
||||||
|
if (owner != null && entity is NPC && entity.task != null && entity.task.levelReq > owner.skills.getLevel(Skills.SLAYER)) {
|
||||||
|
owner.debug("[Poison] -> Removed poison from ${entity.name} ${entity.location} due to Slayer reqs!")
|
||||||
|
removeTimer(entity, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun run (entity: Entity) : Boolean {
|
override fun run (entity: Entity) : Boolean {
|
||||||
|
|||||||
@@ -177,14 +177,23 @@ public abstract class MapZone implements Zone {
|
|||||||
if (multi || e.isIgnoreMultiBoundaries(t) || e.getZoneMonitor().isIgnoreMultiBoundaries(t)) {
|
if (multi || e.isIgnoreMultiBoundaries(t) || e.getZoneMonitor().isIgnoreMultiBoundaries(t)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Entity attacker = e instanceof Familiar ? ((Familiar) e).getOwner() : e;
|
||||||
Entity target = t.getAttribute("combat-attacker", e);
|
Entity target = t.getAttribute("combat-attacker", e);
|
||||||
if (t.getAttribute("combat-time", -1L) > time && target != e && target.isActive()) {
|
if (target instanceof Familiar) {
|
||||||
|
target = ((Familiar) target).getOwner();
|
||||||
|
}
|
||||||
|
if (t.getAttribute("combat-time", -1L) > time && target != attacker && target.isActive()) {
|
||||||
if (message && e instanceof Player) {
|
if (message && e instanceof Player) {
|
||||||
((Player) e).getPacketDispatch().sendMessage("Someone else is already fighting this" + (t instanceof Player ? " player." : "."));
|
((Player) e).getPacketDispatch().sendMessage("Someone else is already fighting this" + (t instanceof Player ? " player." : "."));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (e.getAttribute("combat-time", -1L) > time && (target = e.getAttribute("combat-attacker", t)) != t && target.isActive()) {
|
Entity attackerOnE = e.getAttribute("combat-attacker", t);
|
||||||
|
if (attackerOnE instanceof Familiar) {
|
||||||
|
attackerOnE = ((Familiar) attackerOnE).getOwner();
|
||||||
|
}
|
||||||
|
Entity tResolved = t instanceof Familiar ? ((Familiar) t).getOwner() : t;
|
||||||
|
if (e.getAttribute("combat-time", -1L) > time && attackerOnE != tResolved && attackerOnE.isActive()) {
|
||||||
if (t.getId() == 1614 || t.getId() == 1613) {
|
if (t.getId() == 1614 || t.getId() == 1613) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,23 @@ public final class WildernessZone extends MapZone {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (e instanceof Player && target instanceof NPC && ((NPC) target).getName().contains("Revenant")) {
|
||||||
|
Player p = (Player) e;
|
||||||
|
NPC npc = (NPC) target;
|
||||||
|
int level = p.getSkullManager().getLevel();
|
||||||
|
int npcWildLevel = getWilderness(npc);
|
||||||
|
if (npcWildLevel < level) {
|
||||||
|
level = npcWildLevel;
|
||||||
|
}
|
||||||
|
int combat = p.getProperties().getCurrentCombatLevel();
|
||||||
|
int npcCombat = npc.getDefinition().getCombatLevel();
|
||||||
|
if (combat - level > npcCombat || combat + level < npcCombat) {
|
||||||
|
if (message) {
|
||||||
|
p.getPacketDispatch().sendMessage("The level difference between you and your opponent is too great.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user