the whole game
This commit is contained in:
47
src/world/entity/player/Abilities.h
Executable file
47
src/world/entity/player/Abilities.h
Executable file
@@ -0,0 +1,47 @@
|
||||
#ifndef NET_MINECRAFT_WORLD_ENTITY_PLAYER__Abilities_H__
|
||||
#define NET_MINECRAFT_WORLD_ENTITY_PLAYER__Abilities_H__
|
||||
|
||||
//package net.minecraft.world.entity.player;
|
||||
|
||||
#include "../../../nbt/CompoundTag.h"
|
||||
|
||||
class Abilities
|
||||
{
|
||||
public:
|
||||
Abilities()
|
||||
: invulnerable(false),
|
||||
flying(false),
|
||||
mayfly(false),
|
||||
instabuild(false)
|
||||
{
|
||||
}
|
||||
|
||||
void addSaveData(CompoundTag* parentTag) {
|
||||
CompoundTag* tag = new CompoundTag();
|
||||
|
||||
tag->putBoolean("invulnerable", invulnerable);
|
||||
tag->putBoolean("flying", invulnerable);
|
||||
tag->putBoolean("mayfly", mayfly);
|
||||
tag->putBoolean("instabuild", instabuild);
|
||||
|
||||
parentTag->put("abilities", tag);
|
||||
}
|
||||
|
||||
void loadSaveData(CompoundTag* parentTag) {
|
||||
if (parentTag->contains("abilities")) {
|
||||
CompoundTag* tag = parentTag->getCompound("abilities");
|
||||
|
||||
invulnerable = tag->getBoolean("invulnerable");
|
||||
flying = tag->getBoolean("flying");
|
||||
mayfly = tag->getBoolean("mayfly");
|
||||
instabuild = tag->getBoolean("instabuild");
|
||||
}
|
||||
}
|
||||
|
||||
bool invulnerable;
|
||||
bool flying;
|
||||
bool mayfly;
|
||||
bool instabuild;
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_ENTITY_PLAYER__Abilities_H__*/
|
||||
359
src/world/entity/player/Inventory.cpp
Executable file
359
src/world/entity/player/Inventory.cpp
Executable file
@@ -0,0 +1,359 @@
|
||||
#include "Inventory.h"
|
||||
#include "../../level/material/Material.h"
|
||||
#include "../../level/tile/QuartzBlockTile.h"
|
||||
#include "../../level/tile/TreeTile.h"
|
||||
#include "../../level/tile/StoneSlabTile.h"
|
||||
#include "../../item/DyePowderItem.h"
|
||||
#include "../../item/crafting/Recipe.h"
|
||||
#include "../../item/CoalItem.h"
|
||||
#include "../../level/tile/SandStoneTile.h"
|
||||
|
||||
Inventory::Inventory( Player* player, bool creativeMode )
|
||||
: super( 36 + Inventory::MAX_SELECTION_SIZE,
|
||||
MAX_SELECTION_SIZE,
|
||||
ContainerType::INVENTORY,
|
||||
creativeMode),
|
||||
player(player),
|
||||
selected(0)
|
||||
{
|
||||
setupDefault();
|
||||
compressLinkedSlotList(0);
|
||||
}
|
||||
|
||||
Inventory::~Inventory() {
|
||||
}
|
||||
|
||||
ItemInstance* Inventory::getSelected() {
|
||||
return getLinked(selected);
|
||||
}
|
||||
|
||||
void Inventory::selectSlot( int slot ) {
|
||||
if (slot < MAX_SELECTION_SIZE && slot >= 0)
|
||||
selected = slot;
|
||||
}
|
||||
|
||||
bool Inventory::moveToSelectedSlot( int inventorySlot, bool propagate ) {
|
||||
return linkSlot(selected, inventorySlot, propagate);
|
||||
}
|
||||
|
||||
int Inventory::getSelectionSize() {
|
||||
return MAX_SELECTION_SIZE;
|
||||
}
|
||||
|
||||
void Inventory::setupDefault() {
|
||||
clearInventory();
|
||||
int Sel[MAX_SELECTION_SIZE] = {0};
|
||||
|
||||
#ifdef DEMO_MODE
|
||||
if (_isCreative) {
|
||||
Sel[0] = addItem(new ItemInstance(Item::shovel_stone));
|
||||
addItem(new ItemInstance(Item::pickAxe_stone));
|
||||
addItem(new ItemInstance(Item::hatchet_stone));
|
||||
addItem(new ItemInstance((Item*)Item::shears));
|
||||
addItem(new ItemInstance(Tile::ladder));
|
||||
Sel[3] = addItem(new ItemInstance(Tile::torch));
|
||||
addItem(new ItemInstance(Item::door_wood));
|
||||
|
||||
Sel[4] = addItem(new ItemInstance(Tile::stoneBrick));
|
||||
Sel[5] = addItem(new ItemInstance(Tile::wood));
|
||||
Sel[2] = addItem(new ItemInstance(Tile::redBrick));
|
||||
Sel[1] = addItem(new ItemInstance(Tile::dirt));
|
||||
addItem(new ItemInstance(Tile::sandStone));
|
||||
addItem(new ItemInstance(Tile::gravel));
|
||||
addItem(new ItemInstance(Tile::rock));
|
||||
addItem(new ItemInstance(Tile::sand));
|
||||
//addItem(new ItemInstance(Tile::clay));
|
||||
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 15));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 14));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 13));
|
||||
Sel[7] = addItem(new ItemInstance(Tile::cloth, 1, 12));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 11));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 10));
|
||||
Sel[8] = addItem(new ItemInstance(Tile::cloth, 1, 9));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 8));
|
||||
Sel[6] = addItem(new ItemInstance(Tile::glass));
|
||||
addItem(new ItemInstance(Tile::thinGlass));
|
||||
addItem(new ItemInstance(Tile::stairs_stone));
|
||||
addItem(new ItemInstance(Tile::bookshelf));
|
||||
addItem(new ItemInstance(Tile::workBench));
|
||||
addItem(new ItemInstance(Tile::chest));
|
||||
addItem(new ItemInstance(Tile::furnace));
|
||||
|
||||
addItem(new ItemInstance(((Tile*)Tile::flower)));
|
||||
addItem(new ItemInstance(Tile::cactus));
|
||||
|
||||
//
|
||||
// Those below are inactive due to demo
|
||||
//
|
||||
addItem(new ItemInstance(Item::sword_stone));
|
||||
addItem(new ItemInstance(Tile::treeTrunk, 1, 0));
|
||||
addItem(new ItemInstance(Tile::treeTrunk, 1, 1));
|
||||
addItem(new ItemInstance(Tile::treeTrunk, 1, 2));
|
||||
addItem(new ItemInstance(Tile::fence));
|
||||
addItem(new ItemInstance(Tile::fenceGate));
|
||||
addItem(new ItemInstance(Item::reeds));
|
||||
addItem(new ItemInstance(((Tile*)Tile::rose)));
|
||||
addItem(new ItemInstance(((Tile*)Tile::mushroom2)));
|
||||
addItem(new ItemInstance(((Tile*)Tile::mushroom1)));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 7));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 6));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 5));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 4));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 3));
|
||||
addItem(new ItemInstance(Tile::stairs_wood));
|
||||
addItem(new ItemInstance(Tile::goldBlock));
|
||||
addItem(new ItemInstance(Tile::ironBlock));
|
||||
addItem(new ItemInstance(Tile::emeraldBlock));
|
||||
addItem(new ItemInstance(Tile::lapisBlock));
|
||||
addItem(new ItemInstance(Tile::obsidian));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 0));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 1));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 2));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf));
|
||||
} else {
|
||||
#if defined(WIN32)
|
||||
// Survival
|
||||
addItem(new ItemInstance((Item*)Item::shears));
|
||||
addItem(new ItemInstance(Tile::redBrick));
|
||||
addItem(new ItemInstance(Tile::glass));
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if (_isCreative) {
|
||||
// Blocks
|
||||
Sel[1] = addItem(new ItemInstance(Tile::stoneBrick));
|
||||
addItem(new ItemInstance(Tile::stoneBrickSmooth, 1, 0));
|
||||
addItem(new ItemInstance(Tile::stoneBrickSmooth, 1, 1));
|
||||
addItem(new ItemInstance(Tile::stoneBrickSmooth, 1, 2));
|
||||
addItem(new ItemInstance(Tile::mossStone));
|
||||
Sel[5] = addItem(new ItemInstance(Tile::wood));
|
||||
Sel[2] = addItem(new ItemInstance(Tile::redBrick));
|
||||
|
||||
#ifdef RPI
|
||||
Sel[3] = addItem(new ItemInstance(Tile::rock));
|
||||
#else
|
||||
Sel[0] = addItem(new ItemInstance(Tile::rock));
|
||||
#endif
|
||||
Sel[4] = addItem(new ItemInstance(Tile::dirt));
|
||||
addItem(new ItemInstance(Tile::grass));
|
||||
addItem(new ItemInstance(Tile::clay));
|
||||
addItem(new ItemInstance(Tile::sandStone, 1, 0));
|
||||
addItem(new ItemInstance(Tile::sandStone, 1, 1));
|
||||
addItem(new ItemInstance(Tile::sandStone, 1, 2));
|
||||
addItem(new ItemInstance(Tile::sand));
|
||||
addItem(new ItemInstance(Tile::gravel));
|
||||
|
||||
Sel[7] = addItem(new ItemInstance(Tile::treeTrunk, 1, 0));
|
||||
addItem(new ItemInstance(Tile::treeTrunk, 1, 1));
|
||||
addItem(new ItemInstance(Tile::treeTrunk, 1, 2));
|
||||
addItem(new ItemInstance(Tile::netherBrick));
|
||||
addItem(new ItemInstance(Tile::netherrack));
|
||||
addItem(new ItemInstance(Tile::stairs_stone));
|
||||
addItem(new ItemInstance(Tile::stairs_wood));
|
||||
Sel[6] = addItem(new ItemInstance(Tile::stairs_brick));
|
||||
addItem(new ItemInstance(Tile::stairs_sandStone));
|
||||
addItem(new ItemInstance(Tile::stairs_stoneBrickSmooth));
|
||||
addItem(new ItemInstance(Tile::stairs_netherBricks));
|
||||
addItem(new ItemInstance(Tile::stairs_quartz));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::STONE_SLAB));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::COBBLESTONE_SLAB));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::WOOD_SLAB));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::BRICK_SLAB));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::SAND_SLAB));
|
||||
addItem(new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::SMOOTHBRICK_SLAB));
|
||||
|
||||
addItem(new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_DEFAULT));
|
||||
addItem(new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_LINES));
|
||||
addItem(new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_CHISELED));
|
||||
|
||||
|
||||
|
||||
// Ores
|
||||
addItem(new ItemInstance(Tile::coalOre));
|
||||
addItem(new ItemInstance(Tile::ironOre));
|
||||
addItem(new ItemInstance(Tile::goldOre));
|
||||
addItem(new ItemInstance(Tile::emeraldOre));
|
||||
addItem(new ItemInstance(Tile::lapisOre));
|
||||
addItem(new ItemInstance(Tile::redStoneOre));
|
||||
|
||||
addItem(new ItemInstance(Tile::goldBlock));
|
||||
addItem(new ItemInstance(Tile::ironBlock));
|
||||
addItem(new ItemInstance(Tile::emeraldBlock));
|
||||
addItem(new ItemInstance(Tile::lapisBlock));
|
||||
addItem(new ItemInstance(Tile::obsidian));
|
||||
addItem(new ItemInstance(Tile::snow));
|
||||
addItem(new ItemInstance(Tile::glass));
|
||||
addItem(new ItemInstance(Tile::lightGem));
|
||||
|
||||
addItem(new ItemInstance(Tile::netherReactor));
|
||||
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 0));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 7));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 6));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 5));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 4));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 3));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 2));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 1));
|
||||
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 15));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 14));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 13));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 12));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 11));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 10));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 9));
|
||||
addItem(new ItemInstance(Tile::cloth, 1, 8));
|
||||
addItem(new ItemInstance(Tile::ladder));
|
||||
#ifdef RPI
|
||||
addItem(new ItemInstance(Tile::torch));
|
||||
#else
|
||||
Sel[3] = addItem(new ItemInstance(Tile::torch));
|
||||
#endif
|
||||
addItem(new ItemInstance(Tile::thinGlass));
|
||||
|
||||
addItem(new ItemInstance(Item::door_wood));
|
||||
addItem(new ItemInstance(Tile::trapdoor));
|
||||
addItem(new ItemInstance(Tile::fence));
|
||||
addItem(new ItemInstance(Tile::fenceGate));
|
||||
|
||||
addItem(new ItemInstance(Item::bed));
|
||||
addItem(new ItemInstance(Tile::bookshelf));
|
||||
addItem(new ItemInstance(Item::painting));
|
||||
addItem(new ItemInstance(Tile::workBench));
|
||||
addItem(new ItemInstance(Tile::stonecutterBench));
|
||||
addItem(new ItemInstance(Tile::chest));
|
||||
addItem(new ItemInstance(Tile::furnace));
|
||||
addItem(new ItemInstance(Tile::tnt));
|
||||
|
||||
addItem(new ItemInstance(((Tile*)Tile::flower)));
|
||||
addItem(new ItemInstance(((Tile*)Tile::rose)));
|
||||
addItem(new ItemInstance(((Tile*)Tile::mushroom1)));
|
||||
addItem(new ItemInstance(((Tile*)Tile::mushroom2)));
|
||||
addItem(new ItemInstance(Tile::cactus));
|
||||
addItem(new ItemInstance(Tile::melon));
|
||||
addItem(new ItemInstance(Item::reeds));
|
||||
Sel[8] = addItem(new ItemInstance(Tile::sapling, 1, 0));
|
||||
addItem(new ItemInstance(Tile::sapling, 1, 1));
|
||||
addItem(new ItemInstance(Tile::sapling, 1, 2));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 0));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 1));
|
||||
addItem(new ItemInstance((Tile*)Tile::leaves, 1, 2));
|
||||
|
||||
addItem(new ItemInstance(Item::seeds_wheat));
|
||||
addItem(new ItemInstance(Item::seeds_melon));
|
||||
addItem(new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE));
|
||||
addItem(new ItemInstance(Item::hoe_iron));
|
||||
#ifdef RPI
|
||||
Sel[0] = addItem(new ItemInstance(Item::sword_iron));
|
||||
#else
|
||||
addItem(new ItemInstance(Item::sword_iron));
|
||||
#endif
|
||||
addItem(new ItemInstance(Item::bow));
|
||||
addItem(new ItemInstance(Item::sign));
|
||||
} else {
|
||||
#if defined(WIN32)
|
||||
// Survival
|
||||
addItem(new ItemInstance(Item::ironIngot, 64));
|
||||
addItem(new ItemInstance(Item::ironIngot, 34));
|
||||
addItem(new ItemInstance(Tile::stonecutterBench));
|
||||
addItem(new ItemInstance(Tile::workBench));
|
||||
addItem(new ItemInstance(Tile::furnace));
|
||||
addItem(new ItemInstance(Tile::wood, 54));
|
||||
addItem(new ItemInstance(Item::stick, 14));
|
||||
addItem(new ItemInstance(Item::coal, 31));
|
||||
addItem(new ItemInstance(Tile::sand, 6));
|
||||
addItem(new ItemInstance(Item::dye_powder, 23, DyePowderItem::PURPLE));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned int i = 0; i < items.size(); ++i) {
|
||||
ItemInstance* item = items[i];
|
||||
|
||||
if (i < MAX_SELECTION_SIZE) {
|
||||
if (item)
|
||||
LOGE("Error: Should not have items on slot %i\n", i);
|
||||
|
||||
items[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item && _isCreative)
|
||||
item->count = 5;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_SELECTION_SIZE; ++i) {
|
||||
linkedSlots[i] = LinkedSlot(Sel[i]);
|
||||
}
|
||||
|
||||
//LOGI("Inventory has %d items\n", (int)items.size());
|
||||
}
|
||||
|
||||
void Inventory::clearInventoryWithDefault()
|
||||
{
|
||||
clearInventory();
|
||||
setupDefault();
|
||||
}
|
||||
|
||||
int Inventory::getAttackDamage( Entity* entity )
|
||||
{
|
||||
ItemInstance* item = getSelected();
|
||||
if (item != NULL) return item->getAttackDamage(entity);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Inventory::canDestroy( Tile* tile )
|
||||
{
|
||||
if (tile->material->isAlwaysDestroyable()) return true;
|
||||
|
||||
ItemInstance* item = getSelected();
|
||||
if (item != NULL) return item->canDestroySpecial(tile);
|
||||
return false;
|
||||
}
|
||||
|
||||
float Inventory::getDestroySpeed( Tile* tile )
|
||||
{
|
||||
ItemInstance* item = getSelected();
|
||||
if (item && item->id >= 256) {
|
||||
return Item::items[item->id]->getDestroySpeed(NULL, tile);
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
bool Inventory::moveToSelectionSlot( int selectionSlot, int inventorySlot, bool propagate ) {
|
||||
return linkSlot(selectionSlot, inventorySlot, propagate);
|
||||
}
|
||||
|
||||
bool Inventory::moveToEmptySelectionSlot( int inventorySlot ) {
|
||||
return linkEmptySlot(inventorySlot);
|
||||
}
|
||||
|
||||
void Inventory::doDrop( ItemInstance* item, bool randomly )
|
||||
{
|
||||
player->drop(item, randomly);
|
||||
}
|
||||
|
||||
bool Inventory::stillValid(Player* player) {
|
||||
if (this->player->removed) return false;
|
||||
if (player->distanceToSqr(this->player) > 8 * 8) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Inventory::add( ItemInstance* item ){
|
||||
if (_isCreative || player->hasFakeInventory)
|
||||
return true;
|
||||
|
||||
return super::add(item);
|
||||
}
|
||||
|
||||
bool Inventory::removeItem( const ItemInstance* samePtr ) {
|
||||
for (int i = MAX_SELECTION_SIZE; i < (int)items.size(); ++i) {
|
||||
if (items[i] == samePtr) {
|
||||
clearSlot(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
57
src/world/entity/player/Inventory.h
Executable file
57
src/world/entity/player/Inventory.h
Executable file
@@ -0,0 +1,57 @@
|
||||
#ifndef NET_MINECRAFT_WORLD_ENTITY_PLAYER__JInventory_H__
|
||||
#define NET_MINECRAFT_WORLD_ENTITY_PLAYER__JInventory_H__
|
||||
|
||||
//package net.minecraft.world.entity.player;
|
||||
|
||||
#include "../../inventory/FillingContainer.h"
|
||||
#include <vector>
|
||||
|
||||
class Tile;
|
||||
class Entity;
|
||||
class Player;
|
||||
class ListTag;
|
||||
class CompoundTag;
|
||||
|
||||
class Inventory: public FillingContainer
|
||||
{
|
||||
typedef FillingContainer super;
|
||||
public:
|
||||
static const int INVENTORY_SIZE_DEMO = 27;
|
||||
static const int MAX_SELECTION_SIZE = 9; // Including "More..." right now
|
||||
static const int POP_TIME_DURATION = 5;
|
||||
|
||||
Inventory(Player* player, bool creativeMode);
|
||||
~Inventory();
|
||||
|
||||
void clearInventoryWithDefault();
|
||||
//
|
||||
// Selection slots
|
||||
//
|
||||
void selectSlot(int slot);
|
||||
ItemInstance* getSelected();
|
||||
|
||||
static int getSelectionSize();
|
||||
// Special for this "selection based" inventory
|
||||
bool moveToSelectionSlot(int selectionSlot, int inventorySlot, bool propagate);
|
||||
bool moveToSelectedSlot(int inventorySlot, bool propagate);
|
||||
bool moveToEmptySelectionSlot(int inventorySlot);
|
||||
|
||||
bool removeItem(const ItemInstance* samePtr);
|
||||
|
||||
void doDrop(ItemInstance* item, bool randomly);
|
||||
bool stillValid(Player* player);
|
||||
bool add(ItemInstance* item);
|
||||
|
||||
int getAttackDamage(Entity* entity);
|
||||
float getDestroySpeed(Tile* tile);
|
||||
bool canDestroy(Tile* tile);
|
||||
private:
|
||||
void setupDefault();
|
||||
public:
|
||||
//ItemList armor;
|
||||
|
||||
int selected;
|
||||
Player* player;
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_ENTITY_PLAYER__JInventory_H__*/
|
||||
9
src/world/entity/player/InventorySlotManager.h
Executable file
9
src/world/entity/player/InventorySlotManager.h
Executable file
@@ -0,0 +1,9 @@
|
||||
#ifndef NET_MINECRAFT_WORLD_ENTITY_PLAYER__InventorySlotManager_H__
|
||||
#define NET_MINECRAFT_WORLD_ENTITY_PLAYER__InventorySlotManager_H__
|
||||
|
||||
//package net.minecraft.world.entity.player;
|
||||
|
||||
class InventorySlotManager {
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_ENTITY_PLAYER__InventorySlotManager_H__*/
|
||||
44
src/world/entity/player/OldInventory.h
Executable file
44
src/world/entity/player/OldInventory.h
Executable file
@@ -0,0 +1,44 @@
|
||||
#ifndef NET_MINECRAFT_WORLD_ENTITY_PLAYER__Inventory_H__
|
||||
#define NET_MINECRAFT_WORLD_ENTITY_PLAYER__Inventory_H__
|
||||
|
||||
//package net.minecraft.world.entity.player;
|
||||
|
||||
class Player;
|
||||
class Tile;
|
||||
|
||||
class Inventory
|
||||
{
|
||||
public:
|
||||
static const int POP_TIME_DURATION = 5;
|
||||
static const int MAX_SELECTION_SIZE = 9; // Including "More..." right now
|
||||
|
||||
static const int INVENTORY_ROWS = 5;
|
||||
static const int INVENTORY_COLS = 9;
|
||||
static const int INVENTORY_SIZE = INVENTORY_COLS * INVENTORY_ROWS;
|
||||
static const int INVENTORY_SIZE_DEMO = 18;
|
||||
|
||||
Inventory(Player* player_);
|
||||
|
||||
void selectSlot(int slot);
|
||||
|
||||
int getSelectedItemId();
|
||||
int getSelectionSlotItemId(int slot);
|
||||
|
||||
void setSelectionSlotItemId(int slot, int id);
|
||||
|
||||
float getDestroySpeed(Tile* tile);
|
||||
|
||||
|
||||
//int getCurrentSelectionSize();
|
||||
//void setCurrentSelectionSize(int size) { _selectionSize = size; }
|
||||
|
||||
int selected;
|
||||
protected:
|
||||
//int _selectionSize;
|
||||
Player* player;
|
||||
|
||||
int itemIds[MAX_SELECTION_SIZE];
|
||||
int inventoryIds[INVENTORY_SIZE];
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_ENTITY_PLAYER__Inventory_H__*/
|
||||
924
src/world/entity/player/Player.cpp
Executable file
924
src/world/entity/player/Player.cpp
Executable file
@@ -0,0 +1,924 @@
|
||||
#include "Player.h"
|
||||
#include "Inventory.h"
|
||||
#include "../item/ItemEntity.h"
|
||||
#include "../../level/Level.h"
|
||||
#include "../../item/ItemInstance.h"
|
||||
#include "../../item/BowItem.h"
|
||||
#include "../../inventory/BaseContainerMenu.h"
|
||||
#include "../../../nbt/CompoundTag.h"
|
||||
|
||||
#include "../../../network/RakNetInstance.h"
|
||||
#include "../../../network/packet/AnimatePacket.h"
|
||||
#include "../../inventory/FurnaceMenu.h"
|
||||
#include "../SharedFlags.h"
|
||||
#include "../../level/tile/BedTile.h"
|
||||
#include "../../Direction.h"
|
||||
#include "../EntityEvent.h"
|
||||
#include "../../Difficulty.h"
|
||||
#include "../../item/ArmorItem.h"
|
||||
|
||||
const float Player::DEFAULT_WALK_SPEED = 0.1f;
|
||||
const float Player::DEFAULT_FLY_SPEED = 0.02f;
|
||||
|
||||
// @todo: Move out to ArmorInventory
|
||||
static ListTag* saveArmor(ItemInstance* armor);
|
||||
static void loadArmor(ItemInstance* armor, ListTag* listTag);
|
||||
|
||||
Player::Player(Level* level, bool isCreative)
|
||||
: super(level),
|
||||
userType(0),
|
||||
playerHasRespawnPosition(false),
|
||||
hasFakeInventory(false),
|
||||
containerMenu(NULL),
|
||||
useItemDuration(0),
|
||||
playerIsSleeping(false),
|
||||
sleepCounter(0),
|
||||
bedOffsetX(0),
|
||||
bedOffsetY(0),
|
||||
bedOffsetZ(0),
|
||||
respawnPosition(0, -1, 0),
|
||||
allPlayersSleeping(false)
|
||||
{
|
||||
canRemove = false;
|
||||
|
||||
_init();
|
||||
entityRendererId = ER_PLAYER_RENDERER;
|
||||
|
||||
autoSendPosRot = false;
|
||||
inventory = new Inventory(this, isCreative);
|
||||
|
||||
//inventoryMenu = /*new*/ InventoryMenu(inventory, !level.isOnline);
|
||||
//containerMenu = inventoryMenu;
|
||||
|
||||
heightOffset = 1.62f;
|
||||
|
||||
Pos spawnPos = level->getSharedSpawnPos();
|
||||
this->moveTo((float)spawnPos.x + 0.5f, (float)(spawnPos.y + 1), (float)spawnPos.z + 0.5f, 0, 0);
|
||||
|
||||
health = MAX_HEALTH;
|
||||
modelName = "humanoid";
|
||||
rotOffs = 180;
|
||||
flameTime = 20;
|
||||
|
||||
textureName = "mob/char.png";
|
||||
entityData.define(DATA_PLAYER_FLAGS_ID, (PlayerFlagIDType) 0);
|
||||
entityData.define(DATA_BED_POSITION_ID, Pos());
|
||||
//entityData.define(DATA_PLAYER_RUNNING_ID, (SynchedEntityData::TypeChar) 0);
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
delete inventory;
|
||||
}
|
||||
bool Player::isSleeping() {
|
||||
return playerIsSleeping;
|
||||
}
|
||||
int Player::startSleepInBed( int x, int y, int z ) {
|
||||
if(!level->isClientSide) {
|
||||
if(isSleeping() || !isAlive()) {
|
||||
return BedSleepingResult::OTHER_PROBLEM;
|
||||
}
|
||||
if(Mth::abs(this->x - x) > 3 || Mth::abs(this->y - y) > 4 || Mth::abs(this->z - z) > 3) {
|
||||
return BedSleepingResult::TOO_FAR_AWAY;
|
||||
}
|
||||
if(level->dimension->isNaturalDimension()) {
|
||||
return BedSleepingResult::NOT_POSSIBLE_HERE;
|
||||
}
|
||||
if(level->isDay()) {
|
||||
return BedSleepingResult::NOT_POSSIBLE_NOW;
|
||||
}
|
||||
float hRange = 8;
|
||||
float vRange = 5;
|
||||
EntityList monsters;
|
||||
level->getEntitiesOfClass(MobTypes::BaseEnemy, AABB(x- hRange, y - vRange, z - hRange, x + hRange, y + vRange, z + hRange), monsters);
|
||||
if(!monsters.empty()) {
|
||||
return BedSleepingResult::NOT_SAFE;
|
||||
}
|
||||
}
|
||||
|
||||
setSize(0.2f, 0.2f);
|
||||
heightOffset = 0.2f;
|
||||
if(level->hasChunkAt(x, y, z)) {
|
||||
int data = level->getData(x, y, z);
|
||||
int direction = BedTile::getDirection(data);
|
||||
float xo = 0.5f, zo = 0.5f;
|
||||
switch(direction) {
|
||||
case Direction::SOUTH:
|
||||
zo = 0.9f;
|
||||
break;
|
||||
case Direction::NORTH:
|
||||
zo = 0.1f;
|
||||
break;
|
||||
case Direction::WEST:
|
||||
xo = 0.1f;
|
||||
break;
|
||||
case Direction::EAST:
|
||||
xo = 0.9f;
|
||||
break;
|
||||
}
|
||||
setBedOffset(direction);
|
||||
setPos(x + xo, y + 15.0f / 16.0f, z + zo);
|
||||
} else {
|
||||
setPos(x + 0.5f, y + 1.0f / 16.0f, z + 0.5f);
|
||||
}
|
||||
playerIsSleeping = true;
|
||||
sleepCounter = 0;
|
||||
bedPosition = Pos(x, y, z);
|
||||
xd = zd = yd = 0;
|
||||
if(!level->isClientSide) {
|
||||
level->updateSleepingPlayerList();
|
||||
}
|
||||
entityData.set<Pos>(DATA_BED_POSITION_ID, bedPosition);
|
||||
entityData.setFlag<SharedFlagsInformation::SharedFlagsInformationType>(DATA_PLAYER_FLAGS_ID, PLAYER_SLEEP_FLAG);
|
||||
return BedSleepingResult::OK;
|
||||
}
|
||||
|
||||
void Player::stopSleepInBed( bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint ) {
|
||||
if(!isSleeping())
|
||||
return;
|
||||
setSize(0.6f, 1.8f);
|
||||
setDefaultHeadHeight();
|
||||
Pos standUp = bedPosition;
|
||||
if(level->getTile(int(bedPosition.x), int(bedPosition.y), int(bedPosition.z)) == Tile::bed->id) {
|
||||
BedTile::setOccupied(level, int(bedPosition.x), int(bedPosition.y), int(bedPosition.z), false);
|
||||
bool foundStandUpPosition = BedTile::findStandUpPosition(level, int(bedPosition.x), int(bedPosition.y), int(bedPosition.z), 0, standUp);
|
||||
if(!foundStandUpPosition) {
|
||||
standUp = Pos(bedPosition.x, bedPosition.y, bedPosition.z);
|
||||
}
|
||||
setPos(standUp.x + 0.5f, standUp.y + heightOffset + 0.1f, standUp.z + 0.5f);
|
||||
}
|
||||
playerIsSleeping = false;
|
||||
if(!level->isClientSide && updateLevelList) {
|
||||
level->updateSleepingPlayerList();
|
||||
}
|
||||
if(forcefulWakeUp) {
|
||||
sleepCounter = 0;
|
||||
} else {
|
||||
sleepCounter = SLEEP_DURATION;
|
||||
}
|
||||
// Quick fix to make the spawn position always saved, not sure if we always want to save this position but I like it.
|
||||
if(true || saveRespawnPoint) {
|
||||
Pos newRespawnPos;
|
||||
BedTile::findStandUpPosition(level, bedPosition.x, bedPosition.y, bedPosition.z, 0, newRespawnPos);
|
||||
setRespawnPosition(newRespawnPos);
|
||||
}
|
||||
entityData.clearFlag<SharedFlagsInformation::SharedFlagsInformationType>(DATA_PLAYER_FLAGS_ID, PLAYER_SLEEP_FLAG);
|
||||
allPlayersSleeping = false;
|
||||
}
|
||||
|
||||
int Player::getSleepTimer() {
|
||||
return allPlayersSleeping ? sleepCounter : 0;
|
||||
}
|
||||
void Player::setAllPlayersSleeping() {
|
||||
sleepCounter = 0;
|
||||
allPlayersSleeping = true;
|
||||
}
|
||||
void Player::setBedOffset( int bedDirection ) {
|
||||
bedOffsetX = 0;
|
||||
bedOffsetZ = 0;
|
||||
switch(bedDirection) {
|
||||
case Direction::SOUTH:
|
||||
bedOffsetZ = -1.8f;
|
||||
break;
|
||||
case Direction::NORTH:
|
||||
bedOffsetZ = 1.8f;
|
||||
break;
|
||||
case Direction::WEST:
|
||||
bedOffsetX = 1.8f;
|
||||
break;
|
||||
case Direction::EAST:
|
||||
bedOffsetX = -1.8f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool Player::isSleepingLongEnough() {
|
||||
return isSleeping() && sleepCounter >= SLEEP_DURATION;
|
||||
}
|
||||
|
||||
float Player::getSleepRotation() {
|
||||
if(isSleeping()) {
|
||||
int data = level->getData(bedPosition.x, bedPosition.y, bedPosition.z);
|
||||
int direction = BedTile::getDirection(data);
|
||||
switch(direction) {
|
||||
case Direction::SOUTH:
|
||||
return 90;
|
||||
case Direction::WEST:
|
||||
return 0;
|
||||
case Direction::NORTH:
|
||||
return 270;
|
||||
case Direction::EAST:
|
||||
return 180;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Player::checkBed() {
|
||||
return (level->getTile(bedPosition.x, bedPosition.y, bedPosition.z) == Tile::bed->id);
|
||||
}
|
||||
|
||||
void Player::tick() {
|
||||
bool shouldSleep = entityData.getFlag<SharedFlagsInformation::SharedFlagsInformationType>(DATA_PLAYER_FLAGS_ID, PLAYER_SLEEP_FLAG);
|
||||
if(shouldSleep != isSleeping()) {
|
||||
if(isSleeping()) {
|
||||
stopSleepInBed(true, true, true);
|
||||
} else {
|
||||
bedPosition = entityData.getPos(DATA_BED_POSITION_ID);
|
||||
startSleepInBed(bedPosition.x, bedPosition.y, bedPosition.z);
|
||||
}
|
||||
}
|
||||
if(isSleeping()) {
|
||||
sleepCounter++;
|
||||
if(sleepCounter > SLEEP_DURATION) {
|
||||
sleepCounter = SLEEP_DURATION;
|
||||
}
|
||||
if(!level->isClientSide) {
|
||||
if(!checkBed()) {
|
||||
stopSleepInBed(true, true, false);
|
||||
} else if(level->isDay()) {
|
||||
stopSleepInBed(false, true, true);
|
||||
}
|
||||
}
|
||||
} else if(sleepCounter > 0) {
|
||||
sleepCounter++;
|
||||
if(sleepCounter >= (SLEEP_DURATION + WAKE_UP_DURATION)) {
|
||||
sleepCounter = 0;
|
||||
}
|
||||
}
|
||||
super::tick();
|
||||
|
||||
if (!level->isClientSide) {
|
||||
foodData.tick(this);
|
||||
// if (containerMenu != NULL && !containerMenu->stillValid(this)) {
|
||||
// closeContainer();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
int Player::getMaxHealth() {
|
||||
return MAX_HEALTH;
|
||||
}
|
||||
|
||||
//
|
||||
// Use items
|
||||
//
|
||||
bool Player::isUsingItem() {
|
||||
return !useItem.isNull();
|
||||
}
|
||||
|
||||
ItemInstance* Player::getUseItem() {
|
||||
return &useItem;
|
||||
}
|
||||
|
||||
void Player::spawnEatParticles(const ItemInstance* useItem, int count) {
|
||||
if (useItem->getUseAnimation() == UseAnim::drink) {
|
||||
level->playSound(this, "random.drink", 0.5f, level->random.nextFloat() * 0.1f + 0.9f);
|
||||
}
|
||||
else if (useItem->getUseAnimation() == UseAnim::eat) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
const float xx = -xRot * Mth::PI / 180;
|
||||
const float yy = -yRot * Mth::PI / 180;
|
||||
Vec3 d((random.nextFloat() - 0.5f) * 0.1f, Mth::random() * 0.1f + 0.1f, 0);
|
||||
d.xRot(xx);
|
||||
d.yRot(yy);
|
||||
Vec3 p((random.nextFloat() - 0.5f) * 0.3f, -random.nextFloat() * 0.6f - 0.3f, 0.6f);
|
||||
p.xRot(xx);
|
||||
p.yRot(yy);
|
||||
p = p.add(x, y + getHeadHeight(), z);
|
||||
level->addParticle(PARTICLETYPE(iconcrack), p.x, p.y, p.z, d.x, d.y + 0.05f, d.z, useItem->getItem()->id);
|
||||
}
|
||||
level->playSound(this, "random.eat", .5f + .5f * random.nextInt(2), (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::startUsingItem(ItemInstance instance, int duration) {
|
||||
if(instance == useItem) return;
|
||||
useItem = instance;
|
||||
useItemDuration = duration;
|
||||
if(!level->isClientSide) {
|
||||
setSharedFlag(SharedFlagsInformation::FLAG_USINGITEM, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::stopUsingItem() {
|
||||
if(getCarriedItem() != NULL && useItem.id == getCarriedItem()->id)
|
||||
getCarriedItem()->setAuxValue(useItem.getAuxValue());
|
||||
useItem.setNull();
|
||||
useItemDuration = 0;
|
||||
if(!level->isClientSide) {
|
||||
setSharedFlag(SharedFlagsInformation::FLAG_USINGITEM, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::releaseUsingItem() {
|
||||
if(!useItem.isNull()) {
|
||||
useItem.releaseUsing(level, this, useItemDuration);
|
||||
}
|
||||
stopUsingItem();
|
||||
}
|
||||
|
||||
void Player::completeUsingItem() {
|
||||
if(!useItem.isNull()) {
|
||||
spawnEatParticles(&useItem, 10);
|
||||
|
||||
// Check if the item is valid, and if we should overwrite the
|
||||
// inventory item afterwards.
|
||||
ItemInstance* selected = inventory->getSelected();
|
||||
bool doOverwrite = selected && ItemInstance::matches(&useItem, selected);
|
||||
|
||||
ItemInstance itemInstance = useItem.useTimeDepleted(level, this);
|
||||
|
||||
if (doOverwrite) {
|
||||
*selected = useItem;
|
||||
if (selected->count == 0)
|
||||
inventory->clearSlot(inventory->selected);
|
||||
}
|
||||
|
||||
/*
|
||||
int oldCount = useItem.count;
|
||||
if (!itemInstance.matches(&useItem)) {
|
||||
ItemInstance* selected = inventory->getSelected();
|
||||
if (ItemInstance::matches(&useItem, selected)) {
|
||||
if (selected) *selected = itemInstance;
|
||||
if (itemInstance.count == 0) {
|
||||
inventory->clearSlot(inventory->selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
stopUsingItem();
|
||||
}
|
||||
}
|
||||
|
||||
int Player::getUseItemDuration() {
|
||||
return useItemDuration;
|
||||
}
|
||||
|
||||
int Player::getTicksUsingItem() {
|
||||
if(isUsingItem()) {
|
||||
return useItem.getUseDuration() - useItemDuration;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Player::travel(float xa, float ya) {
|
||||
if (abilities.flying) {
|
||||
float ydo = yd;
|
||||
float ofs = flyingSpeed;
|
||||
flyingSpeed = 0.05f;
|
||||
super::travel(xa, ya);
|
||||
yd = ydo * 0.6f;
|
||||
flyingSpeed = ofs;
|
||||
} else {
|
||||
super::travel(xa, ya);
|
||||
}
|
||||
}
|
||||
|
||||
/*protected*/
|
||||
bool Player::isImmobile() {
|
||||
return health <= 0 || isSleeping();
|
||||
}
|
||||
|
||||
/*protected*/
|
||||
void Player::closeContainer() {
|
||||
containerMenu = NULL;
|
||||
//containerMenu = inventoryMenu;
|
||||
}
|
||||
|
||||
void Player::resetPos(bool clearMore) {
|
||||
if(!isSleeping()) {
|
||||
heightOffset = 1.62f;
|
||||
setSize(0.6f, 1.8f);
|
||||
super::resetPos(clearMore);
|
||||
}
|
||||
invisible = false;
|
||||
|
||||
if (clearMore) {
|
||||
health = getMaxHealth();
|
||||
deathTime = 0;
|
||||
playerIsSleeping = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*protected*/
|
||||
void Player::updateAi() {
|
||||
updateAttackAnim();
|
||||
}
|
||||
|
||||
int Player::getItemInHandIcon(ItemInstance* item, int layer) {
|
||||
int icon = item->getIcon();
|
||||
if(useItem.id != 0 && item->id == Item::bow->id) {
|
||||
int ticksHeld = (item->getUseDuration() - useItemDuration);
|
||||
if(ticksHeld >= BowItem::MAX_DRAW_DURATION - 2) {
|
||||
return 5 + 8 * 16;
|
||||
}
|
||||
if(ticksHeld > (2 * BowItem::MAX_DRAW_DURATION) / 3) {
|
||||
return 5 + 7*16;
|
||||
}
|
||||
if(ticksHeld > 0) {
|
||||
return 5 + 6 * 16;
|
||||
}
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
void Player::aiStep() {
|
||||
if (level->difficulty == Difficulty::PEACEFUL && health < MAX_HEALTH) {
|
||||
if (tickCount % (12 * SharedConstants::TicksPerSecond) == 0) heal(1);
|
||||
}
|
||||
//inventory.tick();
|
||||
oBob = bob;
|
||||
// moved the super::aiStep() part to the local player
|
||||
|
||||
float tBob = (float) Mth::sqrt(xd * xd + zd * zd);
|
||||
float tTilt = (float) Mth::atan(-yd * 0.2f) * 15.f;
|
||||
if (tBob > 0.1f) tBob = 0.1f;
|
||||
if (!onGround || health <= 0) tBob = 0;
|
||||
if (onGround || health <= 0) tTilt = 0;
|
||||
bob += (tBob - bob) * 0.4f;
|
||||
tilt += (tTilt - tilt) * 0.8f;
|
||||
|
||||
if (health > 0) {
|
||||
EntityList& entities = level->getEntities(this, bb.grow(1, 0, 1));
|
||||
for (unsigned int i = 0; i < entities.size(); i++) {
|
||||
Entity* e = entities[i];
|
||||
if (!e->removed) {
|
||||
touch(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*private*/
|
||||
void Player::touch(Entity* entity) {
|
||||
entity->playerTouch(this);
|
||||
}
|
||||
|
||||
int Player::getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
void Player::die(Entity* source) {
|
||||
super::die(source);
|
||||
this->setSize(0.2f, 0.2f);
|
||||
setPos(x, y, z);
|
||||
yd = 0.1f;
|
||||
|
||||
//inventory->dropAll(level->isClientSide);
|
||||
|
||||
if (source != NULL) {
|
||||
xd = -(float) Mth::cos((hurtDir + yRot) * Mth::PI / 180) * 0.1f;
|
||||
zd = -(float) Mth::sin((hurtDir + yRot) * Mth::PI / 180) * 0.1f;
|
||||
} else {
|
||||
xd = zd = 0;
|
||||
}
|
||||
this->heightOffset = 0.1f;
|
||||
}
|
||||
|
||||
void Player::reset() {
|
||||
super::reset();
|
||||
this->_init();
|
||||
}
|
||||
|
||||
void Player::_init() {
|
||||
oBob = bob = 0;
|
||||
swinging = 0;
|
||||
swingTime = 0;
|
||||
score = 0;
|
||||
}
|
||||
|
||||
float Player::getWalkingSpeedModifier() {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
void Player::awardKillScore(Entity* victim, int score) {
|
||||
this->score += score;
|
||||
}
|
||||
|
||||
bool Player::isShootable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::isCreativeModeAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
//void Player::drop() {
|
||||
// //drop(inventory.removeItem(inventory.selected, 1), false);
|
||||
//}
|
||||
|
||||
void Player::drop(ItemInstance* item) {
|
||||
drop(item, false);
|
||||
}
|
||||
|
||||
void Player::drop(ItemInstance* item, bool randomly) {
|
||||
if (item == NULL || item->isNull())
|
||||
return;
|
||||
|
||||
ItemEntity* thrownItem = new ItemEntity(level, x, y - 0.3f + getHeadHeight(), z, *item);
|
||||
{ //@todo:itementity
|
||||
delete item;
|
||||
item = NULL;
|
||||
}
|
||||
thrownItem->throwTime = 20 * 2;
|
||||
|
||||
float pow = 0.1f;
|
||||
if (randomly) {
|
||||
float _pow = random.nextFloat() * 0.5f;
|
||||
float dir = random.nextFloat() * Mth::PI * 2;
|
||||
thrownItem->xd = -Mth::sin(dir) * _pow;
|
||||
thrownItem->zd = Mth::cos(dir) * _pow;
|
||||
thrownItem->yd = 0.2f;
|
||||
|
||||
} else {
|
||||
pow = 0.3f;
|
||||
thrownItem->xd = -Mth::sin(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * pow;
|
||||
thrownItem->zd = Mth::cos(yRot / 180 * Mth::PI) * Mth::cos(xRot / 180 * Mth::PI) * pow;
|
||||
thrownItem->yd = -Mth::sin(xRot / 180 * Mth::PI) * pow + 0.1f;
|
||||
pow = 0.02f;
|
||||
|
||||
float dir = random.nextFloat() * Mth::PI * 2;
|
||||
pow *= random.nextFloat();
|
||||
thrownItem->xd += Mth::cos(dir) * pow;
|
||||
thrownItem->yd += (random.nextFloat() - random.nextFloat()) * 0.1f;
|
||||
thrownItem->zd += Mth::sin(dir) * pow;
|
||||
}
|
||||
|
||||
reallyDrop(thrownItem);
|
||||
}
|
||||
|
||||
/*protected*/
|
||||
void Player::reallyDrop(ItemEntity* thrownItem) {
|
||||
level->addEntity(thrownItem);
|
||||
}
|
||||
|
||||
float Player::getDestroySpeed(Tile* tile) {
|
||||
float speed = inventory->getDestroySpeed(tile);
|
||||
//if (isUnderLiquid(Material.water)) speed /= 5;
|
||||
//if (!onGround) speed /= 5;
|
||||
return speed;
|
||||
}
|
||||
|
||||
bool Player::canDestroy(Tile* tile) {
|
||||
return inventory->canDestroy(tile);
|
||||
}
|
||||
|
||||
//@SuppressWarnings("unchecked")
|
||||
void Player::readAdditionalSaveData(CompoundTag* entityTag) {
|
||||
super::readAdditionalSaveData(entityTag);
|
||||
|
||||
if (entityTag->contains("Inventory", Tag::TAG_List)) {
|
||||
ListTag* inventoryList = entityTag->getList("Inventory");
|
||||
inventory->load(inventoryList);
|
||||
}
|
||||
if (entityTag->contains("Armor", Tag::TAG_List)) {
|
||||
loadArmor(armor, entityTag->getList("Armor"));
|
||||
}
|
||||
|
||||
dimension = entityTag->getInt("Dimension");
|
||||
|
||||
//return;
|
||||
if(entityTag->contains("Sleeping") && entityTag->contains("SleepTimer")
|
||||
&& entityTag->contains("BedPositionX") && entityTag->contains("BedPositionY") && entityTag->contains("BedPositionZ")) {
|
||||
playerIsSleeping = entityTag->getBoolean("Sleeping");
|
||||
sleepCounter = entityTag->getShort("SleepTimer");
|
||||
bedPosition = Pos(entityTag->getInt("BedPositionX"), entityTag->getInt("BedPositionY"), entityTag->getInt("BedPositionZ"));
|
||||
} else {
|
||||
playerIsSleeping = false;
|
||||
bedPosition = Pos(0,0,0);
|
||||
}
|
||||
|
||||
if(!playerIsSleeping) {
|
||||
stopSleepInBed(true, true, false);
|
||||
entityData.clearFlag<SharedFlagsInformation::SharedFlagsInformationType>(DATA_PLAYER_FLAGS_ID, PLAYER_SLEEP_FLAG);
|
||||
} else {
|
||||
playerIsSleeping = false;
|
||||
startSleepInBed(bedPosition.x, bedPosition.y, bedPosition.z);
|
||||
entityData.setFlag<SharedFlagsInformation::SharedFlagsInformationType>(DATA_PLAYER_FLAGS_ID, PLAYER_SLEEP_FLAG);
|
||||
}
|
||||
entityData.set<Pos>(DATA_BED_POSITION_ID, bedPosition);
|
||||
if (entityTag->contains("SpawnX") && entityTag->contains("SpawnY") && entityTag->contains("SpawnZ")) {
|
||||
respawnPosition.set(entityTag->getInt("SpawnX"), entityTag->getInt("SpawnY"), entityTag->getInt("SpawnZ"));
|
||||
}
|
||||
playerHasRespawnPosition = respawnPosition.y >= 0;
|
||||
}
|
||||
|
||||
void Player::addAdditonalSaveData(CompoundTag* entityTag) {
|
||||
super::addAdditonalSaveData(entityTag);
|
||||
|
||||
ListTag* inventoryTag = inventory->save(new ListTag());
|
||||
// if (inventoryTag->size() > 0)
|
||||
entityTag->put("Inventory", inventoryTag);
|
||||
//else
|
||||
// delete inventoryTag;
|
||||
|
||||
ListTag* armorTag = saveArmor(armor);
|
||||
entityTag->put("Armor", armorTag);
|
||||
|
||||
entityTag->putInt("Dimension", dimension);
|
||||
//return;
|
||||
|
||||
entityTag->putBoolean("Sleeping", isSleeping());
|
||||
entityTag->putShort("SleepTimer", sleepCounter);
|
||||
entityTag->putInt("BedPositionX", bedPosition.x);
|
||||
entityTag->putInt("BedPositionY", bedPosition.y);
|
||||
entityTag->putInt("BedPositionZ", bedPosition.z);
|
||||
|
||||
entityTag->putInt("SpawnX", respawnPosition.x);
|
||||
entityTag->putInt("SpawnY", respawnPosition.y);
|
||||
entityTag->putInt("SpawnZ", respawnPosition.z);
|
||||
}
|
||||
|
||||
//static Pos getRespawnPosition(Level level, CompoundTag entityTag) {
|
||||
// if (entityTag.contains("SpawnX") && entityTag.contains("SpawnY") && entityTag.contains("SpawnZ")) {
|
||||
// return /*new*/ Pos(entityTag.getInt("SpawnX"), entityTag.getInt("SpawnY"), entityTag.getInt("SpawnZ"));
|
||||
// }
|
||||
// return level.getSharedSpawnPos();
|
||||
//}
|
||||
|
||||
void Player::startCrafting(int x, int y, int z, int tableSize) {
|
||||
}
|
||||
|
||||
void Player::startStonecutting(int x, int y, int z) {
|
||||
}
|
||||
|
||||
void Player::openFurnace(FurnaceTileEntity* e) {
|
||||
}
|
||||
|
||||
void Player::take(Entity* e, int orgCount) {
|
||||
}
|
||||
|
||||
float Player::getHeadHeight() {
|
||||
return 0.12f; // heightOffset; // 0.12f;
|
||||
}
|
||||
|
||||
/*protected*/
|
||||
void Player::setDefaultHeadHeight() {
|
||||
heightOffset = 1.62f;
|
||||
}
|
||||
|
||||
bool Player::isHurt() {
|
||||
return health > 0 && health < getMaxHealth();
|
||||
}
|
||||
|
||||
bool Player::hurt(Entity* source, int dmg) {
|
||||
if (abilities.invulnerable) return false;
|
||||
|
||||
noActionTime = 0;
|
||||
if (health <= 0) return false;
|
||||
if(isSleeping() && !level->isClientSide) {
|
||||
stopSleepInBed(true, true, false);
|
||||
}
|
||||
|
||||
if (source != NULL && (source->getCreatureBaseType() == MobTypes::BaseEnemy
|
||||
|| source->getEntityTypeId() == EntityTypes::IdArrow)) {
|
||||
|
||||
if (source->isMob() && level->adventureSettings.noMvP)
|
||||
return false;
|
||||
|
||||
if (level->difficulty == Difficulty::PEACEFUL) dmg = 0;
|
||||
else if (level->difficulty == Difficulty::EASY) dmg = dmg / 3 + 1;
|
||||
else if (level->difficulty == Difficulty::HARD) dmg = dmg * 3 / 2;
|
||||
}
|
||||
|
||||
if (dmg == 0) return false;
|
||||
|
||||
// Entity* attacker = source;
|
||||
// //if (attacker instanceof Arrow) {
|
||||
// // if (((Arrow) attacker).owner != NULL) {
|
||||
// // attacker = ((Arrow) attacker).owner;
|
||||
// // }
|
||||
// //}
|
||||
return super::hurt(source, dmg);
|
||||
}
|
||||
|
||||
void Player::interact(Entity* entity) {
|
||||
if (entity->interact(this)) return;
|
||||
ItemInstance* item = inventory->getSelected();
|
||||
if (item != NULL && entity->isMob()) {
|
||||
item->interactEnemy((Mob*)entity);
|
||||
if (item->count <= 0) {
|
||||
//item.snap(this);
|
||||
inventory->clearSlot(inventory->selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Player::swing() {
|
||||
//LOGI("swinging: %d\n", swinging);
|
||||
// if (!swinging || swingTime >= 3 || swingTime < 0) {
|
||||
// swingTime = -1;
|
||||
// swinging = true;
|
||||
// }
|
||||
|
||||
//level->raknetInstance->send(owner, new AnimatePacket(AnimatePacket::Swing, this));
|
||||
//}
|
||||
//}
|
||||
|
||||
void Player::attack(Entity* entity) {
|
||||
int dmg = inventory->getAttackDamage(entity);
|
||||
if (dmg > 0) {
|
||||
entity->hurt(this, dmg);
|
||||
ItemInstance* item = inventory->getSelected();
|
||||
if (item != NULL && entity->isMob() && abilities.instabuild != true) {
|
||||
item->hurtEnemy((Mob*) entity);
|
||||
if (item->count <= 0) {
|
||||
//item->snap(this);
|
||||
inventory->clearSlot(inventory->selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::respawn() {
|
||||
}
|
||||
|
||||
/*protected static*/
|
||||
void Player::animateRespawn(Player* player, Level* level) {
|
||||
//for (int i = 0; i < 45; i++) {
|
||||
// float angle = i * Mth::PI * 4.0f / 25.0f;
|
||||
// float xo = Mth::cos(angle) * .7f;
|
||||
// float zo = Mth::sin(angle) * .7f;
|
||||
//}
|
||||
}
|
||||
|
||||
/*virtual*/
|
||||
void Player::animateRespawn() {}
|
||||
|
||||
//void carriedChanged(ItemInstance carried) {
|
||||
//}
|
||||
ItemInstance* Player::getCarriedItem() {
|
||||
return inventory->getSelected();
|
||||
}
|
||||
|
||||
void Player::remove() {
|
||||
invisible = true;
|
||||
super::remove();
|
||||
}
|
||||
|
||||
//@Override
|
||||
bool Player::isInWall() {
|
||||
return super::isInWall();
|
||||
}
|
||||
|
||||
bool Player::hasResource( int id ) {
|
||||
return inventory->hasResource(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is currently only relevant to client-side players. It will
|
||||
* try to load the messageId from the language file and display it to the
|
||||
* client.
|
||||
*/
|
||||
void Player::displayClientMessage(const std::string& messageId) {
|
||||
}
|
||||
|
||||
Pos Player::getRespawnPosition() {
|
||||
return respawnPosition;
|
||||
}
|
||||
|
||||
void Player::setRespawnPosition(const Pos& respawnPosition) { // @attn WARNING CHECK THIS THROUGH @fix @todo: rewrite
|
||||
if (respawnPosition.y < 0) {
|
||||
playerHasRespawnPosition = false;
|
||||
}
|
||||
else {
|
||||
playerHasRespawnPosition = true;
|
||||
}
|
||||
this->respawnPosition = respawnPosition;
|
||||
}
|
||||
|
||||
bool Player::isPlayer() { return true; }
|
||||
|
||||
/*static*/
|
||||
bool Player::isPlayer( Entity* e ) {
|
||||
return e && e->isPlayer();
|
||||
}
|
||||
|
||||
Player* Player::asPlayer( Entity* e ) {
|
||||
return isPlayer(e)? (Player*) e : NULL;
|
||||
}
|
||||
|
||||
void Player::openContainer(ChestTileEntity* container)
|
||||
{
|
||||
}
|
||||
|
||||
void Player::tileEntityDestroyed( int tileEntityId ) {
|
||||
|
||||
//LOGI("TileEntityDestroyed, container: %p, %p\n", this, containerMenu);
|
||||
|
||||
if (!containerMenu) return;
|
||||
|
||||
//LOGI("TileEntityDestroyed, id: %d, %p, %d\n", this, tileEntityId, ((FurnaceMenu*)containerMenu)->furnaceTileEntityId);
|
||||
|
||||
if (containerMenu->tileEntityDestroyedIsInvalid(tileEntityId))
|
||||
closeContainer();
|
||||
}
|
||||
|
||||
bool Player::canUseCarriedItemWhileMoving() {
|
||||
ItemInstance* item = getCarriedItem();
|
||||
return item &&
|
||||
( item->id == Item::bow->id
|
||||
|| item->getItem()->isFood());
|
||||
}
|
||||
|
||||
void Player::handleEntityEvent( char id ) {
|
||||
if (id == EntityEvent::USE_ITEM_COMPLETE) {
|
||||
completeUsingItem();
|
||||
} else {
|
||||
super::handleEntityEvent(id);
|
||||
}
|
||||
}
|
||||
|
||||
ItemInstance* Player::getSelectedItem() {
|
||||
return inventory->getSelected();
|
||||
}
|
||||
|
||||
bool Player::hasRespawnPosition(){
|
||||
return playerHasRespawnPosition;
|
||||
}
|
||||
|
||||
void Player::openTextEdit( TileEntity* tileEntity ) {
|
||||
// Do nothing on the server, this is a client thing :)
|
||||
}
|
||||
|
||||
//
|
||||
// Armor code. Move this out to an ArmorInventory
|
||||
//
|
||||
static ListTag* saveArmor(ItemInstance* armor) {
|
||||
ListTag* listTag = new ListTag();
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
CompoundTag* tag = new CompoundTag();
|
||||
armor[i].save(tag);
|
||||
listTag->add(tag);
|
||||
}
|
||||
return listTag;
|
||||
}
|
||||
|
||||
static void loadArmor(ItemInstance* armor, ListTag* listTag) {
|
||||
if (!listTag)
|
||||
return;
|
||||
|
||||
const int count = Mth::Min(4, listTag->size());
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Tag* tag = listTag->get(i);
|
||||
if (tag->getId() != Tag::TAG_Compound) continue;
|
||||
|
||||
armor[i].load((CompoundTag*) tag);
|
||||
}
|
||||
}
|
||||
|
||||
ItemInstance* Player::getArmor(int slot) {
|
||||
if (slot < 0 || slot >= NUM_ARMOR)
|
||||
return NULL;
|
||||
|
||||
if (armor[slot].isNull())
|
||||
return NULL;
|
||||
|
||||
return &armor[slot];
|
||||
}
|
||||
|
||||
void Player::setArmor(int slot, const ItemInstance* item) {
|
||||
if (item == NULL)
|
||||
armor[slot].setNull();
|
||||
else {
|
||||
armor[slot] = *item;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::hurtArmor( int dmg ) {
|
||||
dmg = Mth::Max(1, dmg / 4);
|
||||
|
||||
for (int i = 0; i < NUM_ARMOR; i++) {
|
||||
ItemInstance& item = armor[i];
|
||||
if (!ItemInstance::isArmorItem(&item))
|
||||
continue;
|
||||
|
||||
item.hurt(dmg);
|
||||
if (item.count == 0) {
|
||||
item.setNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Player::getArmorTypeHash() {
|
||||
return (armor[0].id ) +
|
||||
(armor[1].id << 8) +
|
||||
(armor[2].id << 16) +
|
||||
(armor[3].id << 24);
|
||||
}
|
||||
|
||||
int Player::getArmorValue() {
|
||||
int val = 0;
|
||||
|
||||
for (int i = 0; i < NUM_ARMOR; i++) {
|
||||
ItemInstance& item = armor[i];
|
||||
if (!ItemInstance::isArmorItem(&item))
|
||||
continue;
|
||||
|
||||
int baseProtection = ((ArmorItem*) item.getItem())->defense;
|
||||
val += baseProtection;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
201
src/world/entity/player/Player.h
Executable file
201
src/world/entity/player/Player.h
Executable file
@@ -0,0 +1,201 @@
|
||||
#ifndef NET_MINECRAFT_WORLD_ENTITY_PLAYER__Player_H__
|
||||
#define NET_MINECRAFT_WORLD_ENTITY_PLAYER__Player_H__
|
||||
|
||||
//package net.minecraft.world.entity.player;
|
||||
|
||||
#include "Abilities.h"
|
||||
#include "../Mob.h"
|
||||
#include "../../Pos.h"
|
||||
#include "../../food/SimpleFoodData.h"
|
||||
#include "../../item/crafting/Recipe.h"
|
||||
|
||||
class Tile;
|
||||
class ItemEntity;
|
||||
class ItemInstance;
|
||||
class Inventory;
|
||||
class FillingContainer;
|
||||
class FurnaceTileEntity;
|
||||
class CompoundTag;
|
||||
class ChestTileEntity;
|
||||
class BaseContainerMenu;
|
||||
class TileEntity;
|
||||
class BedSleepingResult {
|
||||
public:
|
||||
static const int OK = 0;
|
||||
static const int NOT_POSSIBLE_HERE = 1;
|
||||
static const int NOT_POSSIBLE_NOW = 2;
|
||||
static const int TOO_FAR_AWAY = 3;
|
||||
static const int OTHER_PROBLEM = 4;
|
||||
static const int NOT_SAFE = 5;
|
||||
};
|
||||
|
||||
class Player: public Mob
|
||||
{
|
||||
typedef Mob super;
|
||||
typedef SynchedEntityData::TypeChar PlayerFlagIDType;
|
||||
|
||||
static const int DATA_PLAYER_FLAGS_ID = 16;
|
||||
static const int DATA_BED_POSITION_ID = 17;
|
||||
static const int PLAYER_SLEEP_FLAG = 1;
|
||||
|
||||
public:
|
||||
static const int MAX_NAME_LENGTH = 16;
|
||||
static const int MAX_HEALTH = 20;
|
||||
static const float DEFAULT_WALK_SPEED;
|
||||
static const float DEFAULT_FLY_SPEED;
|
||||
static const int SLEEP_DURATION = 100;
|
||||
static const int WAKE_UP_DURATION = 10;
|
||||
|
||||
Player(Level* level, bool isCreative);
|
||||
virtual ~Player();
|
||||
|
||||
void _init();
|
||||
virtual void reset();
|
||||
|
||||
static bool isPlayer(Entity* e);
|
||||
static Player* asPlayer(Entity* e);
|
||||
|
||||
virtual void tick();
|
||||
void aiStep();
|
||||
void travel(float xa, float ya);
|
||||
|
||||
virtual float getWalkingSpeedModifier();
|
||||
|
||||
void die(Entity* source);
|
||||
void remove();
|
||||
void respawn();
|
||||
void resetPos(bool clearMore);
|
||||
Pos getRespawnPosition();
|
||||
void setRespawnPosition(const Pos& respawnPosition);
|
||||
|
||||
bool isShootable();
|
||||
bool isCreativeModeAllowed();
|
||||
bool isPlayer();
|
||||
bool isInWall();
|
||||
|
||||
virtual bool hasResource( int id );
|
||||
|
||||
bool isUsingItem();
|
||||
ItemInstance* getUseItem();
|
||||
|
||||
void startUsingItem(ItemInstance instance, int duration);
|
||||
void stopUsingItem();
|
||||
void releaseUsingItem();
|
||||
virtual void completeUsingItem();
|
||||
|
||||
int getUseItemDuration();
|
||||
int getTicksUsingItem();
|
||||
|
||||
int getScore();
|
||||
void awardKillScore(Entity* victim, int score);
|
||||
void handleEntityEvent(char id);
|
||||
|
||||
virtual void take(Entity* e, int orgCount);
|
||||
//void drop();
|
||||
virtual void drop(ItemInstance* item);
|
||||
virtual void drop(ItemInstance* item, bool randomly);
|
||||
void reallyDrop(ItemEntity* thrownItem);
|
||||
|
||||
bool canDestroy(Tile* tile);
|
||||
float getDestroySpeed(Tile* tile);
|
||||
|
||||
int getMaxHealth();
|
||||
bool isHurt();
|
||||
|
||||
bool hurt(Entity* source, int dmg);
|
||||
void hurtArmor(int dmg);
|
||||
void setArmor(int slot, const ItemInstance* item);
|
||||
ItemInstance* getArmor(int slot);
|
||||
int getArmorTypeHash();
|
||||
|
||||
void interact(Entity* entity);
|
||||
void attack(Entity* entity);
|
||||
virtual ItemInstance* getCarriedItem();
|
||||
bool canUseCarriedItemWhileMoving();
|
||||
|
||||
virtual void startCrafting(int x, int y, int z, int tableSize);
|
||||
virtual void startStonecutting(int x, int y, int z);
|
||||
|
||||
virtual void openContainer(ChestTileEntity* container);
|
||||
virtual void openFurnace(FurnaceTileEntity* e);
|
||||
void tileEntityDestroyed( int tileEntityId );
|
||||
|
||||
virtual void displayClientMessage(const std::string& messageId);
|
||||
virtual void animateRespawn();
|
||||
float getHeadHeight();
|
||||
|
||||
// id == 0 -> not possible to create via serialization (yet)
|
||||
int getEntityTypeId() const { return 0; }
|
||||
|
||||
int getItemInHandIcon(ItemInstance* item, int layer);
|
||||
bool isSleeping();
|
||||
virtual int startSleepInBed(int x, int y, int z);
|
||||
virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint);
|
||||
virtual int getSleepTimer();
|
||||
void setAllPlayersSleeping();
|
||||
float getSleepRotation();
|
||||
bool isSleepingLongEnough();
|
||||
ItemInstance* getSelectedItem();
|
||||
Inventory* inventory;
|
||||
bool hasRespawnPosition();
|
||||
virtual void openTextEdit( TileEntity* tileEntity );
|
||||
//AbstractContainerMenu inventoryMenu;
|
||||
//AbstractContainerMenu containerMenu;
|
||||
int getArmorValue();
|
||||
protected:
|
||||
bool isImmobile();
|
||||
void updateAi();
|
||||
virtual void closeContainer();
|
||||
void setDefaultHeadHeight();
|
||||
|
||||
void readAdditionalSaveData(CompoundTag* entityTag);
|
||||
void addAdditonalSaveData(CompoundTag* entityTag);
|
||||
|
||||
void setBedOffset(int bedDirection);
|
||||
bool checkBed();
|
||||
|
||||
static void animateRespawn(Player* player, Level* level);
|
||||
void spawnEatParticles(const ItemInstance* useItem, int count);
|
||||
private:
|
||||
void touch(Entity* entity);
|
||||
|
||||
//void eat( ItemInstance* instance );
|
||||
|
||||
public:
|
||||
char userType;
|
||||
int score;
|
||||
float oBob, bob;
|
||||
|
||||
std::string name;
|
||||
int dimension;
|
||||
|
||||
Abilities abilities;
|
||||
SimpleFoodData foodData;
|
||||
//Stats stats;
|
||||
|
||||
BaseContainerMenu* containerMenu;
|
||||
|
||||
// ok I know it's not so nice to build in RakNet dependency here, BUT I DON'T CARE! MUAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHHAHAHAHAHAHAHAHAHHAHAHAHAHAHAHA
|
||||
RakNet::RakNetGUID owner;
|
||||
bool hasFakeInventory;
|
||||
Pos bedPosition;
|
||||
float bedOffsetX;
|
||||
float bedOffsetY;
|
||||
float bedOffsetZ;
|
||||
protected:
|
||||
ItemInstance useItem;
|
||||
int useItemDuration;
|
||||
short sleepCounter;
|
||||
|
||||
static const int NUM_ARMOR = 4;
|
||||
private:
|
||||
Pos respawnPosition;
|
||||
bool playerHasRespawnPosition;
|
||||
bool playerIsSleeping;
|
||||
bool allPlayersSleeping;
|
||||
|
||||
ItemInstance armor[NUM_ARMOR];
|
||||
//FishingHook fishing = NULL;
|
||||
};
|
||||
|
||||
#endif /*NET_MINECRAFT_WORLD_ENTITY_PLAYER__Player_H__*/
|
||||
Reference in New Issue
Block a user