Almost finsihed! Just need to do testing + finishing the reward item.
This commit is contained in:
parent
5dd7716e88
commit
f276b9aedf
5 changed files with 226 additions and 22 deletions
|
@ -1,7 +1,12 @@
|
|||
# Spooky
|
||||
Halloween plugin.
|
||||
A new bossfight's waiting for you and your players. <b>The GHost is <i>here</i></b>.
|
||||
Build a structure like this one: (be sure to place the pumpkin in last!)
|
||||
<img src="https://raw.githubusercontent.com/Ad5001/Spooky/master/img/struct.png"></img>
|
||||
|
||||
|
||||
Afterwards let the boss fight go!
|
||||
|
||||
Music used here: NIVIRO - The Return
|
||||
Track's link: https://soundcloud.com/djniviro/thereturn
|
||||
Niviro's website: https://www.djniviro.com
|
|
@ -5,7 +5,10 @@ use pocketmine\command\Command;
|
|||
use pocketmine\plugin\PluginBase;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\event\Listener;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\event\block\BlockPlaceEvent;
|
||||
use pocketmine\block\Block;
|
||||
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
|
@ -18,7 +21,7 @@ use Ad5001\Spooky\entity\Ghost;
|
|||
use Ad5001\Spooky\tasks\TickTask;
|
||||
|
||||
|
||||
class Main extends PluginBase{
|
||||
class Main extends PluginBase implements Listener{
|
||||
|
||||
public $ghosts = [];
|
||||
|
||||
|
@ -29,8 +32,9 @@ class Main extends PluginBase{
|
|||
*/
|
||||
public function onEnable(){
|
||||
// Registering some enchants
|
||||
Enchantement::registerEnchantment(new Enchantement(Enchantement::SHARPNESS, "%enchantment.attack.sharpness", Enchantement::RARITY_COMMON, Enchantement::SLOT_SWORD));
|
||||
// $this->getServer()->getScheduler()->scheduler<Delayed or Repeating>Task(new Task1($this), <TIME>);
|
||||
Enchantment::registerEnchantment(new Enchantment(Enchantment::SHARPNESS, "%enchantment.attack.sharpness", Enchantment::ACTIVATION_HELD, Enchantment::RARITY_COMMON, Enchantment::SLOT_SWORD));
|
||||
$this->getServer()->getScheduler()->scheduleRepeatingTask(new TickTask($this), 2);
|
||||
$this->getServer()->getPluginManager()->registerEvents($this, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +55,57 @@ class Main extends PluginBase{
|
|||
}
|
||||
|
||||
|
||||
public function onBlockPlace(BlockPlaceEvent $event) {
|
||||
// Checking the pumpkin at the top
|
||||
$found = false;
|
||||
if($event->getBlock()->getId() == Block::PUMPKIN){
|
||||
$under = $event->getBlock()->asVector3();
|
||||
$under->y--;
|
||||
// Hay bale for the body
|
||||
if($event->getBlock()->getLevel()->getBlock($under)->getId() == Block::HAY_BALE) {
|
||||
$under2 = $event->getBlock()->asVector3();
|
||||
$under2->y--;
|
||||
// Fence for the bottom
|
||||
if($event->getBlock()->getLevel()->getBlock($under2)->getId() == Block::FENCE){
|
||||
// Fences for the sides.
|
||||
$side1 = $under->asVector3();
|
||||
$side1->x++;
|
||||
$side2 = $under->asVector3();
|
||||
$side2->x--;
|
||||
if($event->getBlock()->getLevel()->getBlock($side1)->getId() == Block::FENCE && $event->getBlock()->getLevel()->getBlock($side1)->getId() == Block::FENCE) {
|
||||
$found = true;
|
||||
} else {
|
||||
$side1 = $under->asVector3();
|
||||
$side1->z++;
|
||||
$side2 = $under->asVector3();
|
||||
$side2->z--;
|
||||
if($event->getBlock()->getLevel()->getBlock($side1)->getId() == Block::FENCE && $event->getBlock()->getLevel()->getBlock($side1)->getId() == Block::FENCE) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If everything's right, we can destroy the structure & generate the ghost
|
||||
if($found){
|
||||
$event->setCancelled();
|
||||
$event->getBlock()->getLevel()->setBlock($under, Block::get(Block::AIR));
|
||||
$event->getBlock()->getLevel()->setBlock($under2, Block::get(Block::AIR));
|
||||
$event->getBlock()->getLevel()->setBlock($side1, Block::get(Block::AIR));
|
||||
$event->getBlock()->getLevel()->setBlock($side2, Block::get(Block::AIR));
|
||||
if($event->getPlayer()){
|
||||
$this->spawnGhost($event->getPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawns a ghost
|
||||
*
|
||||
* @param Player $p
|
||||
* @return void
|
||||
*/
|
||||
public function spawnGhost(Player $p){
|
||||
// Getting the skin
|
||||
$nbtSkin = new NBT(NBT::BIG_ENDIAN);
|
||||
|
|
|
@ -17,6 +17,7 @@ use pocketmine\nbt\tag\FloatTag;
|
|||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\entity\Effect;
|
||||
|
||||
use Ad5001\Spooky\Main;
|
||||
|
@ -47,7 +48,7 @@ class Ghost extends Human {
|
|||
$this->setDataProperty(self::DATA_SCALE, self::DATA_TYPE_FLOAT, new FloatTag("Scale", 1.2));
|
||||
parent::__construct($level, $nbt);
|
||||
$it = Item::get(Item::GOLDEN_HOE, 0);
|
||||
$it->addEnchantement(Enchantement::getEnchantement(Enchantement::SHARPNESS));
|
||||
$it->addEnchantment(Enchantment::getEnchantment(Enchantment::SHARPNESS));
|
||||
$this->getInventory()->setItemInHand($it);
|
||||
}
|
||||
|
||||
|
@ -97,6 +98,7 @@ class Ghost extends Human {
|
|||
public function intenseFight(){
|
||||
if(!$this->checkIfConnected()) return;
|
||||
// TODO: Custom intense fight
|
||||
$this->fightType = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +120,7 @@ class Ghost extends Human {
|
|||
public function blackOutEnterPhase(){
|
||||
if(!$this->checkIfConnected()) return;
|
||||
$this->fightType = 0;
|
||||
$spawnBlock = $this->getPlayer()->getLineOfSight(2, 3, []);
|
||||
$spawnBlock = $this->getPlayer()->getLineOfSight(2);
|
||||
$spawnBlock = $spawnBlock[count($spawnBlock) -1];
|
||||
$this->getPlayer()->addEffect(
|
||||
Effect::getEffectById(Effect::BLINDNESS)->setDuration(30*20)->setAmplifier(4)->setVisible(false)
|
||||
|
@ -243,14 +245,18 @@ class Ghost extends Human {
|
|||
/**
|
||||
* Check the damage to reduce it by 25%
|
||||
*
|
||||
* @param EntityDamageEvent $source
|
||||
* @param EntityDamageEvent $event
|
||||
*/
|
||||
public function attack(EntityDamageEvent $source) {
|
||||
public function attack(EntityDamageEvent $event) {
|
||||
if($event instanceof EntityDamageByEntityEvent) {
|
||||
if($event->getDamager() instanceof Player && $event->getDamager()){
|
||||
$source->setDamage($source->getDamage() * 0.75);
|
||||
$event->setDamage($event->getDamage() * 0.75);
|
||||
} else {
|
||||
$event->setCancelled(true);
|
||||
$event->getDamager()->motionY = 12;
|
||||
$event->getDamager()->addEffect(
|
||||
Effect::getEffectById(Effect::NAUSEA)->setDuration(30*20)->setAmplifier(99)->setVisible(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,19 +268,62 @@ class Ghost extends Human {
|
|||
* @return bool
|
||||
*/
|
||||
public function onUpdate(int $currentTick): bool {
|
||||
if(!$this->checkIfConnected()) return;
|
||||
if(!$this->checkIfConnected()) return false;
|
||||
if($this->fightType == 0) return false;
|
||||
// Teleportation
|
||||
if(rand(0, 200) == 0) { // Do we do teleportation?
|
||||
if(rand(0,1) == 0){ // Which kind of tp? Random around or forward?
|
||||
$los = $this->getLineOfSight(10);
|
||||
$b = $los[count($los) - 1];
|
||||
$this->teleport($b);
|
||||
} else {
|
||||
$x = rand($this->x + 8, $this->x - 8);
|
||||
$z = rand($this->z + 8, $this->z - 8);
|
||||
for($y = $this->y + 16; $y > $this->y - 16; $this->y--){
|
||||
if($this->getLevel()->getBlock($x, $y -1, $z)->getId() !== 0) break;
|
||||
}
|
||||
$this->teleport(new Vector3($x, $y, $z));
|
||||
}
|
||||
}
|
||||
// Setting specific motion
|
||||
$diffV3 = $this->associatedPlayer->asVector3()->subtract($this->asVector3());
|
||||
$distDiff = $diffV3->asVector3()->abs();
|
||||
$distDiff = $distDiff->x + $distDiff->z;
|
||||
// Check if we can attack the player
|
||||
if($this->distanceSquared($this->associatedPlayer) <= 1){
|
||||
$this->attackEntity($this->associatedPlayer);
|
||||
}
|
||||
// If not, try moving torowards him
|
||||
if ($diff > 0) {
|
||||
$this->motionX = $this->getSpeed() * 0.15 * ($x / $diff);
|
||||
$this->motionZ = $this->getSpeed() * 0.15 * ($z / $diff);
|
||||
$this->yaw = rad2deg(-atan2($x / $diff, $z / $diff));
|
||||
$this->motionX = $this->getSpeed() * 0.15 * ($diffV3->x / $distDiff);
|
||||
$this->motionZ = $this->getSpeed() * 0.15 * ($diffV3->z / $distDiff);
|
||||
$this->yaw = rad2deg(-atan2($diffV3->x / $distDiff, $diffV3->z / $distDiff));
|
||||
}
|
||||
if($y == 0){
|
||||
$this->pitch = 0;
|
||||
} else {
|
||||
$this->pitch = rad2deg(-atan2($diffV3->y, sqrt($diffV3->x ** 2 + $distDiff->z ** 2)));;
|
||||
}
|
||||
$currentB = $this->getLevel()->getBlock($this->asVector3());
|
||||
if($currentB instanceof \pocketmine\block\Liquid){ // in water, we need to get it floating
|
||||
$this->motionY = $this->gravity * 2;
|
||||
} else {
|
||||
// Check if the ghost is in air and not stuck in the ground. Then, we'll get the target block to check if it's possible to jump.
|
||||
if($currentB->canPassThought()){
|
||||
$targetB = $this->getTargetBlock(2);
|
||||
} else {
|
||||
$targetB = $currentB;
|
||||
}
|
||||
$canJump = true;
|
||||
// Check 3 blocks up that position (to see if the entity can go up)
|
||||
for($i = 1; $i <= 3; $i++){
|
||||
$blockUp = $targetB->asVector3();
|
||||
$blockUp->y += $i;
|
||||
if(!$this->getLevel()->getBlock($blockUp)->canPassThought()) $canJump = false;
|
||||
}
|
||||
// FInally, jump if possible
|
||||
if($canJump && $this->gravity * 3.2 > $this->motionY) $this->motionY = $this->gravity * 3.2;
|
||||
}
|
||||
$this->pitch = $y == 0 ? 0 : rad2deg(-atan2($y, sqrt($x * $x + $z * $z)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -284,10 +333,19 @@ class Ghost extends Human {
|
|||
* @return void
|
||||
*/
|
||||
public function attackEntity(Entity $et){
|
||||
$ev = new EntityDamageByEntityEvent($this, $this->associatedPlayer, EntityDamageEvent::CAUSE_ENTITY_ATTACK
|
||||
/* Todo Calcule armor */);
|
||||
if($et instanceof Player){
|
||||
$damage = [
|
||||
EntityDamageEvent::MODIFIER_BASE => 10 // Two hit a player which has no armor
|
||||
];
|
||||
$points = 0;
|
||||
foreach($et->getInventory()->getArmorContents() as $armorItem){
|
||||
$points += $armorItem->getDefensePoints();
|
||||
}
|
||||
$damage[EntityDamageEvent::MODIFIER_ARMOR] = -($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04);
|
||||
$ev = new EntityDamageByEntityEvent($this, $this->associatedPlayer, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage);
|
||||
$et->attack($ev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives some drops when the ghost die
|
||||
|
@ -295,7 +353,13 @@ class Ghost extends Human {
|
|||
* @return array
|
||||
*/
|
||||
public function getDrops() : array{
|
||||
return
|
||||
$it = Item::get(Item::GOLDEN_HOE, 0);
|
||||
$it->setCustomName("§r§cSoul Stealer");
|
||||
$it->setNamedTagEntry(new StringTag("customDamage", 10));
|
||||
$e = Enchantment::getEnchantment(Enchantment::SHARPNESS);
|
||||
$e->setLevel(10);
|
||||
$it->addEnchantment($e);
|
||||
return [$it];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,6 +33,87 @@ class TickTask extends PluginTask {
|
|||
public function onRun(int $tick) {
|
||||
foreach(self::$ghosts as $i => $g){
|
||||
self::$ghosts[$i]->currentSec += 0.1;
|
||||
switch(self::$ghosts[$i]->currentSec){
|
||||
case 48: // 0m48s
|
||||
self::$ghosts[$i]->getPlayer()->getLevel()->setTime(16000); // Set time to night
|
||||
break;
|
||||
case 64: // 1m04s
|
||||
self::$ghosts[$i]->blackOutEnterPhase();
|
||||
break;
|
||||
case 66: // 1m06s
|
||||
self::$ghosts[$i]->blackOutExitPhase();
|
||||
self::$ghosts[$i]->intenseFight();
|
||||
break;
|
||||
case 82: // 1m22s
|
||||
self::$ghosts[$i]->movePlayerRandomly();
|
||||
self::$ghosts[$i]->repeatFunc = "move";
|
||||
break;
|
||||
case 88: // 1m28s
|
||||
self::$ghosts[$i]->calmFight();
|
||||
self::$ghosts[$i]->repeatFunc = null;
|
||||
break;
|
||||
case 95: // 1m35s
|
||||
self::$ghosts[$i]->destroyBlocksRandomly();
|
||||
self::$ghosts[$i]->repeatFunc = "blockdis";
|
||||
break;
|
||||
case 100: // 1m40s
|
||||
self::$ghosts[$i]->repeatFunc = null;
|
||||
self::$ghosts[$i]->blackOutEnterPhase();
|
||||
break;
|
||||
case 103: // 1m43s
|
||||
self::$ghosts[$i]->blackOutExitPhase();
|
||||
self::$ghosts[$i]->intenseFight();
|
||||
break;
|
||||
case 136: // 2m16s
|
||||
self::$ghosts[$i]->calmFight();
|
||||
break;
|
||||
case 151: // 2m31s
|
||||
self::$ghosts[$i]->fightType = 0;
|
||||
break;
|
||||
case 152: // 2m32s
|
||||
self::$ghosts[$i]->blackOutEnterPhase();
|
||||
break;
|
||||
case 153: // 2m33s
|
||||
self::$ghosts[$i]->blackOutExitPhase();
|
||||
self::$ghosts[$i]->intenseFight();
|
||||
break;
|
||||
case 168: // 2m48s
|
||||
self::$ghosts[$i]->movePlayerRandomly();
|
||||
self::$ghosts[$i]->repeatFunc = "move";
|
||||
break;
|
||||
case 176: // 2m56s
|
||||
self::$ghosts[$i]->destroyBlocksRandomly();
|
||||
self::$ghosts[$i]->repeatFunc = "blockdis";
|
||||
break;
|
||||
case 183: // 3m03s
|
||||
self::$ghosts[$i]->repeatFunc = null;
|
||||
self::$ghosts[$i]->intenseFight();
|
||||
break;
|
||||
case 197: // 3m17s
|
||||
self::$ghosts[$i]->blackOutEnterPhase();
|
||||
break;
|
||||
case 198: // 3m18s
|
||||
self::$ghosts[$i]->blackOutExitPhase();
|
||||
self::$ghosts[$i]->intenseFight();
|
||||
break;
|
||||
case 227: // 3m47s
|
||||
self::$ghosts[$i]->calmFight();
|
||||
break;
|
||||
case 262: // 4m22
|
||||
self::unregisterGhost(self::$ghosts[$i]);
|
||||
self::$ghosts[$i]->close();
|
||||
break;
|
||||
default:
|
||||
switch(self::$ghosts[$i]->repeatFunc){
|
||||
case "move":
|
||||
self::$ghosts[$i]->movePlayerRandomly();
|
||||
break;
|
||||
case "blockdis":
|
||||
self::$ghosts[$i]->destroyBlocksRandomly();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +125,7 @@ class TickTask extends PluginTask {
|
|||
*/
|
||||
public static function registerGhost(Ghost $g){
|
||||
$g->currentSec = 0;
|
||||
$g->repeatFunc = null;
|
||||
self::$ghosts[$g->getId()] = $g;
|
||||
}
|
||||
|
||||
|
|
7
timing
7
timing
|
@ -17,10 +17,9 @@ Phase 7: 3:47 -> 4:22 (35 secs, more calm period)
|
|||
|
||||
If killed before the end, gets reward, otherwise, nothing happends.
|
||||
|
||||
Reward: Hat of despair:
|
||||
Pumpkin:
|
||||
Reward: Soul Stealer:
|
||||
Gold hoe (weapon):
|
||||
- Night vision,
|
||||
- when sneak, diseapears (!== invisibility, he cannot be seen nor hit),
|
||||
- if he sneaks more than 12 seconds, having nausea
|
||||
- If he strikes someone while being invisible, he'll loose invisility
|
||||
- if he sneaks more than 12 seconds, having nausea (high so that the screen shakes)
|
||||
- if he strikes someone otherwise, the person will get a big creepy sound
|
||||
|
|
Loading…
Reference in a new issue