本帖最后由 QQ酱29797 于 2022-12-26 11:58 编辑
本文基于 CC BY 发表,本文开发环境 1.16.5 Forge,Java 8。
本文基于袭击正常打法和正常模组环境下,请勿与袭击塔或各种神仙武器比较。
袭击在原版中强度还是算比较高的,但放在整合包中,又显得如此力不从心,因此本文基于这一点提供一些强化袭击的方案。
仅仅对面板数值的强化显得毫无意义,因此本文将从以下方面加强袭击:
- 任意玩家死亡算作袭击失败。
- 袭击仅能在主世界发起。
- 袭击的波数从原先的7+1波提升至固定15波。
- 袭击进行时所有实现了ITickableTileEntity接口的方块实体停止工作。
- 所有袭击者在袭击的时候免疫摔落、魔法、爆炸和其他袭击者的伤害。
- 袭击生成时所有袭击者分散开。
1.玩家死亡时袭击失败
这一点比较好实现,LivingDeathEvent即可实现,需要注意的是,在死亡的时候获取袭击记得判断是否为空。
当玩家死亡的时候使用Raid#stop即可停止袭击。
但这是停止袭击,而不是袭击失败。袭击状态是private的,因此我们需要将其改为public的。
- public net.minecraft.world.raid.Raid$WaveMember field_221286_h
- public net.minecraft.world.raid.Raid$Status
- public net.minecraft.world.raid.Raid field_221360_x
- public net.minecraft.world.raid.Raid field_221347_k
复制代码 后文还有其他要用到的,这里就一起修改访问权限了。
- @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.FORGE)
- public class PlayerDeathEvent {
- @SubscribeEvent
- public static void playerDeathEvent(LivingDeathEvent event){
- LivingEntity livingEntity = event.getEntityLiving();
- if(livingEntity.level.isClientSide) return;
- if(livingEntity instanceof PlayerEntity){
- MinecraftServer server = livingEntity.level.getServer();
- ServerWorld world = server.overworld();
- BlockPos deathPos = new BlockPos(livingEntity.position());
- Raid raid = world.getRaidAt(deathPos);
- if(raid != null){
- raid.status = Raid.Status.LOSS;
- }
- }
- }
- }
复制代码
2.袭击仅能在主世界发起:
这个也比较简单,在PlayerTickEvent判断玩家这里有不有袭击,有的话是不是主世界,不是停止即可。
- @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.FORGE)
- public class PlayerTickEvent {
- @SubscribeEvent
- public static void playerTickEvent(TickEvent.PlayerTickEvent event) {
- PlayerEntity player = event.getPlayer();
- if(player.level.isClientSide()) return;
- if(!player.level.dimension().equals(World.OVERWORLD)){
- if(world.isRaided(playerPos)){
- world.getRaidAt(playerPos).stop();
- }
- }
- }
- }
复制代码
3.拓展袭击波数
原版的袭击默认情况最高7波,当不祥之兆等级大于等于1会额外增加一波,因此最多是8波。
从原版的生成袭击者的方法来看,是从一个int[]数组中获取值,然后加上附加数量,但是这个数组只有八个,也就是索引最多到7,直接获取会越界。
根据以上分析,我们需要重写其生成时候的方法了,同时将原有的不祥之兆大于1级时的额外波数取消掉。
首先,拓展袭击波数:
- @Mixin(Raid.class)
- public abstract class RaidMixin{
- @Inject(method = "getNumGroups",at = @At("HEAD"),cancellable = true)
- public void getGroups(Difficulty p_221306_1_, CallbackInfoReturnable<Integer> cir){
- cir.setReturnValue(15);
- cir.cancel();
- }
- @Inject(method = "hasBonusWave",at = @At("HEAD"),cancellable = true)
- public void moreWave(CallbackInfoReturnable<Boolean> cir){
- cir.setReturnValue(false);
- cir.cancel();
- }
- }
复制代码
然后来干预袭击生成者的数量,从第9波开始,每波数量为第8波数量加上i-8波的数量(i为当前波数)
- @Shadow
- public int groupsSpawned; //我们在上文修改了它的访问权限,因此直接通过 (Raid)(Object)this 来获取也行
- @Inject(method = "getDefaultNumSpawns",at = @At("HEAD"),cancellable = true)
- public void getDefaultNumSpawns(Raid.WaveMember p_221330_1_, int p_221330_2_, boolean p_221330_3_, CallbackInfoReturnable<Integer> cir){
- if(groupsSpawned >= 7){
- int i = groupsSpawned - 7;
- List<Raid.WaveMember> members = new ArrayList<>(Arrays.asList(Raid.WaveMember.values()));
- for(Raid.WaveMember m : members){
- cir.setReturnValue(m.spawnsPerWaveBeforeBonus[i] + m.spawnsPerWaveBeforeBonus[7]);
- cir.cancel();
- }
- }
- }
复制代码
同样的,getPotentialBonusSpawns方法与上面类似,注意不要太多了,不然会变成神仙打架。
另外一点,袭击如果40分钟没有分出胜负将自动停止,因此我们需要把这个机制去掉:
- @Inject(method = "tick",at = @At(value = "INVOKE",target = "Lnet/minecraft/world/raid/Raid;stop()V",ordinal = 2),cancellable = true)
- public void tick(CallbackInfo ci){
- ci.cancel();
- }
复制代码
4.所有tick的方块实体停机
这个在袭击中还是比较重要的,对于植物魔法等模组,存在范围攻击、静止生物、生命恢复的功能花。
当world tick方块实体的时候,判断这个方块实体在不在袭击范围内,如果在的话将它放到不加载的Set中。
同时,袭击结束后得从不加载的Set中删除,并重新放到加载集合中。
注意,星辉魔法的仪式基座(效应放大器)会存在一些问题,建议将其排除在外,通过其他手段禁用。
- import hellfirepvp.astralsorcery.common.tile.base.TileEntitySynchronized;
- @Mixin(World.class)
- public abstract class WorldMixin {
- @Shadow
- @Final
- protected final Set<TileEntity> blockEntitiesToUnload = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>());
- private final Set<TileEntity> ban = new HashSet<>();
- @Inject(method = "tickBlockEntities",at = @At("HEAD"))
- public void tickEntities(CallbackInfo ci){
- World world = (World)(Object)this;
- MinecraftServer server = world.getServer();
- if(server == null) return;
- ServerWorld serverWorld = server.overworld();
- if(ban.size() != 0){
- for(TileEntity entity:ban){
- if(!world.tickableBlockEntities.contains(entity) && !serverWorld.isRaided(entity.getBlockPos())){
- world.tickableBlockEntities.add(entity);
- }
- }
- }
- for(TileEntity tileEntity : world.tickableBlockEntities){
- Raid raid = serverWorld.getRaidAt(tileEntity.getBlockPos());
- blockEntitiesToUnload.remove(tileEntity);
- if(raid != null && tileEntity instanceof ITickableTileEntity && !(tileEntity instanceof TileEntitySynchronized)){
- blockEntitiesToUnload.add(tileEntity);
- ban.add(tileEntity);
- }
- }
- }
- }
复制代码
5.让袭击者在袭击期间免疫杂项伤害:
这个也很简单,LivingDamageEvent事件就可以解决了,首先判断是否在袭击里面,然后判断伤害源就行,在这里就不多赘述了。
- @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.FORGE)
- public class LDEvent {
- @SubscribeEvent
- public static void livingDamageEvent(LivingDamageEvent event){
- LivingEntity entity = event.getEntityLiving();
- float damage = event.getAmount();
- DamageSource source = event.getSource();
- if(entity.level.isClientSide) return;
- if(entity instanceof AbstractRaiderEntity){
- MinecraftServer server = entity.getServer();
- if(server == null) return;
- Raid raid = server.overworld().getRaidAt(entity.blockPosition());
- damage *= raid != null && (source.getDirectEntity() instanceof AbstractRaiderEntity || source.isFire() || source.isExplosion() || source.equals(DamageSource.FALL)) ? 0 : 1;
- if(raid != null && source.getDirectEntity() instanceof ProjectileEntity){
- ProjectileEntity entity1 = (ProjectileEntity) source.getDirectEntity();
- damage *= entity1.getOwner() instanceof AbstractRaiderEntity ? 0 : 1;
- }
- }
- event.setAmount(damage);
- }
- }
复制代码
6.让袭击生成袭击者的时候随机生成
这个点相较于上面的来说复杂一点,因为需要考虑各种情况。
原有的袭击生成是选定一个点,然后在这里生成袭击者,因此我们要根据这个点和袭击中心点来获取一个随机的坐标。
同时这个坐标需要合法,不能卡在方块里,或者直接从天上掉下来。
首先,根据x和z坐标来获取这个位置的最上面一个方块。(因为我们不让袭击在其他维度发起,也不需要考虑下界顶层了)
- public class RaidUtils {
- private final BlockPos center;
- private final Raid raid;
- private final World world;
- public RaidUtils(@Nonnull Raid raid){
- this.raid = raid;
- this.center = raid.center;
- this.world = raid.getLevel();
- }
- private BlockPos getHighestBlock(BlockPos pos){
- return getHighestBlock(pos.getX(), pos.getZ());
- }
- private BlockPos getHighestBlock(int x,int z){
- for (int i = 255; i > 1; i--) {
- BlockPos pos = new BlockPos(x,i,z);
- BlockPos next = new BlockPos(x,i-1,z);
- if(isAir(pos) && !isAir(next)){
- return next;
- }
- }
- return null;
- }
- private boolean isAir(BlockPos pos){
- return world.getBlockState(pos).getBlock().equals(Blocks.AIR);
- }
- }
复制代码
然后我们根据六个int值,获取其中的随机值:
- private static class PosRange{
- private final int xmin;
- private final int xmax;
- private final int ymin;
- private final int ymax;
- private final int zmin;
- private final int zmax;
- private PosRange(float a,float a1,float a2,float a3,float a4,float a5){
- xmin = (int) a;
- xmax = (int) a1;
- ymin = (int) a2;
- ymax = (int) a3;
- zmin = (int) a4;
- zmax = (int) a5;
- }
- private BlockPos getRandPos(){
- return new BlockPos(getRangeRandom(xmin,xmax),getRangeRandom(ymin,ymax),getRangeRandom(zmin,zmax));
- }
- private static int getRangeRandom(int min,int max){
- if(min == max) return min;
- int m1 = min;
- int m2 = max;
- if(m2 < m1){
- m1 = max;
- m2 = min;
- } //分步改写了,便于阅读
- int r;
- do {
- r = getRandom(m2);
- } while (r < m1);
- return r;
- }
- }
复制代码
接下来我们根据生成点,袭击中心点来获取实际的生成坐标,分为以下四个阶段:
- 对整个范围生成,尝试生成3次。
- 如果找不到1生成点,将缩小一半范围,尝试生成3次。
- 如果找不到2生成点,将选定随机村民,并生成在村民的位置上。
- 如果受到其他模组干预,第三点生成失败,返回空值,袭击停止。
- public BlockPos getRandomPos(BlockPos i){
- PosRange range = new PosRange(i.getX(),2*center.getX()-i.getX(),i.getY(),2*center.getY()-i.getY(),i.getZ(),2*center.getZ()-i.getZ());
- for (int j = 0; j < 3; j++) {
- BlockPos rand = range.getRandPos();
- BlockPos pos1 = getHighestBlock(rand);
- if(pos1 != null){
- return pos1;
- }
- }
- PosRange range1 = new PosRange(0.5f*(i.getX()+center.getX()),1.5f*i.getX()-0.5f*center.getX(),0.5f*(i.getY()+center.getY()),1.5f*i.getY()-0.5f*center.getY(),0.5f*(i.getZ()+center.getZ()),1.5f*i.getZ()-0.5f*center.getZ());
- for (int j = 0; j < 3; j++) {
- BlockPos rand = range1.getRandPos();
- BlockPos pos2 = getHighestBlock(rand);
- if(pos2 != null){
- return pos2;
- }
- }
- if(getVillager().size() >= 1){
- VillagerEntity randomVillager = getVillager().get((int) (Math.random() * getVillager().size()));
- return randomVillager.blockPosition();
- }
- return null;
- }
- public List<VillagerEntity> getVillager(){
- MinecraftServer server = world.getServer();
- List<VillagerEntity> villagers = new ArrayList<>();
- World world = raid.getLevel();
- BlockPos pos = raid.getCenter();
- int x = pos.getX();
- int y = pos.getY();
- int z = pos.getZ();
- AxisAlignedBB aabb = new AxisAlignedBB(x+96,Math.min(256,y+96),z+96,x-96,Math.max(0,y-96),z-96);
- for(VillagerEntity v : world.getLoadedEntitiesOfClass(VillagerEntity.class,aabb)){
- BlockPos villagerPos = v.blockPosition();
- if(server != null && server.overworld().getRaidAt(villagerPos) != null && server.overworld().getRaidAt(pos).getId() == raid.getId()){
- villagers.add(v);
- }
- }
- return villagers;
- }
- }
复制代码
获取到了随机坐标后,我们修改加入袭击的方法,从而改变其生成的坐标。
将其后半段重写即可,同时我们可以略微修改部分参数。
- @Mutable
- @Shadow
- @Final
- private final ServerWorld level;
-
- protected RaidMixin(ServerWorld level) {
- this.level = level;
- }
- @Inject(method = "joinRaid",at = @At(value = "INVOKE",target = "Lnet/minecraft/entity/monster/AbstractRaiderEntity;setPos(DDD)V"),cancellable = true)
- public void spawn(int p_221317_1_, AbstractRaiderEntity p_221317_2_, BlockPos p_221317_3_, boolean p_221317_4_, CallbackInfo ci){
- Raid raid = (Raid) (Object) this;
- BlockPos pos = new RaidUtils(raid).getRandomPos(p_221317_3_);
- if(pos == null){
- raid.stop();
- ci.cancel();
- return;
- }
- p_221317_2_.setPos((double)pos.getX() + 0.5D, (double)pos.getY() + 1.0D, (double)pos.getZ() + 0.5D);
- p_221317_2_.finalizeSpawn(this.level, this.level.getCurrentDifficultyAt(p_221317_3_), SpawnReason.EVENT, (ILivingEntityData)null, (CompoundNBT)null);
- p_221317_2_.applyRaidBuffs(p_221317_1_, false);
- p_221317_2_.setOnGround(true);
- this.level.addFreshEntity(p_221317_2_);
- ci.cancel();
- }
复制代码
好了,这也只是我突发奇想随便想想的思路,如果有更好的点子不妨分享出来。
|
|