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