diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 9b1dd4244..a0eb81597 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -24,13 +24,18 @@ namespace pocketmine; use pocketmine\block\Block; use pocketmine\command\CommandSender; use pocketmine\entity\DroppedItem; +use pocketmine\entity\Entity; use pocketmine\entity\Human; +use pocketmine\entity\Living; +use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\inventory\InventoryCloseEvent; use pocketmine\event\inventory\InventoryPickupItemEvent; use pocketmine\event\player\PlayerAchievementAwardedEvent; use pocketmine\event\player\PlayerAnimationEvent; use pocketmine\event\player\PlayerChatEvent; use pocketmine\event\player\PlayerCommandPreprocessEvent; +use pocketmine\event\player\PlayerDeathEvent; use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerItemConsumeEvent; @@ -69,6 +74,7 @@ use pocketmine\network\protocol\Info as ProtocolInfo; use pocketmine\network\protocol\LoginStatusPacket; use pocketmine\network\protocol\MessagePacket; use pocketmine\network\protocol\MovePlayerPacket; +use pocketmine\network\protocol\SetHealthPacket; use pocketmine\network\protocol\SetSpawnPositionPacket; use pocketmine\network\protocol\SetTimePacket; use pocketmine\network\protocol\StartGamePacket; @@ -1062,7 +1068,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $pk = new TakeItemEntityPacket; $pk->eid = $this->getID(); $pk->target = $entity->getID(); - $this->server->broadcastPacket($entity->getViewers(), $pk); + Server::broadcastPacket($entity->getViewers(), $pk); $this->inventory->addItem(clone $item); $entity->kill(); } @@ -1219,7 +1225,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $pk = new LoginStatusPacket; $pk->status = 0; - $this->directDataPacket($pk); + $this->dataPacket($pk); if(($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){ $this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); @@ -1236,17 +1242,21 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $pk->generator = 1; //0 old, 1 infinite, 2 flat $pk->gamemode = $this->gamemode & 0x01; $pk->eid = 0; //Always use EntityID as zero for the actual player - $this->directDataPacket($pk); + $this->dataPacket($pk); $pk = new SetTimePacket(); $pk->time = $this->getLevel()->getTime(); - $this->directDataPacket($pk); + $this->dataPacket($pk); $pk = new SetSpawnPositionPacket; $pk->x = (int) $this->spawnPosition->x; $pk->y = (int) $this->spawnPosition->y; $pk->z = (int) $this->spawnPosition->z; - $this->directDataPacket($pk); + $this->dataPacket($pk); + + $pk = new SetHealthPacket(); + $pk->health = $this->getHealth(); + $this->dataPacket($pk); $this->server->getLogger()->info(TextFormat::AQUA . $this->username . TextFormat::WHITE . "[/" . $this->ip . ":" . $this->port . "] logged in with entity id " . $this->id . " at (" . $this->getLevel()->getName() . ", " . round($this->x, 4) . ", " . round($this->y, 4) . ", " . round($this->z, 4) . ")"); @@ -1345,7 +1355,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->craftingType = 0; - if(($this->spawned === false or $this->blocked === true) and $packet->face >= 0 and $packet->face <= 5){ + if(($this->spawned === false or $this->blocked === true or $this->dead === true) and $packet->face >= 0 and $packet->face <= 5){ $target = $this->getLevel()->getBlock($blockVector); $block = $target->getSide($packet->face); @@ -1420,7 +1430,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::PLAYER_ACTION_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } @@ -1498,7 +1508,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::REMOVE_BLOCK_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } $this->craftingType = 0; @@ -1537,7 +1547,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } @@ -1571,95 +1581,112 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ //$this->entity->updateMetadata(); } break; - /*case ProtocolInfo::INTERACT_PACKET: + case ProtocolInfo::INTERACT_PACKET: if($this->spawned === false){ break; } $this->craftingType = 0; - $packet->eid = $this->id; - $data = []; - $data["target"] = $packet->target; - $data["eid"] = $packet->eid; - $data["action"] = $packet->action; - $target = Entity::get($packet->target); - if($target instanceof Entity and $this->gamemode !== VIEW and $this->blocked === false and ($target instanceof Entity) and $this->entity->distance($target) <= 8){ - $data["targetentity"] = $target; - $data["entity"] = $this->entity; - if($target instanceof Player and ($this->server->api->getProperty("pvp") == false or $this->server->difficulty <= 0 or ($target->player->gamemode & 0x01) === 0x01)){ - break; - }elseif($this->server->handle("player.interact", $data) !== false){ - $slot = $this->getSlot($this->getCurrentEquipment()); - switch($slot->getID()){ - case WOODEN_SWORD: - case GOLD_SWORD: - $damage = 4; - break; - case STONE_SWORD: - $damage = 5; - break; - case IRON_SWORD: - $damage = 6; - break; - case DIAMOND_SWORD: - $damage = 7; - break; - case WOODEN_AXE: - case GOLD_AXE: - $damage = 3; - break; - case STONE_AXE: - $damage = 4; - break; - case IRON_AXE: - $damage = 5; - break; - case DIAMOND_AXE: - $damage = 6; - break; + $target = $this->getLevel()->getEntity($packet->target); - case WOODEN_PICKAXE: - case GOLD_PICKAXE: - $damage = 2; - break; - case STONE_PICKAXE: - $damage = 3; - break; - case IRON_PICKAXE: - $damage = 4; - break; - case DIAMOND_PICKAXE: - $damage = 5; - break; + if($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->blocked === false and $this->dead !== true and $target->dead !== true){ + $cancelled = false; + $item = $this->inventory->getItemInHand(); + $damageTable = [ + Item::WOODEN_SWORD => 4, + Item::GOLD_SWORD => 4, + Item::STONE_SWORD => 5, + Item::IRON_SWORD => 6, + Item::DIAMOND_SWORD => 7, - case WOODEN_SHOVEL: - case GOLD_SHOVEL: - $damage = 1; - break; - case STONE_SHOVEL: - $damage = 2; - break; - case IRON_SHOVEL: - $damage = 3; - break; - case DIAMOND_SHOVEL: - $damage = 4; - break; + Item::WOODEN_AXE => 3, + Item::GOLD_AXE => 3, + Item::STONE_AXE => 3, + Item::IRON_AXE => 5, + Item::DIAMOND_AXE => 6, - default: - $damage = 1;//$this->server->difficulty; + Item::WOODEN_PICKAXE => 2, + Item::GOLD_PICKAXE => 2, + Item::STONE_PICKAXE => 3, + Item::IRON_PICKAXE => 4, + Item::DIAMOND_PICKAXE => 5, + + Item::WOODEN_SHOVEL => 1, + Item::GOLD_SHOVEL => 1, + Item::STONE_SHOVEL => 2, + Item::IRON_SHOVEL => 3, + Item::DIAMOND_SHOVEL => 4, + ]; + + $damage = [ + EntityDamageByEntityEvent::MODIFIER_BASE => isset($damageTable[$item->getID()]) ? $damageTable[$item->getID()] : 1, + ]; + + if($this->distance($target) > 8){ + $cancelled = true; + }elseif($target instanceof Player){ + if(($target->getGamemode() & 0x01) > 0){ + break; + }elseif($this->server->getConfigBoolean("pvp") === false or $this->server->getDifficulty() === 0){ + $cancelled = true; } - $target->harm($damage, $this->id); - if($slot->isTool() === true and ($this->gamemode & 0x01) === 0){ - if($slot->useOn($target) and $slot->getDamage() >= $slot->getMaxDurability()){ - $this->setSlot($this->getCurrentEquipment(), new Item(AIR, 0, 1)); + + $armorValues = [ + Item::LEATHER_CAP => 1, + Item::LEATHER_TUNIC => 3, + Item::LEATHER_PANTS => 2, + Item::LEATHER_BOOTS => 1, + Item::CHAIN_HELMET => 1, + Item::CHAIN_CHESTPLATE => 5, + Item::CHAIN_LEGGINGS => 4, + Item::CHAIN_BOOTS => 1, + Item::GOLD_HELMET => 1, + Item::GOLD_CHESTPLATE => 5, + Item::GOLD_LEGGINGS => 3, + Item::GOLD_BOOTS => 1, + Item::IRON_HELMET => 2, + Item::IRON_CHESTPLATE => 6, + Item::IRON_LEGGINGS => 5, + Item::IRON_BOOTS => 2, + Item::DIAMOND_HELMET => 3, + Item::DIAMOND_CHESTPLATE => 8, + Item::DIAMOND_LEGGINGS => 6, + Item::DIAMOND_BOOTS => 3, + ]; + $points = 0; + foreach($target->getInventory()->getArmorContents() as $index => $i){ + if(isset($armorValues[$i->getID()])){ + $points += $armorValues[$i->getID()]; } } + + $damage[EntityDamageEvent::MODIFIER_ARMOR] = -intval($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04); + } + + $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage); + if($cancelled){ + $ev->setCancelled(); + } + $this->server->getPluginManager()->callEvent($ev); + if($ev->isCancelled()){ + if($item->isTool() and ($this->gamemode & 0x01) === 0){ + $this->inventory->sendContents($this); + } + break; + } + + $target->attack($ev->getFinalDamage(), $ev); + + if($item->isTool() and ($this->gamemode & 0x01) === 0){ + if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){ + $this->inventory->setItemInHand(Item::get(Item::AIR, 0 ,1)); + } } } - break;*/ + + break; case ProtocolInfo::ANIMATE_PACKET: if($this->spawned === false){ break; @@ -1673,10 +1700,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $pk = new AnimatePacket(); $pk->eid = $this->getID(); $pk->action = $ev->getAnimationType(); - $this->server->broadcastPacket($this->getViewers(), $pk); + Server::broadcastPacket($this->getViewers(), $pk); break; case ProtocolInfo::RESPAWN_PACKET: - if($this->spawned === false or $this->dead === false){ + if($this->spawned === false or $this->dead !== true){ break; } $this->craftingType = 0; @@ -1686,19 +1713,22 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->teleport($ev->getRespawnPosition()); //$this->entity->fire = 0; //$this->entity->air = 300; - //$this->entity->setHealth(20, "respawn", true); + $this->setHealth(20); + $this->dead = false; //$this->entity->updateMetadata(); $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); + $this->spawnToAll(); + $this->blocked = false; break; case ProtocolInfo::SET_HEALTH_PACKET: //Not used break; case ProtocolInfo::ENTITY_EVENT_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } $this->craftingType = 0; @@ -1743,9 +1773,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $pk->event = 9; $this->dataPacket($pk); $pk->eid = $this->getID(); - $this->server->broadcastPacket($this->getViewers(), $pk); + Server::broadcastPacket($this->getViewers(), $pk); - $this->heal($items[$slot->getID()], "eating"); + $this->heal($items[$slot->getID()]); --$slot->count; $this->inventory->setItemInHand($slot); if($slot->getID() === Item::MUSHROOM_STEW or $slot->getID() === Item::BEETROOT_SOUP){ @@ -1756,15 +1786,12 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::DROP_ITEM_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } $packet->eid = $this->id; $item = $this->inventory->getItemInHand(); $ev = new PlayerDropItemEvent($this, $item); - if($this->blocked === true){ - $ev->setCancelled(true); - } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->inventory->sendContents($this); @@ -1817,7 +1844,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } @@ -1937,7 +1964,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ } break; case ProtocolInfo::ENTITY_DATA_PACKET: - if($this->spawned === false or $this->blocked === true){ + if($this->spawned === false or $this->blocked === true or $this->dead === true){ break; } $this->craftingType = 0; @@ -2091,6 +2118,72 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ return $this->username; } + public function kill(){ + parent::kill(); + $message = $this->getName() ." died"; + $cause = $this->getLastDamageCause(); + $ev = null; + if($cause instanceof EntityDamageEvent){ + $ev = $cause; + $cause = $ev->getCause(); + } + + switch($cause){ + case EntityDamageEvent::CAUSE_ENTITY_ATTACK: + if($ev instanceof EntityDamageByEntityEvent){ + $e = $ev->getDamager(); + if($e instanceof Player){ + $message = $this->getName() ." was killed by ".$e->getName(); + break; + }elseif($e instanceof Living){ + $message = $this->getName() ." was slain by ".$e->getName(); + break; + } + } + $message = $this->getName() ." was killed"; + break; + case EntityDamageEvent::CAUSE_SUICIDE: + + break; + case EntityDamageEvent::CAUSE_CONTACT: + case EntityDamageEvent::CAUSE_PROJECTILE: + case EntityDamageEvent::CAUSE_SUFFOCATION: + case EntityDamageEvent::CAUSE_FALL: + case EntityDamageEvent::CAUSE_FIRE: + case EntityDamageEvent::CAUSE_FIRE_TICK: + case EntityDamageEvent::CAUSE_LAVA: + case EntityDamageEvent::CAUSE_DROWNING: + case EntityDamageEvent::CAUSE_BLOCK_EXPLOSION: + case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION: + case EntityDamageEvent::CAUSE_VOID: + case EntityDamageEvent::CAUSE_MAGIC: + case EntityDamageEvent::CAUSE_CUSTOM: + + default: + + } + + $this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), $message)); + + $this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS); + + } + + public function setHealth($amount){ + parent::setHealth($amount); + $pk = new SetHealthPacket(); + $pk->health = $this->getHealth(); + $this->dataPacket($pk); + } + + public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ + $pk = new EntityEventPacket(); + $pk->eid = 0; + $pk->event = 2; + $this->dataPacket($pk); + parent::attack($damage, $source); + } + /** * @param Inventory $inventory diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 5bc29e85e..1cbe48aad 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1379,11 +1379,17 @@ class Server{ $version = new VersionString($this->getPocketMineVersion()); $this->logger->info("Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion()); - $this->logger->info("Loading properties..."); + $this->logger->info("Loading pocketmine.yml..."); if(!file_exists($this->dataPath . "pocketmine.yml")){ - @file_put_contents($this->dataPath . "pocketmine.yml", file_get_contents($this->filePath . "src/pocketmine/resources/pocketmine.yml")); + $content = file_get_contents($this->filePath . "src/pocketmine/resources/pocketmine.yml"); + if($version->isDev()){ + $content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content); + } + @file_put_contents($this->dataPath . "pocketmine.yml", $content); } $this->config = new Config($this->dataPath . "pocketmine.yml", Config::YAML, []); + + $this->logger->info("Loading server properties..."); $this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, array( "motd" => "Minecraft: PE Server", "server-port" => 19132, @@ -1555,7 +1561,7 @@ class Server{ * @param Player[] $players * @param DataPacket $packet */ - public function broadcastPacket(array $players, DataPacket $packet){ + public static function broadcastPacket(array $players, DataPacket $packet){ foreach($players as $player){ $player->dataPacket(clone $packet); } diff --git a/src/pocketmine/block/Door.php b/src/pocketmine/block/Door.php index 70f0bdcf8..6882c4eed 100644 --- a/src/pocketmine/block/Door.php +++ b/src/pocketmine/block/Door.php @@ -112,7 +112,7 @@ abstract class Door extends Transparent{ $pk->z = $this->z; $pk->evid = 1003; $pk->data = 0; - Server::getInstance()->broadcastPacket($players, $pk); + Server::broadcastPacket($players, $pk); return true; } @@ -131,7 +131,7 @@ abstract class Door extends Transparent{ $pk->z = $this->z; $pk->evid = 1003; $pk->data = 0; - Server::getInstance()->broadcastPacket($players, $pk); + Server::broadcastPacket($players, $pk); } return true; diff --git a/src/pocketmine/command/defaults/KillCommand.php b/src/pocketmine/command/defaults/KillCommand.php index 92d42d708..9e394b439 100644 --- a/src/pocketmine/command/defaults/KillCommand.php +++ b/src/pocketmine/command/defaults/KillCommand.php @@ -22,7 +22,9 @@ namespace pocketmine\command\defaults; use pocketmine\command\CommandSender; +use pocketmine\event\entity\EntityDamageEvent; use pocketmine\Player; +use pocketmine\Server; use pocketmine\utils\TextFormat; class KillCommand extends VanillaCommand{ @@ -45,9 +47,14 @@ class KillCommand extends VanillaCommand{ if($sender instanceof Player){ //TODO: EntityDamageEvent - //$ev->setLastDamageCause() + Server::getInstance()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000)); + + if($ev->isCancelled()){ + return true; + } + + $sender->setLastDamageCause($ev); $sender->setHealth(0); - //TODO: set update $sender->sendMessage("Ouch. That look like it hurt."); }else{ $sender->sendMessage(TextFormat::RED . "You can only perform this command as a player"); diff --git a/src/pocketmine/entity/DroppedItem.php b/src/pocketmine/entity/DroppedItem.php index 74ba872bc..5033d00af 100644 --- a/src/pocketmine/entity/DroppedItem.php +++ b/src/pocketmine/entity/DroppedItem.php @@ -21,6 +21,7 @@ namespace pocketmine\entity; +use pocketmine\event\entity\EntityDamageEvent; use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\Byte; @@ -106,11 +107,11 @@ class DroppedItem extends Entity{ return true; } - public function attack($damage, $source = "generic"){ + public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ } - public function heal($amount, $source = "generic"){ + public function heal($amount){ } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 572f7b6d6..d05bba658 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -25,6 +25,8 @@ namespace pocketmine\entity; use pocketmine\block\Block; +use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityDeathEvent; use pocketmine\event\entity\EntityDespawnEvent; use pocketmine\event\entity\EntityLevelChangeEvent; use pocketmine\event\entity\EntityMotionEvent; @@ -79,6 +81,8 @@ abstract class Entity extends Position implements Metadatable{ /** @var Chunk */ public $chunk; + protected $lastDamageCause = null; + public $lastX; public $lastY; public $lastZ; @@ -242,9 +246,15 @@ abstract class Entity extends Position implements Metadatable{ } } - abstract function attack($damage, $source = "generic"); + /** + * @param float $damage + * @param int|EntityDamageEvent $source + * + * @return mixed + */ + abstract function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC); - abstract function heal($amount, $source = "generic"); + abstract function heal($amount); /** * @return int @@ -259,9 +269,15 @@ abstract class Entity extends Position implements Metadatable{ * @param int $amount */ public function setHealth($amount){ - if($amount < 0){ + if($amount === $this->health){ + return; + } + + if($amount <= 0){ $this->health = 0; - $this->dead = true; + if($this->dead !== true){ + $this->kill(); + } }elseif($amount > $this->getMaxHealth()){ $this->health = $this->getMaxHealth(); }else{ @@ -269,6 +285,20 @@ abstract class Entity extends Position implements Metadatable{ } } + /** + * @param int|EntityDamageEvent $type + */ + public function setLastDamageCause($type){ + $this->lastDamageCause = $type; + } + + /** + * @return int|EntityDamageEvent|null + */ + public function getLastDamageCause(){ + return $this->lastDamageCause; + } + /** * @return int */ @@ -379,6 +409,8 @@ abstract class Entity extends Position implements Metadatable{ $this->close(); return false; + }elseif($this->dead === true){ + $this->despawnFromAll(); } $hasUpdate = false; @@ -447,7 +479,7 @@ abstract class Entity extends Position implements Metadatable{ [$this->id, $this->x, $this->y, $this->z, $this->yaw, $this->pitch] ]; } - $this->server->broadcastPacket($this->hasSpawned, $pk); + Server::broadcastPacket($this->hasSpawned, $pk); } if(!($this instanceof Player) and ($this->lastMotionX != $this->motionX or $this->lastMotionY != $this->motionY or $this->lastMotionZ != $this->motionZ)){ @@ -459,7 +491,7 @@ abstract class Entity extends Position implements Metadatable{ $pk->entities = [ [$this->getID(), $this->motionX, $this->motionY, $this->motionZ] ]; - $this->server->broadcastPacket($this->hasSpawned, $pk); + Server::broadcastPacket($this->hasSpawned, $pk); } } @@ -909,7 +941,14 @@ abstract class Entity extends Position implements Metadatable{ $this->motionY = $motion->y; $this->motionZ = $motion->z; if(!$this->justCreated){ - $this->scheduleUpdate(); + if($this instanceof Player){ + $pk = new SetEntityMotionPacket; + $pk->entities = [ + [0, $this->motionX, $this->motionY, $this->motionZ] + ]; + $this->dataPacket($pk); + } + $this->updateMovement(); } } @@ -918,6 +957,7 @@ abstract class Entity extends Position implements Metadatable{ } public function kill(){ + $this->setHealth(0); $this->dead = true; $this->scheduleUpdate(); } diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 51f7c1efe..1e7d4eded 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -75,6 +75,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ parent::initEntity(); } + public function getName(){ + return $this->nameTag; + } + public function saveNBT(){ parent::saveNBT(); $this->namedtag->Inventory = new Enum("Inventory", []); @@ -204,12 +208,4 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ return $d; } - public function attack($damage, $source = "generic"){ - - } - - public function heal($amount, $source = "generic"){ - - } - } diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index df5e4b7ea..71c08271d 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -22,6 +22,15 @@ namespace pocketmine\entity; +use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityDeathEvent; +use pocketmine\event\entity\EntityRegainHealthEvent; +use pocketmine\math\Vector3; +use pocketmine\network\protocol\EntityEventPacket; +use pocketmine\Server; +use pocketmine\item\Item; + abstract class Living extends Entity implements Damageable{ protected $gravity = 0.08; @@ -30,4 +39,47 @@ abstract class Living extends Entity implements Damageable{ protected function initEntity(){ } + + public abstract function getName(); + + public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ + //TODO: attack tick limit + $pk = new EntityEventPacket(); + $pk->eid = $this->getID(); + $pk->event = 2; //Ouch! + Server::broadcastPacket($this->hasSpawned, $pk); + $this->setLastDamageCause($source); + $motion = new Vector3(0, 0.25, 0); + if($source instanceof EntityDamageByEntityEvent){ + $e = $source->getDamager(); + $motion->x = -cos(deg2rad($e->pitch)) * sin(deg2rad($e->yaw)) * 0.5; + $motion->z = cos(deg2rad($e->pitch)) * sin(deg2rad($e->yaw)) * 0.5; + } + $this->setMotion($motion); + $this->setHealth($this->getHealth() - $damage); + + } + + public function heal($amount){ + $this->server->getPluginManager()->callEvent($ev = new EntityRegainHealthEvent($this, $amount)); + if($ev->isCancelled()){ + return; + } + $this->setHealth($this->getHealth() + $amount); + } + + public function kill(){ + parent::kill(); + $this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops())); + foreach($ev->getDrops() as $item){ + $this->getLevel()->dropItem($this, $item); + } + } + + /** + * @return Item[] + */ + public function getDrops(){ + return []; + } } \ No newline at end of file diff --git a/src/pocketmine/event/entity/EntityDamageByEntityEvent.php b/src/pocketmine/event/entity/EntityDamageByEntityEvent.php new file mode 100644 index 000000000..faf962a32 --- /dev/null +++ b/src/pocketmine/event/entity/EntityDamageByEntityEvent.php @@ -0,0 +1,53 @@ +damager = $damager; + parent::__construct($entity, $cause, $damage); + } + + /** + * @return Entity + */ + public function getDamager(){ + return $this->damager; + } + + +} \ No newline at end of file diff --git a/src/pocketmine/event/entity/EntityDamageEvent.php b/src/pocketmine/event/entity/EntityDamageEvent.php new file mode 100644 index 000000000..76c5e05b0 --- /dev/null +++ b/src/pocketmine/event/entity/EntityDamageEvent.php @@ -0,0 +1,147 @@ +entity = $entity; + $this->cause = $cause; + if(is_array($damage)){ + $this->modifiers = $damage; + }else{ + $this->modifiers = [ + self::MODIFIER_BASE => $damage + ]; + } + + $this->originals = $this->modifiers; + + if(!isset($this->modifiers[self::MODIFIER_BASE])){ + throw new \Exception("BASE Damage modifier missing"); + } + } + + /** + * @return int + */ + public function getCause(){ + return $this->cause; + } + + /** + * @param int $type + * + * @return int + */ + public function getOriginalDamage($type = self::MODIFIER_BASE){ + if(isset($this->originals[$type])){ + return $this->originals[$type]; + } + + return 0; + } + + /** + * @param int $type + * + * @return int + */ + public function getDamage($type = self::MODIFIER_BASE){ + if(isset($this->modifiers[$type])){ + return $this->modifiers[$type]; + } + + return 0; + } + + /** + * @param float $damage + * @param int $type + * + * @throws \UnexpectedValueException + */ + public function setDamage($damage, $type = self::MODIFIER_BASE){ + if(!isset($this->modifiers[$type])){ + throw new \UnexpectedValueException($type ." is not applicable to ".$this->getEntity()); + } + $this->modifiers[$type] = $damage; + } + + /** + * @param int $type + * + * @return bool + */ + public function isApplicable($type){ + return isset($this->modifiers[$type]); + } + + /** + * @return int + */ + public function getFinalDamage(){ + $damage = 0; + foreach($this->modifiers as $type => $d){ + $damage += $d; + } + return $damage; + } + +} \ No newline at end of file diff --git a/src/pocketmine/event/entity/EntityDeathEvent.php b/src/pocketmine/event/entity/EntityDeathEvent.php new file mode 100644 index 000000000..c61def730 --- /dev/null +++ b/src/pocketmine/event/entity/EntityDeathEvent.php @@ -0,0 +1,68 @@ +entity = $entity; + $this->drops = $drops; + } + + /** + * @return Living + */ + public function getEntity(){ + return $this->entity; + } + + /** + * @return \pocketmine\item\Item[] + */ + public function getDrops(){ + return $this->drops; + } + + /** + * @param Item[] $drops + */ + public function setDrops(array $drops){ + $this->drops = $drops; + } + +} \ No newline at end of file diff --git a/src/pocketmine/event/entity/EntityRegainHealthEvent.php b/src/pocketmine/event/entity/EntityRegainHealthEvent.php new file mode 100644 index 000000000..c7fad1d4f --- /dev/null +++ b/src/pocketmine/event/entity/EntityRegainHealthEvent.php @@ -0,0 +1,58 @@ +entity = $entity; + $this->amount = $amount; + } + + /** + * @return float + */ + public function getAmount(){ + return $this->amount; + } + + /** + * @param float $amount + */ + public function setAmount($amount){ + $this->amount = $amount; + } + +} \ No newline at end of file diff --git a/src/pocketmine/event/player/PlayerDeathEvent.php b/src/pocketmine/event/player/PlayerDeathEvent.php new file mode 100644 index 000000000..67eba844a --- /dev/null +++ b/src/pocketmine/event/player/PlayerDeathEvent.php @@ -0,0 +1,58 @@ +deathMessage = $deathMessage; + } + + /** + * @return Player + */ + public function getEntity(){ + return $this->entity; + } + + public function getDeathMessage(){ + return $this->deathMessage; + } + + public function setDeathMessage($deathMessage){ + $this->deathMessage = $deathMessage; + } + +} \ No newline at end of file diff --git a/src/pocketmine/inventory/ChestInventory.php b/src/pocketmine/inventory/ChestInventory.php index c5efeec72..acd04533b 100644 --- a/src/pocketmine/inventory/ChestInventory.php +++ b/src/pocketmine/inventory/ChestInventory.php @@ -48,7 +48,7 @@ class ChestInventory extends ContainerInventory{ $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; - Server::getInstance()->broadcastPacket($this->getHolder()->getLevel()->getPlayers(), $pk); + Server::broadcastPacket($this->getHolder()->getLevel()->getPlayers(), $pk); } } @@ -60,7 +60,7 @@ class ChestInventory extends ContainerInventory{ $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; - Server::getInstance()->broadcastPacket($this->getHolder()->getLevel()->getPlayers(), $pk); + Server::broadcastPacket($this->getHolder()->getLevel()->getPlayers(), $pk); } parent::onClose($who); } diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index e70cab8e1..733bba7d4 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -147,7 +147,7 @@ class Explosion{ $pk->z = $this->source->z; $pk->radius = $this->size; $pk->records = $send; - Server::getInstance()->broadcastPacket($this->level->getPlayers(), $pk); + Server::broadcastPacket($this->level->getPlayers(), $pk); } }