diff -u -r org/src/main/java/jp/jias/bukkit/bossmonster/BossMonster.java new/src/main/java/jp/jias/bukkit/bossmonster/BossMonster.java --- org/src/main/java/jp/jias/bukkit/bossmonster/BossMonster.java Fri Feb 26 21:10:12 2016 +++ new/src/main/java/jp/jias/bukkit/bossmonster/BossMonster.java Fri Feb 26 21:46:58 2016 @@ -1,5 +1,8 @@ package jp.jias.bukkit.bossmonster; +import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.logging.Logger; @@ -10,12 +13,17 @@ import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Arrow; import org.bukkit.entity.Creature; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.Player; +import org.bukkit.entity.Skeleton; +import org.bukkit.entity.Skeleton.SkeletonType; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByBlockEvent; @@ -23,10 +31,12 @@ import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.Vector; /** * ボスモンスタープラグイン本体 @@ -47,6 +57,11 @@ static final private String HEALTHGAUGE="||||||||||"; /** + * ボスのドロップアイテムの設定キー + */ + static final private String CONF_DROPITEM="dropItem"; + + /** * ボスモンスター */ private Creature boss=null; @@ -67,12 +82,26 @@ private Logger log; /** + * 設定ファイルの内容 + */ + private Configuration conf; + + /** * プラグインが開始するとき呼び出される */ public void onEnable(){ log=this.getLogger(); + // 独自のシリアライズ・デシリアライズ可能クラスを登録する + ConfigurationSerialization.registerClass(DropItem.class); + + // デフォルトの設定ファイルを用意する + this.saveDefaultConfig(); + + // 設定ファイルを読み込む + conf=this.getConfig(); + // イベントリスナーの登録 getServer().getPluginManager().registerEvents((Listener) this,this); } @@ -136,7 +165,7 @@ } // 難易度を1以上50以下に制限する - if(level < 0){ + if(level < 1){ level=1; } else if(level > 50){ @@ -147,6 +176,11 @@ // ボスモンスターを配置する boss=(Creature) ((Entity) sender).getWorld().spawnEntity(((Entity) sender).getLocation(),EntityType.SKELETON); + // スケルトンをスポーンさせた場合、ネザーではウィザースケルトンが出る可能性があるので、通常のスケルトンにする + if(boss.getType() == EntityType.SKELETON){ + ((Skeleton) boss).setSkeletonType(SkeletonType.NORMAL); + } + // ボスに持たせる武器を作る ItemStack weapon=new ItemStack(Material.BOW); @@ -175,7 +209,7 @@ // ボスの移動速度を、レベル20以下ではレベル2、それ以上ではレベル3にする int speed=level <= 20?2:3; - // ボスに1時間(3600秒)のエンチャント(移動速度増加・攻撃力増加・再生能力・ダメージ減少・火炎耐性)をかける + // ボスに1時間(3600秒)のエンチャント(移動速度増加・攻撃力上昇・再生能力・耐性・火炎耐性)をかける boss.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,3600 * 20,speed,false)); boss.addPotionEffect(new PotionEffect(PotionEffectType.INCREASE_DAMAGE,3600 * 20,1,false)); boss.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION,3600 * 20,1,false)); @@ -196,6 +230,62 @@ return true; } + // ドロップアイテムを設定するコマンドか + else if(commandLabel.equalsIgnoreCase("bossdrop")){ + + // ホットバーに登録されているアイテムのリストを作る + List items=new ArrayList<>(); + Inventory inv=((Player) sender).getInventory(); + for(int i=0;i < 9;i++){ + // nullまたは空気アイテムは無視 + if(inv.getItem(i) == null + || inv.getItem(i).getType() == Material.AIR){ + continue; + } + + // アイテム個数は所持数の0.5倍から2倍の範囲、レアリティは100%でドロップアイテムのデータを作る + ItemStack item=inv.getItem(i).clone(); + DropItem drop=new DropItem(); + drop.setAmountMin(item.getAmount() / 2d); + drop.setAmountMax(item.getAmount() * 2); + drop.setRarity(100); + + // アイテム個数のデータはamountMin、amountMaxで表現されるので、ItemStackのアイテム個数は1にしておく + item.setAmount(1); + drop.setItem(item); + + // リストに追加 + items.add(drop); + } + + // ドロップアイテムがないときはエラー + if(items.isEmpty()){ + sender.sendMessage(ChatColor.GOLD + + "ドロップアイテムを設定するには、あなたのホットバーにドロップしたいアイテムを配置し、このコマンドをもう一度実行してください。"); + return true; + } + + // ドロップアイテムの設定を上書きして保存 + conf.set(CONF_DROPITEM,items); + sender.sendMessage(ChatColor.GOLD + + "ドロップアイテムを設定しました。ドロップの数量や確率を調整するには、以下のファイルを直接編集して下さい。"); + sender.sendMessage(ChatColor.GOLD + + this.getDataFolder().getAbsolutePath() + File.separator + + "conf.yml"); + + // パスをコピペできるよう、サーバーコンソールにも同じメッセージを出力する + Bukkit.getConsoleSender() + .sendMessage( + ChatColor.GOLD + + "ドロップアイテムを設定しました。ドロップの数量や確率を調整するには、以下のファイルを直接編集して下さい。"); + Bukkit.getConsoleSender().sendMessage( + ChatColor.GOLD + this.getDataFolder().getAbsolutePath() + + File.separator + "conf.yml"); + + this.saveConfig(); + + return true; + } return false; } @@ -252,6 +342,22 @@ break; } } + + // ボスがスケルトンかつレベル30以上かつ体力が30%以下の場合はウィザースケルトンに変身させる + if(boss.getType() == EntityType.SKELETON + && ((Skeleton) boss).getSkeletonType() == SkeletonType.NORMAL + && level >= 30 + && boss.getHealth() / (float) boss.getMaxHealth() < 0.3f){ + getServer().broadcastMessage( + ChatColor.YELLOW + "邪悪な存在はその正体を現した!"); + + // スケルトンタイプをウィザースケルトンに変更 + ((Skeleton) boss).setSkeletonType(SkeletonType.WITHER); + + // 効果音を再生 + boss.getWorld().playSound(boss.getLocation(), + Sound.ZOMBIE_REMEDY,100,0.5f); + } } } @@ -303,6 +409,7 @@ * * @param e */ + @SuppressWarnings("unchecked") @EventHandler public void onEntityDeathEvent(EntityDeathEvent e){ @@ -317,25 +424,80 @@ skill.cancel(); } - // 討伐報酬として1個以上16個以下のランダムな個数のダイヤモンドを、(レベル÷10)+1セットドロップする - for(int n=0;n != (level / 10) + 1;n++){ - // ボスのランダムな周囲の座標を計算する(報酬アイテムを複数撒いたとき、近い場所にあるとまとまってひとつになってしまうのを防ぐ) - Location loc=boss.getLocation(); - loc.add(rand.nextInt(10) - 5,1,rand.nextInt(10) - 5); + // ボスが死んだことをサーバー全体メッセージで報告する + getServer().broadcastMessage(ChatColor.YELLOW + "邪悪な存在は勇者により討伐された…"); - // 1個以上16以下のダイヤモンドを生成する - loc.getWorld().dropItem(loc,new ItemStack(Material.DIAMOND,1 + rand.nextInt(16))); + // 設定ファイルからドロップアイテムの一覧と経験値の最小・最大値を取得する + int dropExpMin,dropExpMax; + List dropItems; + try{ + dropExpMin=conf.getInt("dropExp.amountMin"); + dropExpMax=conf.getInt("dropExp.amountMax"); + dropItems=(List) conf.getList(CONF_DROPITEM); + }catch(Exception ex){ + // エラー発生時は何もドロップしない + log.warning("[ボスモンスタープラグイン] ボス討伐時のドロップ経験値とアイテムの設定を読み込めません。"); + dropExpMin=0; + dropExpMax=0; + dropItems=new ArrayList(); } - // レベルの500倍の経験値オーブをドロップする(レベル24でエンダードラゴンが出す経験値オーブと同じ量になる) - e.setDroppedExp(level * 500); + // ボスの座標を取得 + Location bossLoc=boss.getLocation(); - // ボスが死んだことをサーバー全体メッセージで報告する - getServer().broadcastMessage(ChatColor.YELLOW + "邪悪な存在は勇者により討伐された…"); + // ボスのレベル数だけドロップ計算する + for(int i=0;i < level;i++){ + // ドロップアイテムの一覧を処理 + for(DropItem dropItem:dropItems){ + // ドロップアイテムが集約してしまわないよう、周辺5m以内のランダムな座標を生成する + dropItem.fire(getCircumferenceLocation(bossLoc,5)); + } + } + + // ボスのレベル数だけ経験値オーブを作る + // Spigotサーバではオーブは近くのオーブと合算されてしまうので、別の場所にスポーンさせてからボスのもとへ移動させる + Location spawnLoc=bossLoc.clone(); + spawnLoc.setY(-1); + for(int i=0;i < level;i++){ + // 経験値を計算する + int exp=dropExpMin + rand.nextInt(dropExpMax - dropExpMin); + + // 経験値がゼロだったらオーブをスポーンしない + if(exp == 0){ + continue; + } + + // 経験値オーブをスポーンする + ExperienceOrb orb=spawnLoc.getWorld().spawn(spawnLoc,ExperienceOrb.class); + orb.setExperience(orb.getExperience() + exp); + orb.teleport(bossLoc); + orb.setVelocity(Vector.getRandom().multiply(0.3f)); + } + + // 経験値オーブは別途スポーンさせているのでボスからは経験値オーブをスポーンさせない + e.setDroppedExp(0); // ボスが死んだので変数を初期化する boss=null; } + } + + /** + * 指定した座標を中心に水平方向の周囲の座標を計算して返す(ただし計算量削減のため"周囲"の形は円ではなく短形) + * + * @param loc + * 中央の座標 + * @param width + * 半径 + * + * @return 周囲の座標 + */ + public Location getCircumferenceLocation(Location loc,int width){ + // 座標をコピー + Location cirLoc=loc.clone(); + // XとZ方向にランダムな値を加算 + cirLoc.add(rand.nextDouble() * width * 2 - width,0,rand.nextDouble() * width * 2 - width); + return cirLoc; } /** Only in new/src/main/java/jp/jias/bukkit/bossmonster: DropItem.java diff -u -r org/src/main/java/jp/jias/bukkit/bossmonster/MachinegunShot.java new/src/main/java/jp/jias/bukkit/bossmonster/MachinegunShot.java --- org/src/main/java/jp/jias/bukkit/bossmonster/MachinegunShot.java Fri Feb 26 21:10:41 2016 +++ new/src/main/java/jp/jias/bukkit/bossmonster/MachinegunShot.java Fri Feb 26 21:35:26 2016 @@ -65,7 +65,7 @@ for(int n=0;n != shot;n++){ // 向いている方向に矢を発射する - Arrow arrow=loc.getWorld().spawnArrow(loc.add(loc.getDirection()),loc.getDirection(),1f,12); + Arrow arrow=loc.getWorld().spawnArrow(loc.add(loc.getDirection()),loc.getDirection(),1f,30); // 発射した矢を、発射済みの矢のリストに加える arrows.add(arrow); Only in new/src/main/resources: config.yml diff -u -r org/src/main/resources/plugin.yml new/src/main/resources/plugin.yml --- org/src/main/resources/plugin.yml Fri Feb 26 21:11:01 2016 +++ new/src/main/resources/plugin.yml Mon Jan 18 01:50:38 2016 @@ -7,4 +7,10 @@ usage: /boss permission: Protecter.basic permission-message: You don't have + default: op + bossdrop: + description: 【管理者専用】ボスモンスタープラグインのドロップアイテムを設定します + usage: /bossdrop + permission: Protecter.basic + permission-message: You don't have default: op Only in org/src/main/resources: pom.xml