Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public record CareSettings(
double minutesUntilSick,
boolean decayWhileStored,
boolean deathOnNeglect,
boolean invulnerable,
double playSeconds,
double playMoodGain,
double playEnergyCost,
Expand Down Expand Up @@ -40,6 +41,7 @@ public static CareSettings defaults() {
3,
false,
false,
false,
6,
22,
3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public static CompanionConfig load(JavaPlugin plugin, FileConfiguration config)
num(logger, care, "minutes-until-sick", defaults.minutesUntilSick()),
care != null && care.contains("decay-while-stored") ? care.getBoolean("decay-while-stored") : defaults.decayWhileStored(),
care != null && care.contains("death-on-neglect") ? care.getBoolean("death-on-neglect") : defaults.deathOnNeglect(),
care != null && care.contains("invulnerable") ? care.getBoolean("invulnerable") : defaults.invulnerable(),
num(logger, care, "play-seconds", defaults.playSeconds()),
num(logger, care, "play-mood-gain", defaults.playMoodGain()),
num(logger, care, "play-energy-cost", defaults.playEnergyCost()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,18 @@ public void onEnvironmentalDamage(EntityDamageEvent event) {
&& runtime.byEntity(event.getEntity()) != null) event.setCancelled(true);
}

/** Owners cannot hurt their own pets: following pets end up inside area skills such as dashes. */
/**
* Owners cannot hurt their own pets: following pets end up inside area skills such as dashes.
* With care.invulnerable nothing can, since a pet's death deletes it.
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onOwnerDamage(EntityDamageByEntityEvent event) {
public void onProtectedDamage(EntityDamageEvent event) {
Pet pet = runtime.byEntity(event.getEntity());
if (pet != null && pet.ownerId().equals(responsiblePlayer(event))) event.setCancelled(true);
if (pet == null) return;
if (runtime.config().care().invulnerable()
|| event instanceof EntityDamageByEntityEvent attack && pet.ownerId().equals(responsiblePlayer(attack))) {
event.setCancelled(true);
}
}

private static UUID responsiblePlayer(EntityDamageByEntityEvent event) {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ care:
minutes-until-sick: 60 # Additional minutes with critical needs after the unwell threshold before sickness.
decay-while-stored: false # true advances care in the Pet House while the owner is online.
death-on-neglect: false # true kills at zero health from illness; false leaves the pet weakened.
invulnerable: false # true cancels all damage to pets; false only blocks damage from their owner.
play-seconds: 6 # Loaded duration in seconds; currently unused by runtime play.
play-mood-gain: 22 # Mood points gained on play completion or when comforting an ill pet.
play-energy-cost: 1 # Energy points spent on play completion.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ void neglectCanWeakenOrKill() {
care.minutesUntilSick(),
care.decayWhileStored(),
true,
care.invulnerable(),
care.playSeconds(),
care.playMoodGain(),
care.playEnergyCost(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
assertNull(runtime.store().kennelOwner(PetStore.kennelKey(world.getName(), 3, 64, 0)));
}
private EntityDamageByEntityEvent attack(Entity attacker, Entity victim, double damage) {
return new EntityDamageByEntityEvent(attacker, victim, EntityDamageEvent.DamageCause.ENTITY_ATTACK, damage);

Check warning on line 168 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageByEntityEvent(org.bukkit.entity.Entity,org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageByEntityEvent has been deprecated and marked for removal
}

@Test void previouslyCancelledBlockClickCannotPlaceOrConsumeKennel() {
Expand Down Expand Up @@ -324,7 +324,7 @@
}

@Test void environmentalDamageHurtsWithoutAMoodPenaltyAndUnrelatedMobsStayUntouched() {
fire(new EntityDamageEvent(body, EntityDamageEvent.DamageCause.FALL, 2));

Check warning on line 327 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageEvent(org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageEvent has been deprecated and marked for removal
assertEquals(List.of(body.getUniqueId() + ":HURT"), animations);
assertEquals(80, pet.need(Need.MOOD));
animations.clear();
Expand All @@ -342,7 +342,7 @@
(unused, event) -> observed.add(((EntityDamageEvent) event).isCancelled()), runtime.plugin());
double health = body.getHealth();
for (int tick = 0; tick < 40; tick++) {
var event = fire(new EntityDamageEvent(body, cause, 1_000));

Check warning on line 345 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageEvent(org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageEvent has been deprecated and marked for removal
assertTrue(event.isCancelled());
if (!event.isCancelled()) body.setHealth(Math.max(0, body.getHealth() - event.getFinalDamage()));
}
Expand All @@ -350,7 +350,7 @@
assertEquals(health, body.getHealth()); assertFalse(body.isDead()); assertFalse(pet.dead());
assertSame(pet, runtime.store().get(pet.id())); assertFalse(runtime.store().isDeleted(pet.id()));
assertTrue(animations.isEmpty()); assertTrue(cancelledAnimations.isEmpty()); assertTrue(sounds.isEmpty());
var ordinary = fire(new EntityDamageEvent(wolf(owner.getLocation()), cause, 1));

Check warning on line 353 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageEvent(org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageEvent has been deprecated and marked for removal
assertFalse(ordinary.isCancelled()); assertFalse(observed.getLast());
}

Expand All @@ -364,7 +364,7 @@
Snowball effect = world.spawn(owner.getEyeLocation(), Snowball.class);
var skill = DamageSource.builder(DamageType.MAGIC).withCausingEntity(owner).withDirectEntity(effect).build();
for (var event : List.of(attack(owner, body, 10), attack(shot, body, 10),
new EntityDamageByEntityEvent(effect, body, EntityDamageEvent.DamageCause.MAGIC, skill, 10))) {

Check warning on line 367 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageByEntityEvent(org.bukkit.entity.Entity,org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,org.bukkit.damage.DamageSource,double) in org.bukkit.event.entity.EntityDamageByEntityEvent has been deprecated and marked for removal
assertTrue(fire(event).isCancelled());
}
assertEquals(List.of(true, true, true), observed);
Expand All @@ -385,6 +385,36 @@
assertFalse(fire(attack(owner, wolf(owner.getLocation()), 2)).isCancelled());
}

@Test void invulnerablePetsTakeNoDamageFromAnySourceBeforeOtherPlugins() throws Exception {
var yaml = new YamlConfiguration();
yaml.loadFromString("""
items: {kennel: BARREL, kennel-block: BARREL, toys: [STICK], treats: [COD]}
care: {invulnerable: true}
pets:
wolf: {entity: WOLF, egg: WOLF_SPAWN_EGG, default-tricks: []}
""");
runtime.config(CompanionConfig.load(runtime.plugin(), yaml));
var observed = new ArrayList<Boolean>();
server.getPluginManager().registerEvent(EntityDamageEvent.class, new Listener() { }, EventPriority.LOW,
(unused, event) -> observed.add(((EntityDamageEvent) event).isCancelled()), runtime.plugin());
var stranger = server.addPlayer();
Snowball shot = world.spawn(stranger.getEyeLocation(), Snowball.class);
shot.setShooter(stranger);
List<EntityDamageEvent> events = new ArrayList<>(List.of(attack(stranger, body, 1_000), attack(shot, body, 1_000),
attack(wolf(owner.getLocation()), body, 1_000)));
for (var cause : List.of(EntityDamageEvent.DamageCause.FALL, EntityDamageEvent.DamageCause.LAVA,
EntityDamageEvent.DamageCause.FIRE_TICK, EntityDamageEvent.DamageCause.VOID, EntityDamageEvent.DamageCause.KILL)) {
events.add(new EntityDamageEvent(body, cause, 1_000));

Check warning on line 407 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageEvent(org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageEvent has been deprecated and marked for removal
}
for (var event : events) assertTrue(fire(event).isCancelled(), event.getCause().name());
assertTrue(observed.stream().allMatch(Boolean::booleanValue));
assertEquals(events.size(), observed.size());
assertEquals(80, pet.need(Need.MOOD));
assertTrue(animations.isEmpty());
assertFalse(fire(attack(stranger, wolf(owner.getLocation()), 2)).isCancelled());
assertFalse(fire(new EntityDamageEvent(wolf(owner.getLocation()), EntityDamageEvent.DamageCause.FALL, 2)).isCancelled());

Check warning on line 415 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

EntityDamageEvent(org.bukkit.entity.Entity,org.bukkit.event.entity.EntityDamageEvent.DamageCause,double) in org.bukkit.event.entity.EntityDamageEvent has been deprecated and marked for removal
}

@Test void toyProjectileDamageIsCancelledBeforeMoodOrVisualEffects() {
Snowball toy = world.spawn(owner.getEyeLocation(), Snowball.class);
toy.getPersistentDataContainer().set(runtime.toyKey(), PersistentDataType.STRING, UUID.randomUUID().toString());
Expand All @@ -403,7 +433,7 @@
Snowball ball = world.getEntities().stream().filter(Snowball.class::isInstance).map(Snowball.class::cast).findFirst().orElseThrow();
assertNotNull(pet.fetch());
ball.teleport(new Location(world, 4, 64, 0));
fire(new ProjectileHitEvent(ball));

Check warning on line 436 in src/test/java/net/tfminecraft/companionpets/listen/PetListenerCoverageTest.java

View workflow job for this annotation

GitHub Actions / build

ProjectileHitEvent(org.bukkit.entity.Projectile) in org.bukkit.event.entity.ProjectileHitEvent has been deprecated and marked for removal
assertFalse(ball.isValid());
assertEquals(net.tfminecraft.companionpets.play.FetchPhase.GROUND, pet.fetch().phase());
Item item = (Item) Bukkit.getEntity(pet.fetch().itemId());
Expand Down
Loading