001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.bukkit.util; 020 021import com.plotsquared.bukkit.player.BukkitPlayer; 022import com.plotsquared.core.configuration.Settings; 023import com.plotsquared.core.configuration.caption.TranslatableCaption; 024import com.plotsquared.core.location.Location; 025import com.plotsquared.core.permissions.Permission; 026import com.plotsquared.core.plot.Plot; 027import com.plotsquared.core.plot.PlotArea; 028import com.plotsquared.core.plot.flag.implementations.AnimalAttackFlag; 029import com.plotsquared.core.plot.flag.implementations.AnimalCapFlag; 030import com.plotsquared.core.plot.flag.implementations.DoneFlag; 031import com.plotsquared.core.plot.flag.implementations.EntityCapFlag; 032import com.plotsquared.core.plot.flag.implementations.HangingBreakFlag; 033import com.plotsquared.core.plot.flag.implementations.HostileAttackFlag; 034import com.plotsquared.core.plot.flag.implementations.HostileCapFlag; 035import com.plotsquared.core.plot.flag.implementations.MiscBreakFlag; 036import com.plotsquared.core.plot.flag.implementations.MiscCapFlag; 037import com.plotsquared.core.plot.flag.implementations.MobCapFlag; 038import com.plotsquared.core.plot.flag.implementations.PveFlag; 039import com.plotsquared.core.plot.flag.implementations.PvpFlag; 040import com.plotsquared.core.plot.flag.implementations.TamedAttackFlag; 041import com.plotsquared.core.plot.flag.implementations.VehicleCapFlag; 042import com.plotsquared.core.util.EntityUtil; 043import com.plotsquared.core.util.entity.EntityCategories; 044import com.sk89q.worldedit.bukkit.BukkitAdapter; 045import net.kyori.adventure.text.minimessage.Template; 046import org.bukkit.entity.Arrow; 047import org.bukkit.entity.Creature; 048import org.bukkit.entity.Entity; 049import org.bukkit.entity.EntityType; 050import org.bukkit.entity.Player; 051import org.bukkit.entity.Projectile; 052import org.bukkit.event.entity.EntityDamageEvent; 053import org.bukkit.projectiles.BlockProjectileSource; 054import org.bukkit.projectiles.ProjectileSource; 055 056import java.util.Objects; 057 058public class BukkitEntityUtil { 059 060 public static final com.sk89q.worldedit.world.entity.EntityType FAKE_ENTITY_TYPE = 061 new com.sk89q.worldedit.world.entity.EntityType("plotsquared:fake"); 062 063 public static boolean entityDamage(Entity damager, Entity victim) { 064 return entityDamage(damager, victim, null); 065 } 066 067 public static boolean entityDamage(Entity damager, Entity victim, EntityDamageEvent.DamageCause cause) { 068 Location dloc = BukkitUtil.adapt(damager.getLocation()); 069 Location vloc = BukkitUtil.adapt(victim.getLocation()); 070 PlotArea dArea = dloc.getPlotArea(); 071 PlotArea vArea; 072 if (dArea != null && dArea.contains(vloc.getX(), vloc.getZ())) { 073 vArea = dArea; 074 } else { 075 vArea = vloc.getPlotArea(); 076 } 077 if (dArea == null && vArea == null) { 078 return true; 079 } 080 081 Plot dplot; 082 if (dArea != null) { 083 dplot = dArea.getPlot(dloc); 084 } else { 085 dplot = null; 086 } 087 Plot vplot; 088 if (vArea != null) { 089 vplot = vArea.getPlot(vloc); 090 } else { 091 vplot = null; 092 } 093 094 Plot plot; 095 String stub; 096 boolean isPlot = true; 097 if (dplot == null && vplot == null) { 098 if (dArea == null) { 099 return true; 100 } 101 plot = null; 102 stub = "road"; 103 isPlot = false; 104 } else { 105 // Prioritize plots for close to seamless pvp zones 106 if (victim.getTicksLived() > damager.getTicksLived()) { 107 if (dplot == null || !(victim instanceof Player)) { 108 if (vplot == null) { 109 plot = dplot; 110 } else { 111 plot = vplot; 112 } 113 } else { 114 plot = dplot; 115 } 116 } else if (dplot == null || !(victim instanceof Player)) { 117 if (vplot == null) { 118 plot = dplot; 119 } else { 120 plot = vplot; 121 } 122 } else if (vplot == null) { 123 plot = dplot; 124 } else { 125 plot = vplot; 126 } 127 if (plot.hasOwner()) { 128 stub = "other"; 129 } else { 130 stub = "unowned"; 131 } 132 } 133 boolean roadFlags = vArea != null ? vArea.isRoadFlags() : dArea.isRoadFlags(); 134 PlotArea area = vArea != null ? vArea : dArea; 135 136 Player player; 137 if (damager instanceof Player) { // attacker is player 138 player = (Player) damager; 139 } else if (damager instanceof Projectile projectile) { 140 ProjectileSource shooter = projectile.getShooter(); 141 if (shooter instanceof Player) { // shooter is player 142 player = (Player) shooter; 143 } else { // shooter is not player 144 if (shooter instanceof BlockProjectileSource) { 145 Location sLoc = BukkitUtil 146 .adapt(((BlockProjectileSource) shooter).getBlock().getLocation()); 147 dplot = dArea.getPlot(sLoc); 148 } 149 player = null; 150 } 151 } else { // Attacker is not player 152 player = null; 153 } 154 if (player != null) { 155 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 156 157 final com.sk89q.worldedit.world.entity.EntityType entityType; 158 159 // Create a fake entity type if the type does not have a name 160 if (victim.getType().getName() == null) { 161 entityType = FAKE_ENTITY_TYPE; 162 } else { 163 entityType = BukkitAdapter.adapt(victim.getType()); 164 } 165 166 if (EntityCategories.HANGING.contains(entityType)) { // hanging 167 if (plot != null && (plot.getFlag(HangingBreakFlag.class) || plot 168 .isAdded(plotPlayer.getUUID()))) { 169 if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) { 170 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 171 plotPlayer.sendMessage( 172 TranslatableCaption.of("done.building_restricted") 173 ); 174 return false; 175 } 176 } 177 return true; 178 } 179 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) { 180 plotPlayer.sendMessage( 181 TranslatableCaption.of("permission.no_permission_event"), 182 Template.of("node", Permission.PERMISSION_ADMIN_DESTROY + "." + stub) 183 ); 184 return false; 185 } 186 } else if (victim.getType() == EntityType.ARMOR_STAND) { 187 if (plot != null && (plot.getFlag(MiscBreakFlag.class) || plot 188 .isAdded(plotPlayer.getUUID()))) { 189 return true; 190 } 191 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) { 192 plotPlayer.sendMessage( 193 TranslatableCaption.of("permission.no_permission_event"), 194 Template.of("node", Permission.PERMISSION_ADMIN_DESTROY + "." + stub) 195 ); 196 if (plot != null) { 197 plot.debug(player.getName() 198 + " could not break armor stand because misc-break = false"); 199 } 200 return false; 201 } 202 } else if (EntityCategories.HOSTILE.contains(entityType)) { 203 if (isPlot) { 204 if (plot.getFlag(HostileAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 205 .isAdded(plotPlayer.getUUID())) { 206 return true; 207 } 208 } else if (roadFlags && (area.getRoadFlag(HostileAttackFlag.class) || area 209 .getFlag(PveFlag.class))) { 210 return true; 211 } 212 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 213 plotPlayer.sendMessage( 214 TranslatableCaption.of("permission.no_permission_event"), 215 Template.of("node", Permission.PERMISSION_ADMIN_PVE + "." + stub) 216 ); 217 if (plot != null) { 218 plot.debug(player.getName() + " could not attack " + entityType 219 + " because pve = false OR hostile-attack = false"); 220 } 221 return false; 222 } 223 } else if (EntityCategories.TAMEABLE.contains(entityType)) { // victim is tameable 224 if (isPlot) { 225 if (plot.getFlag(TamedAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 226 .isAdded(plotPlayer.getUUID())) { 227 return true; 228 } 229 } else if (roadFlags && (area.getRoadFlag(TamedAttackFlag.class) || area 230 .getFlag(PveFlag.class))) { 231 return true; 232 } 233 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 234 plotPlayer.sendMessage( 235 TranslatableCaption.of("permission.no_permission_event"), 236 Template.of("node", Permission.PERMISSION_ADMIN_PVE + "." + stub) 237 ); 238 if (plot != null) { 239 plot.debug(player.getName() + " could not attack " + entityType 240 + " because pve = false OR tamed-attack = false"); 241 } 242 return false; 243 } 244 } else if (EntityCategories.PLAYER.contains(entityType)) { 245 if (isPlot) { 246 if (!plot.getFlag(PvpFlag.class) && !plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVP + "." + stub)) { 247 plotPlayer.sendMessage( 248 TranslatableCaption.of("permission.no_permission_event"), 249 Template.of("node", Permission.PERMISSION_ADMIN_PVP + "." + stub) 250 ); 251 plot.debug(player.getName() + " could not attack " + entityType 252 + " because pve = false"); 253 return false; 254 } else { 255 return true; 256 } 257 } else if (roadFlags && area.getRoadFlag(PvpFlag.class)) { 258 return true; 259 } 260 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVP + "." + stub)) { 261 plotPlayer.sendMessage( 262 TranslatableCaption.of("permission.no_permission_event"), 263 Template.of("node", Permission.PERMISSION_ADMIN_PVP + "." + stub) 264 ); 265 return false; 266 } 267 } else if (EntityCategories.ANIMAL.contains(entityType)) { // victim is animal 268 if (isPlot) { 269 if (plot.getFlag(AnimalAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 270 .isAdded(plotPlayer.getUUID())) { 271 return true; 272 } 273 } else if (roadFlags && (area.getRoadFlag(AnimalAttackFlag.class) || area 274 .getFlag(PveFlag.class))) { 275 return true; 276 } 277 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 278 plotPlayer.sendMessage( 279 TranslatableCaption.of("permission.no_permission_event"), 280 Template.of("node", Permission.PERMISSION_ADMIN_PVE + "." + stub) 281 ); 282 if (plot != null) { 283 plot.debug(player.getName() + " could not attack " + entityType 284 + " because pve = false OR animal-attack = false"); 285 } 286 return false; 287 } 288 } else if (EntityCategories.VEHICLE 289 .contains(entityType)) { // Vehicles are managed in vehicle destroy event 290 return true; 291 } else { // victim is something else 292 if (isPlot) { 293 if (plot.getFlag(PveFlag.class) || plot.isAdded(plotPlayer.getUUID())) { 294 return true; 295 } 296 } else if (roadFlags && area.getRoadFlag(PveFlag.class)) { 297 return true; 298 } 299 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 300 plotPlayer.sendMessage( 301 TranslatableCaption.of("permission.no_permission_event"), 302 Template.of("node", Permission.PERMISSION_ADMIN_PVE + "." + stub) 303 ); 304 if (plot != null) { 305 plot.debug(player.getName() + " could not attack " + entityType 306 + " because pve = false"); 307 } 308 return false; 309 } 310 } 311 return true; 312 } else if (dplot != null && (!dplot.equals(vplot) || Objects 313 .equals(dplot.getOwnerAbs(), vplot.getOwnerAbs()))) { 314 return vplot != null && vplot.getFlag(PveFlag.class); 315 } 316 //disable the firework damage. too much of a headache to support at the moment. 317 if (vplot != null) { 318 if (EntityDamageEvent.DamageCause.ENTITY_EXPLOSION == cause 319 && damager.getType() == EntityType.FIREWORK) { 320 return false; 321 } 322 } 323 if (vplot == null && roadFlags && area.getRoadFlag(PveFlag.class)) { 324 return true; 325 } 326 return ((vplot != null && vplot.getFlag(PveFlag.class)) || !(damager instanceof Arrow 327 && !(victim instanceof Creature))); 328 } 329 330 public static boolean checkEntity(Entity entity, Plot plot) { 331 if (plot == null || !plot.hasOwner() || plot.getFlags().isEmpty() && plot.getArea() 332 .getFlagContainer().getFlagMap().isEmpty()) { 333 return false; 334 } 335 336 final com.sk89q.worldedit.world.entity.EntityType entityType = 337 BukkitAdapter.adapt(entity.getType()); 338 339 if (EntityCategories.PLAYER.contains(entityType)) { 340 return false; 341 } 342 343 if (EntityCategories.PROJECTILE.contains(entityType) || EntityCategories.OTHER 344 .contains(entityType) || EntityCategories.HANGING.contains(entityType)) { 345 return EntityUtil.checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, 346 MiscCapFlag.MISC_CAP_UNLIMITED 347 ); 348 } 349 350 // Has to go go before vehicle as horses are both 351 // animals and vehicles 352 if (EntityCategories.ANIMAL.contains(entityType) || EntityCategories.VILLAGER 353 .contains(entityType) || EntityCategories.TAMEABLE.contains(entityType)) { 354 return EntityUtil 355 .checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, MobCapFlag.MOB_CAP_UNLIMITED, 356 AnimalCapFlag.ANIMAL_CAP_UNLIMITED 357 ); 358 } 359 360 if (EntityCategories.HOSTILE.contains(entityType)) { 361 return EntityUtil 362 .checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, MobCapFlag.MOB_CAP_UNLIMITED, 363 HostileCapFlag.HOSTILE_CAP_UNLIMITED 364 ); 365 } 366 367 if (EntityCategories.VEHICLE.contains(entityType)) { 368 return EntityUtil.checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, 369 VehicleCapFlag.VEHICLE_CAP_UNLIMITED 370 ); 371 } 372 373 return EntityUtil.checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED); 374 } 375 376}