不給糖就搗蛋!
這不是崇洋媚外的心態,在遊戲內純粹就是好玩的心理。為了應景一下,在我們目前的模組我想要做到以下的功能:
好了,讓我們一步一步地完成上面的工作吧!
這個功能我們在[Day24]已經有提過了,我想要達成這個目的很簡單:在進入遊戲後,直接在Mods設定中的將功能全部移除:
這個功能有一點麻煩,但我們先想想要怎麼做以及思考方向:
有了以上的想法後,我們可以透過在講指令時提過,任何指令類別一定要繼承CommandBase
;透過Ctrl + H
查找CommandBase
的子類別,就會找到下面兩個與我們提到要變動時間的工作有關的指令類別:
這兩個類別裡面就已經很清楚的說明如何用指令改時間了,所以我們就將對應的功能放到ClientProxy
內的FMLServerStartingEvent
事件:
ClientProxy.java
...(略)
@Override
public void serverStarting(FMLServerStartingEvent event) {
// 0 means the OverWorld (主世界)
// -1 means the Nether (地獄)
// 1 means the Ent (終界)
WorldServer world = event.getServer().worldServerForDimension(0);
// 更改時間為半夜0時 (18000 = 第15分鐘 * 60秒 * 20刻)
world.setWorldTime(18000);
// 將gamerule中的日夜交替動作取消
world.getGameRules().setOrCreateGameRule("doDaylightCycle", "false");
}
..(略)
這個時候存檔進入遊戲會看到的是月亮在正上方(午夜),並且時間不會變化。
這個想法出現後我馬上就後悔了...因為實在是太花我的時間去思考要如何達成。
但是,最後還是有了一個我想要的效果出來了
我們這邊只以苦力怕作為範例。
Minecraft要自定義不存在的效果是非常困難的,因此首先一定是先想:是什麼現有的東西也會發光,讓我們可以去參考呢?
我想到的是:火把
當然你也有可能想到紅石火把、營火等等,這些東西都有一個共通的特性:都是方塊(Block)
我們要改的可是生物(Entity / Mobs)的亮度阿!
所以這裡我找了一下資料,大概的作法都是:
LivingUpdateEvent
處理我們的邏輯。相關的作法只要找一下都可以參考,我這邊只引用我需要的部分:
com.ithome.mymod.events
套件目錄下新增LivingUpdate
類別LivingUpdateEvent
事件的方法:
public class LivingUpdate {
@SubscribeEvent
public void livingUpdate(LivingEvent.LivingUpdateEvent event) {
if(event.entity instanceof HalloweenCreeper) {
BlockPos pos = event.entity.getPosition();
addLight(event.entity.worldObj, pos.getX(), pos.getY(), pos.getZ(), 10);
}
}
private void addLight(World world, int posX, int posY, int posZ, int lightLevel) {
world.setLightFor(EnumSkyBlock.BLOCK, new BlockPos(posX, posY, posZ), lightLevel);
world.markBlockRangeForRenderUpdate( posX, posY, posZ, 12, 12, 12);
world.updateBlockTick(new BlockPos( posX, posY, posZ),
world.getBlockState(new BlockPos( posX, posY, posZ)).getBlock(), 1, 0);
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos( posX, posY + 1, posZ));
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos( posX, posY - 1, posZ));
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos(posX + 1, posY, posZ));
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos(posX - 1, posY, posZ));
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos(posX, posY, posZ + 1));
world.checkLightFor(EnumSkyBlock.BLOCK, new BlockPos(posX, posY, posZ - 1));
}
}
MyMod.java
主程式內的eventTriggerMap
物件內,新增我們的事件註冊:
MyMod.eventTriggerMap.put(new LivingUpdate(), true);
我們這邊只以苦力怕作為範例。
這個功能與我們在[Day27]提到的"孤僻貓"很像;要做的只是將所有的AI工作移除後,只保留會"追"玩家的部分就好:
HalloweenCreeper
類別到com.ithome.mymod.entities
套件下,繼承EntityCreeper
並且把所有AI工作先移除:
public class HalloweenCreeper extends EntityCreeper {
public static String name = "HalloweenCreeper";
public HalloweenCreeper(World worldIn) {
super(worldIn);
setupUI();
}
private void setupUI() {
this.tasks.taskEntries.clear();
this.targetTasks.taskEntries.clear();
}
}
MyMod.java
主程式,註冊新的HalloweenCreeper
實體,在preInit
方法內加上:
// 註冊HalloweenCreeper
while (EntityList.getClassFromID(availableId) != null) {
availableId++;
}
EntityRegistry.registerGlobalEntityID(HalloweenCreeper.class, HalloweenCreeper.name, availableId);
丟訊息的AI工作就比較簡單了:
EntityAIHalloween
類別在com.ithome.mymod.ai
下:public class EntityAIHalloween extends EntityAIBase {
private final EntityMob entityMob;
// 判斷碰撞的時間,避免太頻繁執行
public static long timer;
public EntityAIHalloween(EntityMob entityMob) {
this.entityMob = entityMob;
// 7可以與大部分的AI工作同時發生
setMutexBits(7);
}
@Override
public boolean shouldExecute() {
if(entityMob.getAttackTarget() != null) {
EntityLivingBase target = entityMob.getAttackTarget();
float distance = entityMob.getDistanceToEntity(target);
if(distance < 2.0F) {
return true;
}
}
return false;
}
@Override
public void startExecuting() {
EntityPlayer player = (EntityPlayer)entityMob.getAttackTarget();
// 往碰撞的玩家送出訊息
player.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE + "Trick or treat!"));
// 設定執行時間
timer = System.currentTimeMillis();
}
@Override
public boolean continueExecuting() {
// 若當前時間與執行時間相差在2秒內,就繼續這個AI工作來避免下一輪的觸發 (避免太頻繁)
return System.currentTimeMillis() - timer < 2 * 1000;
}
}
HalloweenCreeper
類別,加上必要的AI工作:
private void setupUI() {
this.tasks.taskEntries.clear();
this.targetTasks.taskEntries.clear();
// 送訊息給玩家的工作
this.tasks.addTask(0, new EntityAIHalloween(this));
// 閒晃
this.tasks.addTask(1, new EntityAIWander(this, 0.8D));
// 尋找玩家與靠近
this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(3, new EntityAIAttackOnCollide(this, 1.0D, false));
// Idle狀態
this.tasks.addTask(4, new EntityAILookIdle(this));
// 設定目標的工作
this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
}
後續還有很多的功能可以自行發揮,這裡就完成一個簡單版的Halloween!