package jp.jias.bukkit.bossmonster; import java.util.Random; import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; 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.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; /** * ボスモンスタープラグイン本体 * * @author i_shi_ba_shi ( jias.jp ) * */ public class BossMonster extends JavaPlugin implements Listener{ /** * 乱数ジェネレータ */ static private Random rand=new Random(); /** * ボスの残りHPゲージの表示方法 */ static final private String HEALTHGAUGE="||||||||||"; /** * ボスモンスター */ private Creature boss=null; /** * 直前に実行したスキル */ private Skill skill=null; /** * ボスのレベル */ private int level; /** * ログ出力用 */ private Logger log; /** * プラグインが開始するとき呼び出される */ public void onEnable(){ log=this.getLogger(); // イベントリスナーの登録 getServer().getPluginManager().registerEvents((Listener) this,this); } /** * プラグインが終了するとき呼び出される */ public void onDisable(){ // ボスがまだ生きていたら削除する if(boss != null && !boss.isDead()){ Bukkit.broadcastMessage(ChatColor.GOLD + "邪悪な存在は姿を消した…"); boss.remove(); boss=null; } } /** * コマンドが実行されたら呼び出される */ public boolean onCommand(CommandSender sender,Command cmd, String commandLabel,String[] args){ // コマンドを実行したプレイヤーを特定する if(!(sender instanceof Player)){ log.warning("このコマンドはゲームにログインしたキャラクターが実行してください。"); return true; } // ボスモンスターを呼び出すコマンドか if(commandLabel.equalsIgnoreCase("boss")){ // 既にボスがいるか if(boss != null){ // 既にいるボスをコマンド実行者の場所に移動させる boss.teleport(((Player) sender).getLocation()); // 効果音としてワープポータルに入ったときの音を再生する boss.getWorld().playSound(boss.getLocation(),Sound.PORTAL,100,1f); sender.sendMessage(ChatColor.GOLD + "ボスをあなたの場所に移動させました。"); return true; } // 難易度のデフォルトは1 level=1; // コマンドの引数で難易度が指定されているか if(args.length == 1){ // 指定された難易度を数値として解釈する try{ level=Integer.parseInt(args[0]); }catch(NumberFormatException e){ sender.sendMessage(ChatColor.GOLD + "難易度は数値で指定してください。"); return false; } // 難易度を1以上50以下に制限する if(level < 0){ level=1; } else if(level > 50){ level=50; } } // ボスモンスターを配置する boss=(Creature) ((Entity) sender).getWorld().spawnEntity(((Entity) sender).getLocation(),EntityType.SKELETON); // ボスに持たせる武器を作る ItemStack weapon=new ItemStack(Material.BOW); // ボスの武器にダメージ増加エンチャントをかける weapon.addEnchantment(Enchantment.ARROW_DAMAGE,2); // ボスに武器を持たせる boss.getEquipment().setItemInHand(weapon); // ボスに鉄の防具4点セット(ヘルメット・チェストプレート・レギンス・ブーツ)を装備させる boss.getEquipment().setHelmet(new ItemStack(Material.IRON_HELMET)); boss.getEquipment().setChestplate(new ItemStack(Material.IRON_CHESTPLATE)); boss.getEquipment().setLeggings(new ItemStack(Material.IRON_LEGGINGS)); boss.getEquipment().setBoots(new ItemStack(Material.IRON_BOOTS)); // ボスに名前を付ける updateBossHealthGauge(); // ボスの名前を常に表示する boss.setCustomNameVisible(true); // ボスのHPを多めに設定 boss.setMaxHealth(100 * level); boss.setHealth(boss.getMaxHealth()); // ボスの移動速度を、レベル20以下ではレベル2、それ以上ではレベル3にする int speed=level <= 20?2:3; // ボスに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)); boss.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE,3600 * 20,1,false)); boss.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE,3600 * 20,1,false)); // ボスがデスポーンするのを防止する boss.setRemoveWhenFarAway(false); // ボスが配置されたことをサーバー全体メッセージで報告する getServer().broadcastMessage(ChatColor.YELLOW + "邪悪な存在がこの地に舞い降りた…"); // 効果音としてワープポータルに入ったときの音を再生する boss.getWorld().playSound(boss.getLocation(),Sound.PORTAL_TRAVEL,100,1f); // ボスにエフェクトをつける new BossEffect(boss).runTaskTimer(this,0,5); return true; } return false; } /** * エンティティがエンティティからダメージを受けるとき呼び出される * * @param e */ @EventHandler public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent e){ // ダメージを受けたエンティティはボスか if(e.getEntity().equals(boss)){ // ダメージ原因が矢で、かつその矢がボス自身の放ったものならダメージをキャンセルする if(e.getDamager() instanceof Arrow && boss.equals(((Arrow) e.getDamager()).getShooter())){ e.setCancelled(true); return; } // 1/3の確率で、効果音としてウィザーの攻撃音を再生速度50%で再生する if(rand.nextInt(3) == 0){ boss.getWorld().playSound(boss.getLocation(),Sound.WITHER_SHOOT,100,(float) (0.5+rand.nextFloat())); } // ボスのHP表示を更新する updateBossHealthGauge(); // スキルを実行中ではないか if(skill == null || !skill.isRunning()){ // 低確率でスキルを実行する switch(rand.nextInt(20)){ case 0: // 乱数で0が出たらトルネードショット skill=new TornadoShot(this,level); break; case 1: // 乱数で1が出たらマシンガンショット skill=new MachinegunShot(this,level); break; case 2: // 乱数で2が出たらシャワーショット skill=new ShowerShot(this,level); break; case 3: // 乱数で3が出てボスのHPが半分以下ならヒールポーション if(boss.getHealth() / (float) boss.getMaxHealth() < 0.5){ skill=new ThrowPotion(this,level); } break; default: // それ以外はスキルを実行しない break; } } } } /** * エンティティがブロックからダメージを受けるとき呼び出される * * @param e */ @EventHandler public void onEntityDamageByBlockEvent(EntityDamageByBlockEvent e){ // ダメージを受けたエンティティはボスか if(e.getEntity().equals(boss)){ // ボスのHP表示を更新する updateBossHealthGauge(); } } /** * エンティティの体力が回復するとき呼び出される * * @param e */ @EventHandler public void onEntityRegainHealthEvent(EntityRegainHealthEvent e){ // エンティティはボスか if(e.getEntity().equals(boss)){ // ボスのHP表示を更新する updateBossHealthGauge(); } } /** * ボスの名前と残りHPゲージを表示する */ private void updateBossHealthGauge(){ // ボスの残りHPが最大HPの何割か調べる int remaining=(int) (boss.getHealth() / (double) boss.getMaxHealth() * HEALTHGAUGE .length()); // ボスの名前と残りHPを表示する boss.setCustomName("ゴルゴ・サーティワン " + ChatColor.GREEN + HEALTHGAUGE.substring(0,remaining) + ChatColor.DARK_RED + HEALTHGAUGE.substring(remaining) + ChatColor.RESET); } /** * エンティティが死亡するとき呼び出される * * @param e */ @EventHandler public void onEntityDeathEvent(EntityDeathEvent e){ // 死亡したのはボスか if(e.getEntity().equals(boss)){ // 効果音としてウィザーの死亡音を再生する boss.getWorld().playSound(boss.getLocation(),Sound.WITHER_DEATH,100,1f); // スキルを実行中なら停止する if(skill != null && skill.isRunning()){ 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); // 1個以上16以下のダイヤモンドを生成する loc.getWorld().dropItem(loc,new ItemStack(Material.DIAMOND,1 + rand.nextInt(16))); } // レベルの500倍の経験値オーブをドロップする(レベル24でエンダードラゴンが出す経験値オーブと同じ量になる) e.setDroppedExp(level * 500); // ボスが死んだことをサーバー全体メッセージで報告する getServer().broadcastMessage(ChatColor.YELLOW + "邪悪な存在は勇者により討伐された…"); // ボスが死んだので変数を初期化する boss=null; } } /** * チャンクがアンロードされるとき呼び出される * * @param e */ @EventHandler public void onChunkUnloadEvent(ChunkUnloadEvent e){ // ボスは生きているか if(boss != null && !boss.isDead()){ // アンロードされるチャンクはボスがいるチャンクか if(e.getChunk().equals(boss.getLocation().getBlock().getChunk())){ // アンロードをキャンセル e.setCancelled(true); } } } /** * 乱数ジェネレータを返す * * @return 乱数ジェネレータ */ static public Random getRandom(){ return rand; } /** * ボスを返す * * @return ボス */ public Creature getBoss(){ return boss; } }