diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index e646df83..061f00f0 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -79,6 +79,11 @@ at the same time. Triggered right after a lightning strikes. Lightning entity as well as potential horseman trap would already be spawned at that point. `mode` is `true` if the lightning did cause a trap to spawn. +### `__on_explosion(pos, power, source, create_fire, blocks, entities)` +Triggered right before an explosion blows up and any changes to the blocks are done, but the decision to blow up is already made and entities are already affected. +The parameter `blocks` contains the list of blocks that will blow up (empty if `explosionNoBlockDamage` is set to `true`). +The parameter `entities` contains the list of entities that have been affected by the explosion. + ### `__on_carpet_rule_changes(rule, new_value)` Triggered when a carpet mod rule is changed. It includes extension rules, not using default `/carpet` command, which will then be namespaced as `namespace:rule`. @@ -107,7 +112,7 @@ Example events that may cause it to happen is releasing a bow. The event is trig the request, however the `item_tuple` is provided representing the item that the player started with. You can use that and compare with the currently held item for a delta. -### `__on_player_finishes_using_item(player, item_tuple, hand))` +### `__on_player_finishes_using_item(player, item_tuple, hand)` Player using of an item is done. This is controlled server side and is responsible for such events as finishing eating. The event is triggered after confirming that the action is valid, and sending the feedback back to the client, but before triggering it and its effects in game. diff --git a/docs/scarpet/resources/editors/idea/4.txt b/docs/scarpet/resources/editors/idea/4.txt index a921bb46..e11b1bd5 100644 --- a/docs/scarpet/resources/editors/idea/4.txt +++ b/docs/scarpet/resources/editors/idea/4.txt @@ -42,4 +42,5 @@ __on_player_deals_damage(player, amount, entity) -> __on_player_dies(player) -> __on_player_respawns(player) -> __on_player_disconnects(player, reason) -> -__on_player_connects(player) -> +__on_player_connects(player) -> +__on_explosion(pos, power, source, create_fire, blocks, entities) -> diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index b10e15e4..4940cfe3 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -139,6 +139,7 @@ __on_player_respawns __on_player_disconnects __on_player_connects + __on_explosion $ 00' 01\ 02' 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 diff --git a/src/main/java/carpet/helpers/OptimizedExplosion.java b/src/main/java/carpet/helpers/OptimizedExplosion.java index 1c124ce5..5d164adb 100644 --- a/src/main/java/carpet/helpers/OptimizedExplosion.java +++ b/src/main/java/carpet/helpers/OptimizedExplosion.java @@ -48,6 +48,8 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; +import static carpet.script.CarpetEventServer.Event.EXPLOSION; + public class OptimizedExplosion { private static List entitylist; @@ -68,9 +70,14 @@ public class OptimizedExplosion private static ArrayList chances = new ArrayList<>(); private static BlockPos blastChanceLocation; + // Creating entity list for scarpet event + private static List entityList = new ArrayList<>(); + public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) { ExplosionAccessor eAccess = (ExplosionAccessor) e; + entityList.clear(); +boolean eventNeeded = EXPLOSION.isNeeded(); blastCalc(e); if (!CarpetSettings.explosionNoBlockDamage) { @@ -156,6 +163,11 @@ public class OptimizedExplosion densityCache.put(pair, density); } + // If it is needed, it saves the entity + if (eventNeeded) { + entityList.add(entity); + } + double d10 = (1.0D - d12) * density; entity.damage(e.getDamageSource(), (float) ((int) ((d10 * d10 + d10) / 2.0D * 7.0D * (double) f3 + 1.0D))); @@ -195,6 +207,11 @@ public class OptimizedExplosion double posY = eAccess.getY(); double posZ = eAccess.getZ(); + // If it is needed, calls scarpet event + if (EXPLOSION.isNeeded()) { + EXPLOSION.onExplosion((ServerWorld) world, eAccess.getX(), eAccess.getY(), eAccess.getZ(), eAccess.getPower(), e.getDamageSource(), eAccess.isCreateFire(), e.getAffectedBlocks(), entityList); + } + boolean damagesTerrain = eAccess.getDestructionType() != Explosion.DestructionType.NONE; // explosionSound incremented till disabling the explosion particles and sound @@ -598,4 +615,4 @@ public class OptimizedExplosion ); } } -} \ No newline at end of file +} diff --git a/src/main/java/carpet/mixins/Explosion_scarpetEventMixin.java b/src/main/java/carpet/mixins/Explosion_scarpetEventMixin.java new file mode 100644 index 00000000..d0ae56a3 --- /dev/null +++ b/src/main/java/carpet/mixins/Explosion_scarpetEventMixin.java @@ -0,0 +1,69 @@ +package carpet.mixins; + +import carpet.logging.LoggerRegistry; +import carpet.logging.logHelpers.ExplosionLogHelper; +import carpet.script.value.EntityValue; +import net.minecraft.entity.Entity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraft.world.explosion.Explosion; +import net.minecraft.world.explosion.ExplosionBehavior; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static carpet.script.CarpetEventServer.Event.EXPLOSION; + +@Mixin(value = Explosion.class, priority = 990) +public abstract class Explosion_scarpetEventMixin +{ + @Shadow @Final private World world; + @Shadow @Final private double x; + @Shadow @Final private double y; + @Shadow @Final private double z; + @Shadow @Final private float power; + @Shadow @Final private DamageSource damageSource; + @Shadow @Final private boolean createFire; + @Shadow @Final private List affectedBlocks; + private List affectedEntities; + + @Inject(method = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/damage/DamageSource;Lnet/minecraft/world/explosion/ExplosionBehavior;DDDFZLnet/minecraft/world/explosion/Explosion$DestructionType;)V", + at = @At(value = "RETURN")) + private void onExplosionCreated(World world, Entity entity, DamageSource damageSource, ExplosionBehavior explosionBehavior, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType destructionType, CallbackInfo ci) + { + if (EXPLOSION.isNeeded()) + { + affectedEntities = new ArrayList<>(); + } + } + + @Inject(method = "collectBlocksAndDamageEntities", locals= LocalCapture.CAPTURE_FAILHARD, at=@At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z")) + private void onExplosion(CallbackInfo ci, Set s1, float f1, int i1, int i2, int i3, int i4, int i5, int i6, List l1, Vec3d v1, int i7, Entity entity) + { + if (affectedEntities != null) + { + affectedEntities.add(entity); + } + } + + @Inject(method = "affectWorld", at = @At("HEAD")) + private void onExplosion(boolean spawnParticles, CallbackInfo ci) + { + if (EXPLOSION.isNeeded()) + { + EXPLOSION.onExplosion((ServerWorld) world, x, y, z, power, damageSource, createFire, affectedBlocks, affectedEntities); + } + } +} diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 3c1c084d..7324bb4c 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -865,6 +865,25 @@ public class CarpetEventServer ); } }; + public static final Event EXPLOSION = new Event("explosion", 6, true) + { + @Override + public void onExplosion(ServerWorld world, double x, double y, double z, float power, DamageSource source, boolean createFire, List affectedBlocks, List affectedEntities) + { + handler.call( + () -> Arrays.asList( + ListValue.fromTriple(x, y, z), + new NumericValue(power), + source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()), + createFire?Value.TRUE:Value.FALSE, + new ListValue(affectedBlocks.stream().map( + b -> new BlockValue(world.getBlockState(b),world,b) + ).collect(Collectors.toList())), + new ListValue(affectedEntities.stream().map(EntityValue::new).collect(Collectors.toList())) + ), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world) + ); + } + }; public static String getEntityLoadEventName(EntityType et) { @@ -985,6 +1004,7 @@ public class CarpetEventServer public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {} public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer) {} + public void onExplosion(ServerWorld world, double x, double y, double z, float power, DamageSource source, boolean createFire, List affectedBlocks, List affectedEntities) { } public void onWorldEvent(ServerWorld world, BlockPos pos) { } public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { } diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index f945eb81..0d18197f 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -129,6 +129,7 @@ "TntBlockMixin", "ExplosionMixin", "ExplosionAccessor", + "Explosion_scarpetEventMixin", "TntEntityMixin", "ProjectileEntityMixin",