From 4383e272eb48f1b076e0283c847b2865b20355c3 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sun, 15 Mar 2015 23:11:12 +0100 Subject: [PATCH] Added Effects base, /effect and methods --- src/pocketmine/Player.php | 23 +-- src/pocketmine/Server.php | 2 + src/pocketmine/block/Lava.php | 6 +- src/pocketmine/command/SimpleCommandMap.php | 2 + .../command/defaults/EffectCommand.php | 113 +++++++++++ .../command/defaults/GiveCommand.php | 2 +- src/pocketmine/entity/Effect.php | 187 ++++++++++++++++++ src/pocketmine/entity/Entity.php | 66 ++++++- src/pocketmine/entity/InstantEffect.php | 26 +++ src/pocketmine/entity/Living.php | 4 + src/pocketmine/network/protocol/Info.php | 2 +- .../network/protocol/MobEffectPacket.php | 60 ++++++ .../permission/DefaultPermissions.php | 1 + 13 files changed, 478 insertions(+), 16 deletions(-) create mode 100644 src/pocketmine/command/defaults/EffectCommand.php create mode 100644 src/pocketmine/entity/Effect.php create mode 100644 src/pocketmine/entity/InstantEffect.php create mode 100644 src/pocketmine/network/protocol/MobEffectPacket.php diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index f321a715d0..0277734938 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -24,6 +24,7 @@ namespace pocketmine; use pocketmine\block\Block; use pocketmine\command\CommandSender; use pocketmine\entity\Arrow; +use pocketmine\entity\Effect; use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Item as DroppedItem; @@ -955,6 +956,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->gamemode = $gm; $this->sendMessage("Your gamemode has been changed to " . Server::getGamemodeString($this->getGamemode()) . ".\n"); $this->inventory->clearAll(); + $this->inventory->sendContents($this); $this->inventory->sendContents($this->getViewers()); $this->inventory->sendHeldItem($this->hasSpawned); } @@ -1252,18 +1254,16 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->entityBaseTick(1); - if($currentTick % 80 === 0) $this->sendMessage(TextFormat::ITALIC . TextFormat::GRAY . "Y: ".round($this->y)."; biome: ". Biome::getBiome($this->level->getBiomeId((int) $this->x, (int) $this->z))->getName()); - - if($this->speed and $this->isSurvival()){ + if(!$this->forceMovement and $this->speed and $this->isSurvival()){ $speed = sqrt($this->speed->x ** 2 + $this->speed->z ** 2); if($speed > 0.45){ $this->highSpeedTicks += $speed > 3 ? 2 : 1; - if($this->highSpeedTicks > 40 and !$this->server->getAllowFlight()){ - $this->kick("Flying is not enabled on this server"); - return false; + if(!$this->hasEffect(Effect::SPEED) and $this->highSpeedTicks > 40 and !$this->server->getAllowFlight()){ + if($this->kick("Flying is not enabled on this server")){ + return false; + } }elseif($this->highSpeedTicks >= 10 and $this->highSpeedTicks % 4 === 0){ $this->forceMovement = $this->getPosition(); - $this->speed = null; } }elseif($this->highSpeedTicks > 0){ if($speed < 22){ @@ -1278,15 +1278,16 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->inAirTicks = 0; }else{ if($this->inAirTicks > 10 and $this->isSurvival() and !$this->isSleeping()){ - $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - 2)); + $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - 5)); $diff = sqrt(abs($this->speed->y - $expectedVelocity)); - if($diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ + if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ if($this->inAirTicks < 100){ $this->setMotion(new Vector3(0, $expectedVelocity, 0)); }else{ - $this->kick("Flying is not enabled on this server"); - return false; + if($this->kick("Flying is not enabled on this server")){ + return false; + } } } } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 47859dae24..a92bedf9a5 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -32,6 +32,7 @@ use pocketmine\command\ConsoleCommandSender; use pocketmine\command\PluginIdentifiableCommand; use pocketmine\command\SimpleCommandMap; use pocketmine\entity\Arrow; +use pocketmine\entity\Effect; use pocketmine\entity\Entity; use pocketmine\entity\FallingSand; use pocketmine\entity\Human; @@ -1605,6 +1606,7 @@ class Server{ Block::init(); Item::init(); Biome::init(); + Effect::init(); /** TODO: @deprecated */ TextWrapper::init(); $this->craftingManager = new CraftingManager(); diff --git a/src/pocketmine/block/Lava.php b/src/pocketmine/block/Lava.php index 5c1db3993b..37faaa1fa1 100644 --- a/src/pocketmine/block/Lava.php +++ b/src/pocketmine/block/Lava.php @@ -51,8 +51,10 @@ class Lava extends Liquid{ public function onEntityCollide(Entity $entity){ $entity->fallDistance *= 0.5; - $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4); - $entity->attack($ev->getFinalDamage(), $ev); + if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){ + $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4); + $entity->attack($ev->getFinalDamage(), $ev); + } $ev = new EntityCombustByBlockEvent($this, $entity, 15); Server::getInstance()->getPluginManager()->callEvent($ev); diff --git a/src/pocketmine/command/SimpleCommandMap.php b/src/pocketmine/command/SimpleCommandMap.php index 6eb69549c7..b3e3c4cab3 100644 --- a/src/pocketmine/command/SimpleCommandMap.php +++ b/src/pocketmine/command/SimpleCommandMap.php @@ -27,6 +27,7 @@ use pocketmine\command\defaults\BanListCommand; use pocketmine\command\defaults\DefaultGamemodeCommand; use pocketmine\command\defaults\DeopCommand; use pocketmine\command\defaults\DifficultyCommand; +use pocketmine\command\defaults\EffectCommand; use pocketmine\command\defaults\GamemodeCommand; use pocketmine\command\defaults\GiveCommand; use pocketmine\command\defaults\HelpCommand; @@ -98,6 +99,7 @@ class SimpleCommandMap implements CommandMap{ $this->register("pocketmine", new SaveOffCommand("save-off")); $this->register("pocketmine", new SaveCommand("save-all")); $this->register("pocketmine", new GiveCommand("give")); + $this->register("pocketmine", new EffectCommand("effect")); $this->register("pocketmine", new GamemodeCommand("gamemode")); $this->register("pocketmine", new KillCommand("kill")); $this->register("pocketmine", new SpawnpointCommand("spawnpoint")); diff --git a/src/pocketmine/command/defaults/EffectCommand.php b/src/pocketmine/command/defaults/EffectCommand.php new file mode 100644 index 0000000000..0746ca6c1e --- /dev/null +++ b/src/pocketmine/command/defaults/EffectCommand.php @@ -0,0 +1,113 @@ + [seconds] [amplifier]" + ); + $this->setPermission("pocketmine.command.effect"); + } + + public function execute(CommandSender $sender, $currentAlias, array $args){ + if(!$this->testPermission($sender)){ + return true; + } + + if(count($args) < 2){ + $sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage); + + return true; + } + + $player = $sender->getServer()->getPlayer($args[0]); + + if($player === null){ + $sender->sendMessage(TextFormat::RED . "Player {$args[0]} not found"); + return true; + } + + if(strtolower($args[1]) === "clear"){ + foreach($player->getEffects() as $effect){ + $player->removeEffect($effect->getId()); + } + + $sender->sendMessage("Took all effects from " . $player->getDisplayName()); + return true; + } + + $effect = Effect::getEffectByName($args[1]); + + if($effect === null){ + $effect = Effect::getEffect((int) $args[1]); + } + + if($effect === null){ + $sender->sendMessage(TextFormat::RED . "Effect {$args[1]} not found"); + return true; + } + + $duration = 300; + $amplification = 0; + + if(count($args) >= 3){ + $duration = (int) $args[2]; + if(!($effect instanceof InstantEffect)){ + $duration *= 20; + } + }elseif($effect instanceof InstantEffect){ + $duration = 1; + } + + if(count($args) >= 4){ + $amplification = (int) $args[3]; + } + + if($duration === 0){ + if(!$player->hasEffect($effect->getId())){ + $sender->sendMessage("Couldn't take ". $effect->getName() ." from ". $player->getDisplayName()); + return true; + } + + $player->removeEffect($effect->getId()); + $sender->sendMessage("Took ". $effect->getName() ." from ". $player->getDisplayName()); + }else{ + $effect->setDuration($duration)->setAmplifier($amplification); + + $player->addEffect($effect); + self::broadcastCommandMessage($sender, "Given ". $effect->getName() ." (ID ". $effect->getId()." ) * ". $effect->getAmplifier()." to ". $player->getDisplayName() ." for ". ($effect->getDuration() / 20) ." seconds"); + } + + + return true; + } +} \ No newline at end of file diff --git a/src/pocketmine/command/defaults/GiveCommand.php b/src/pocketmine/command/defaults/GiveCommand.php index c6c941d85e..e253da4b27 100644 --- a/src/pocketmine/command/defaults/GiveCommand.php +++ b/src/pocketmine/command/defaults/GiveCommand.php @@ -46,7 +46,7 @@ class GiveCommand extends VanillaCommand{ if(count($args) < 2){ $sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage); - return false; + return true; } $player = $sender->getServer()->getPlayer($args[0]); diff --git a/src/pocketmine/entity/Effect.php b/src/pocketmine/entity/Effect.php new file mode 100644 index 0000000000..082a63927f --- /dev/null +++ b/src/pocketmine/entity/Effect.php @@ -0,0 +1,187 @@ +id = $id; + $this->name = $name; + $this->isBad = (bool) $isBad; + } + + public function getName(){ + return $this->name; + } + + public function getId(){ + return $this->id; + } + + public function setDuration($ticks){ + $this->duration = $ticks; + return $this; + } + + public function getDuration(){ + return $this->duration; + } + + public function isVisible(){ + return $this->show; + } + + public function setVisible($bool){ + $this->show = (bool) $bool; + return $this; + } + + public function getAmplifier(){ + return $this->amplifier; + } + + public function setAmplifier($amplifier){ + $this->amplifier = $amplifier; + return $this; + } + + public function canTick(){ + switch($this->id){ + case Effect::POISON: + if(($interval = 25 >> $this->amplifier) > 0){ + return ($this->duration % $interval) === 0; + } + return true; + case Effect::WITHER: + if(($interval = 50 >> $this->amplifier) > 0){ + return ($this->duration % $interval) === 0; + } + return true; + case Effect::REGENERATION: + if(($interval = 40 >> $this->amplifier) > 0){ + return ($this->duration % $interval) === 0; + } + return true; + } + return false; + } + + public function applyEffect(Entity $entity){ + switch($this->id){ + case Effect::POISON: + if($entity->getHealth() > 1){ + $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1); + $entity->attack($ev->getFinalDamage(), $ev); + } + break; + + case Effect::WITHER: + $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1); + $entity->attack($ev->getFinalDamage(), $ev); + break; + + case Effect::REGENERATION: + if($entity->getHealth() < $entity->getMaxHealth()){ + $ev = new EntityRegainHealthEvent($entity, 1, EntityRegainHealthEvent::CAUSE_MAGIC); + $entity->heal($ev->getAmount(), $ev); + } + break; + } + } +} \ No newline at end of file diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 402c64975f..1ce1bfa343 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -51,6 +51,7 @@ use pocketmine\nbt\tag\Float; use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\String; use pocketmine\Network; +use pocketmine\network\protocol\MobEffectPacket; use pocketmine\network\protocol\MovePlayerPacket; use pocketmine\network\protocol\RemoveEntityPacket; use pocketmine\network\protocol\SetEntityDataPacket; @@ -75,6 +76,9 @@ abstract class Entity extends Location implements Metadatable{ */ protected $hasSpawned = []; + /** @var Effect[] */ + protected $effects = []; + protected $id; public $passenger = null; @@ -228,6 +232,53 @@ abstract class Entity extends Location implements Metadatable{ } + /** + * @return Effect[] + */ + public function getEffects(){ + return $this->effects; + } + + public function removeEffect($effectId){ + if(isset($this->effects[$effectId])){ + $pk = new MobEffectPacket(); + $pk->eid = $this->getId(); + $pk->eventId = MobEffectPacket::EVENT_REMOVE; + $pk->effectId = $effectId; + Server::broadcastPacket($this->getViewers(), $pk); + if($this instanceof Player){ + $this->dataPacket($pk); + } + + unset($this->effects[$effectId]); + } + } + + public function hasEffect($effectId){ + return isset($this->effects[$effectId]); + } + + public function addEffect(Effect $effect){ + $pk = new MobEffectPacket(); + $pk->eid = $this->getId(); + $pk->effectId = $effect->getId(); + $pk->amplifier = $effect->getAmplifier(); + $pk->particles = $effect->isVisible(); + $pk->duration = $effect->getDuration(); + if(isset($this->effects[$effect->getId()])){ + $pk->eventId = MobEffectPacket::EVENT_MODIFY; + }else{ + $pk->eventId = MobEffectPacket::EVENT_ADD; + } + + Server::broadcastPacket($this->getViewers(), $pk); + if($this instanceof Player){ + $this->dataPacket($pk); + } + + $this->effects[$effect->getId()] = $effect; + } + /** * @param int|string $type * @param FullChunk $chunk @@ -516,6 +567,7 @@ abstract class Entity extends Location implements Metadatable{ $isPlayer = $this instanceof Player; if($this->dead === true){ + $this->effects = []; $this->despawnFromAll(); if(!$isPlayer){ $this->close(); @@ -525,6 +577,18 @@ abstract class Entity extends Location implements Metadatable{ return false; } + if(count($this->effects) > 0){ + foreach($this->effects as $effect){ + if($effect->canTick()){ + $effect->applyEffect($this); + } + $effect->setDuration($effect->getDuration() - $tickDiff); + if($effect->getDuration() <= 0){ + $this->removeEffect($effect->getId()); + } + } + } + $hasUpdate = false; $this->checkBlockCollision(); @@ -542,7 +606,7 @@ abstract class Entity extends Location implements Metadatable{ $this->fireTicks = 0; } }else{ - if(($this->fireTicks % 20) === 0 or $tickDiff > 20){ + if(!$this->hasEffect(Effect::FIRE_RESISTANCE) and ($this->fireTicks % 20) === 0 or $tickDiff > 20){ $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1); $this->attack($ev->getFinalDamage(), $ev); } diff --git a/src/pocketmine/entity/InstantEffect.php b/src/pocketmine/entity/InstantEffect.php new file mode 100644 index 0000000000..708f39bee7 --- /dev/null +++ b/src/pocketmine/entity/InstantEffect.php @@ -0,0 +1,26 @@ +hasEffect(Effect::FIRE_RESISTANCE) and $source){ + return; + } + if($this->attackTime > 0 or $this->noDamageTicks > 0){ $lastCause = $this->getLastDamageCause(); if($lastCause instanceof EntityDamageEvent and $lastCause->getDamage() >= $damage){ diff --git a/src/pocketmine/network/protocol/Info.php b/src/pocketmine/network/protocol/Info.php index fb4ef1421d..7c14adef42 100644 --- a/src/pocketmine/network/protocol/Info.php +++ b/src/pocketmine/network/protocol/Info.php @@ -65,7 +65,7 @@ interface Info{ const LEVEL_EVENT_PACKET = 0x96; const TILE_EVENT_PACKET = 0x97; const ENTITY_EVENT_PACKET = 0x98; - //const MOB_EFFECT_PACKET = 0x99; + const MOB_EFFECT_PACKET = 0x99; const PLAYER_EQUIPMENT_PACKET = 0x9a; const PLAYER_ARMOR_EQUIPMENT_PACKET = 0x9b; diff --git a/src/pocketmine/network/protocol/MobEffectPacket.php b/src/pocketmine/network/protocol/MobEffectPacket.php new file mode 100644 index 0000000000..5040c8682d --- /dev/null +++ b/src/pocketmine/network/protocol/MobEffectPacket.php @@ -0,0 +1,60 @@ + + + +class MobEffectPacket extends DataPacket{ + public static $pool = []; + public static $next = 0; + + const EVENT_ADD = 1; + const EVENT_MODIFY = 2; + const EVENT_REMOVE = 3; + + public $eid; + public $eventId; + public $effectId; + public $amplifier; + public $particles = true; + public $duration; + + public function pid(){ + return Info::MOB_EFFECT_PACKET; + } + + public function decode(){ + + } + + public function encode(){ + $this->reset(); + $this->putLong($this->eid); + $this->putByte($this->eventId); + $this->putByte($this->effectId); + $this->putByte($this->amplifier); + $this->putByte($this->particles ? 1 : 0); + $this->putInt($this->duration); + } + +} diff --git a/src/pocketmine/permission/DefaultPermissions.php b/src/pocketmine/permission/DefaultPermissions.php index c2051e6312..2848291b49 100644 --- a/src/pocketmine/permission/DefaultPermissions.php +++ b/src/pocketmine/permission/DefaultPermissions.php @@ -97,6 +97,7 @@ abstract class DefaultPermissions{ self::registerPermission(new Permission(self::ROOT . ".command.tell", "Allows the user to privately message another player", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.say", "Allows the user to talk as the console", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.give", "Allows the user to give items to players", Permission::DEFAULT_OP), $commands); + self::registerPermission(new Permission(self::ROOT . ".command.effect", "Allows the user to give/take potion effects", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.teleport", "Allows the user to teleport players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.kick", "Allows the user to kick players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.stop", "Allows the user to stop the server", Permission::DEFAULT_OP), $commands);