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