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}