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.VehicleBreakFlag; 042import com.plotsquared.core.plot.flag.implementations.VehicleCapFlag; 043import com.plotsquared.core.util.EntityUtil; 044import com.plotsquared.core.util.PlotFlagUtil; 045import com.plotsquared.core.util.entity.EntityCategories; 046import com.sk89q.worldedit.bukkit.BukkitAdapter; 047import net.kyori.adventure.text.Component; 048import net.kyori.adventure.text.minimessage.tag.Tag; 049import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 050import org.bukkit.entity.Arrow; 051import org.bukkit.entity.Creature; 052import org.bukkit.entity.Entity; 053import org.bukkit.entity.EntityType; 054import org.bukkit.entity.Firework; 055import org.bukkit.entity.Player; 056import org.bukkit.entity.Projectile; 057import org.bukkit.event.entity.EntityDamageEvent; 058import org.bukkit.projectiles.BlockProjectileSource; 059import org.bukkit.projectiles.ProjectileSource; 060 061import java.util.Objects; 062 063public class BukkitEntityUtil { 064 065 public static final com.sk89q.worldedit.world.entity.EntityType FAKE_ENTITY_TYPE = 066 new com.sk89q.worldedit.world.entity.EntityType("plotsquared:fake"); 067 068 public static boolean entityDamage(Entity damager, Entity victim) { 069 return entityDamage(damager, victim, null); 070 } 071 072 public static boolean entityDamage(Entity damager, Entity victim, EntityDamageEvent.DamageCause cause) { 073 Location dloc = BukkitUtil.adapt(damager.getLocation()); 074 Location vloc = BukkitUtil.adapt(victim.getLocation()); 075 PlotArea dArea = dloc.getPlotArea(); 076 PlotArea vArea; 077 if (dArea != null && dArea.contains(vloc.getX(), vloc.getZ())) { 078 vArea = dArea; 079 } else { 080 vArea = vloc.getPlotArea(); 081 } 082 if (dArea == null && vArea == null) { 083 return true; 084 } 085 086 Plot dplot; 087 if (dArea != null) { 088 dplot = dArea.getPlot(dloc); 089 } else { 090 dplot = null; 091 } 092 Plot vplot; 093 if (vArea != null) { 094 vplot = vArea.getPlot(vloc); 095 } else { 096 vplot = null; 097 } 098 099 Plot plot; 100 String stub; 101 boolean isPlot = true; 102 if (dplot == null && vplot == null) { 103 if (dArea == null) { 104 return true; 105 } 106 plot = null; 107 stub = "road"; 108 isPlot = false; 109 } else { 110 // Prioritize plots for close to seamless pvp zones 111 if (victim.getTicksLived() > damager.getTicksLived()) { 112 if (dplot == null || !(victim instanceof Player)) { 113 if (vplot == null) { 114 plot = dplot; 115 } else { 116 plot = vplot; 117 } 118 } else { 119 plot = dplot; 120 } 121 } else if (dplot == null || !(victim instanceof Player)) { 122 if (vplot == null) { 123 plot = dplot; 124 } else { 125 plot = vplot; 126 } 127 } else if (vplot == null) { 128 plot = dplot; 129 } else { 130 plot = vplot; 131 } 132 if (plot.hasOwner()) { 133 stub = "other"; 134 } else { 135 stub = "unowned"; 136 } 137 } 138 boolean roadFlags = vArea != null ? vArea.isRoadFlags() : dArea.isRoadFlags(); 139 PlotArea area = vArea != null ? vArea : dArea; 140 141 Player player; 142 if (damager instanceof Player) { // attacker is player 143 player = (Player) damager; 144 } else if (damager instanceof Projectile projectile) { 145 ProjectileSource shooter = projectile.getShooter(); 146 if (shooter instanceof Player) { // shooter is player 147 player = (Player) shooter; 148 } else { // shooter is not player 149 if (shooter instanceof BlockProjectileSource) { 150 Location sLoc = BukkitUtil 151 .adapt(((BlockProjectileSource) shooter).getBlock().getLocation()); 152 dplot = dArea.getPlot(sLoc); 153 } 154 player = null; 155 } 156 } else { // Attacker is not player 157 player = null; 158 } 159 if (player != null) { 160 BukkitPlayer plotPlayer = BukkitUtil.adapt(player); 161 162 final com.sk89q.worldedit.world.entity.EntityType entityType; 163 164 // Create a fake entity type if the type does not have a name 165 if (victim.getType().getName() == null) { 166 entityType = FAKE_ENTITY_TYPE; 167 } else { 168 entityType = BukkitAdapter.adapt(victim.getType()); 169 } 170 171 if (EntityCategories.HANGING.contains(entityType)) { // hanging 172 if (plot != null && (plot.getFlag(HangingBreakFlag.class) || plot 173 .isAdded(plotPlayer.getUUID()))) { 174 if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) { 175 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) { 176 plotPlayer.sendMessage( 177 TranslatableCaption.of("done.building_restricted") 178 ); 179 return false; 180 } 181 } 182 return true; 183 } 184 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) { 185 plotPlayer.sendMessage( 186 TranslatableCaption.of("permission.no_permission_event"), 187 TagResolver.resolver( 188 "node", 189 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) 190 ) 191 ); 192 return false; 193 } 194 } else if (victim.getType() == EntityType.ARMOR_STAND) { 195 if (plot != null && (plot.getFlag(MiscBreakFlag.class) || plot 196 .isAdded(plotPlayer.getUUID()))) { 197 return true; 198 } 199 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) { 200 plotPlayer.sendMessage( 201 TranslatableCaption.of("permission.no_permission_event"), 202 TagResolver.resolver( 203 "node", 204 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_DESTROY + "." + stub)) 205 ) 206 ); 207 if (plot != null) { 208 plot.debug(player.getName() 209 + " could not break armor stand because misc-break = false"); 210 } 211 return false; 212 } 213 } else if (EntityCategories.HOSTILE.contains(entityType)) { 214 if (isPlot) { 215 if (plot.getFlag(HostileAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 216 .isAdded(plotPlayer.getUUID())) { 217 return true; 218 } 219 } else if (roadFlags && (area.getRoadFlag(HostileAttackFlag.class) || area 220 .getFlag(PveFlag.class))) { 221 return true; 222 } 223 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 224 plotPlayer.sendMessage( 225 TranslatableCaption.of("permission.no_permission_event"), 226 TagResolver.resolver( 227 "node", 228 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVE + "." + stub)) 229 ) 230 ); 231 if (plot != null) { 232 plot.debug(player.getName() + " could not attack " + entityType 233 + " because pve = false OR hostile-attack = false"); 234 } 235 return false; 236 } 237 } else if (EntityCategories.TAMEABLE.contains(entityType)) { // victim is tameable 238 if (isPlot) { 239 if (plot.getFlag(TamedAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 240 .isAdded(plotPlayer.getUUID())) { 241 return true; 242 } 243 } else if (roadFlags && (area.getRoadFlag(TamedAttackFlag.class) || area 244 .getFlag(PveFlag.class))) { 245 return true; 246 } 247 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 248 plotPlayer.sendMessage( 249 TranslatableCaption.of("permission.no_permission_event"), 250 TagResolver.resolver( 251 "node", 252 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVE + "." + stub)) 253 ) 254 ); 255 if (plot != null) { 256 plot.debug(player.getName() + " could not attack " + entityType 257 + " because pve = false OR tamed-attack = false"); 258 } 259 return false; 260 } 261 } else if (EntityCategories.PLAYER.contains(entityType)) { 262 if (isPlot) { 263 if (!plot.getFlag(PvpFlag.class) && !plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVP + "." + stub)) { 264 plotPlayer.sendMessage( 265 TranslatableCaption.of("permission.no_permission_event"), 266 TagResolver.resolver( 267 "node", 268 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVP + "." + stub)) 269 ) 270 ); 271 plot.debug(player.getName() + " could not attack " + entityType 272 + " because pve = false"); 273 return false; 274 } else { 275 return true; 276 } 277 } else if (roadFlags && area.getRoadFlag(PvpFlag.class)) { 278 return true; 279 } 280 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVP + "." + stub)) { 281 plotPlayer.sendMessage( 282 TranslatableCaption.of("permission.no_permission_event"), 283 TagResolver.resolver( 284 "node", 285 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVP + "." + stub)) 286 ) 287 ); 288 return false; 289 } 290 } else if (EntityCategories.ANIMAL.contains(entityType)) { // victim is animal 291 if (isPlot) { 292 if (plot.getFlag(AnimalAttackFlag.class) || plot.getFlag(PveFlag.class) || plot 293 .isAdded(plotPlayer.getUUID())) { 294 return true; 295 } 296 } else if (roadFlags && (area.getRoadFlag(AnimalAttackFlag.class) || area 297 .getFlag(PveFlag.class))) { 298 return true; 299 } 300 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 301 plotPlayer.sendMessage( 302 TranslatableCaption.of("permission.no_permission_event"), 303 TagResolver.resolver( 304 "node", 305 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVE + "." + stub)) 306 ) 307 ); 308 if (plot != null) { 309 plot.debug(player.getName() + " could not attack " + entityType 310 + " because pve = false OR animal-attack = false"); 311 } 312 return false; 313 } 314 } else if (EntityCategories.VEHICLE 315 .contains(entityType)) { // Vehicles are managed in vehicle destroy event 316 // Fire damage (e.g. from Fire Aspect swords) needs to be checked here as it bypasses VehicleDestroyEvent 317 if (cause == EntityDamageEvent.DamageCause.FIRE_TICK) { 318 if (isPlot) { 319 if (!plot.hasOwner()) { 320 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED)) { 321 plotPlayer.sendMessage( 322 TranslatableCaption.of("permission.no_permission_event"), 323 TagResolver.resolver( 324 "node", 325 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED) 326 ) 327 ); 328 return false; 329 } 330 return true; 331 } 332 if (plot.getFlag(VehicleBreakFlag.class) || plot.isAdded(plotPlayer.getUUID())) { 333 return true; 334 } 335 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER)) { 336 plotPlayer.sendMessage( 337 TranslatableCaption.of("permission.no_permission_event"), 338 TagResolver.resolver( 339 "node", 340 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER) 341 ) 342 ); 343 plot.debug(player.getName() + " could not set vehicle on fire because vehicle-break = false"); 344 return false; 345 } 346 } else { 347 // Road 348 if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, VehicleBreakFlag.class, true) 349 && !plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD)) { 350 plotPlayer.sendMessage( 351 TranslatableCaption.of("permission.no_permission_event"), 352 TagResolver.resolver( 353 "node", 354 Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD) 355 ) 356 ); 357 return false; 358 } 359 } 360 } 361 return true; 362 } else { // victim is something else 363 if (isPlot) { 364 if (plot.getFlag(PveFlag.class) || plot.isAdded(plotPlayer.getUUID())) { 365 return true; 366 } 367 } else if (roadFlags && area.getRoadFlag(PveFlag.class)) { 368 return true; 369 } 370 if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_PVE + "." + stub)) { 371 plotPlayer.sendMessage( 372 TranslatableCaption.of("permission.no_permission_event"), 373 TagResolver.resolver( 374 "node", 375 Tag.inserting(Component.text(Permission.PERMISSION_ADMIN_PVE + "." + stub)) 376 ) 377 ); 378 if (plot != null) { 379 plot.debug(player.getName() + " could not attack " + entityType 380 + " because pve = false"); 381 } 382 return false; 383 } 384 } 385 return true; 386 } else if (dplot != null && (!dplot.equals(vplot) || Objects 387 .equals(dplot.getOwnerAbs(), vplot.getOwnerAbs()))) { 388 return vplot != null && vplot.getFlag(PveFlag.class); 389 } 390 //disable the firework damage. too much of a headache to support at the moment. 391 if (vplot != null) { 392 if (EntityDamageEvent.DamageCause.ENTITY_EXPLOSION == cause && damager instanceof Firework) { 393 return false; 394 } 395 } 396 if (vplot == null && roadFlags && area.getRoadFlag(PveFlag.class)) { 397 return true; 398 } 399 return ((vplot != null && vplot.getFlag(PveFlag.class)) || !(damager instanceof Arrow 400 && !(victim instanceof Creature))); 401 } 402 403 public static boolean checkEntity(Entity entity, Plot plot) { 404 return checkEntity(entity.getType(), plot); 405 } 406 407 public static boolean checkEntity(EntityType type, Plot plot) { 408 if (plot == null || !plot.hasOwner() || plot.getFlags().isEmpty() && plot.getArea() 409 .getFlagContainer().getFlagMap().isEmpty()) { 410 return false; 411 } 412 413 final com.sk89q.worldedit.world.entity.EntityType entityType = 414 BukkitAdapter.adapt(type); 415 416 if (EntityCategories.PLAYER.contains(entityType)) { 417 return false; 418 } 419 420 if (EntityCategories.PROJECTILE.contains(entityType) || EntityCategories.OTHER 421 .contains(entityType) || EntityCategories.HANGING.contains(entityType)) { 422 return EntityUtil.checkEntity( 423 plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, 424 MiscCapFlag.MISC_CAP_UNLIMITED 425 ); 426 } 427 428 // Has to go go before vehicle as horses are both 429 // animals and vehicles 430 if (EntityCategories.ANIMAL.contains(entityType) || EntityCategories.VILLAGER 431 .contains(entityType) || EntityCategories.TAMEABLE.contains(entityType)) { 432 return EntityUtil 433 .checkEntity( 434 plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, MobCapFlag.MOB_CAP_UNLIMITED, 435 AnimalCapFlag.ANIMAL_CAP_UNLIMITED 436 ); 437 } 438 439 if (EntityCategories.HOSTILE.contains(entityType)) { 440 return EntityUtil 441 .checkEntity( 442 plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, MobCapFlag.MOB_CAP_UNLIMITED, 443 HostileCapFlag.HOSTILE_CAP_UNLIMITED 444 ); 445 } 446 447 if (EntityCategories.VEHICLE.contains(entityType)) { 448 return EntityUtil.checkEntity( 449 plot, EntityCapFlag.ENTITY_CAP_UNLIMITED, 450 VehicleCapFlag.VEHICLE_CAP_UNLIMITED 451 ); 452 } 453 454 return EntityUtil.checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED); 455 } 456 457}