the whole game

This commit is contained in:
2026-03-02 22:04:18 +03:00
parent 816e9060b4
commit f0617a5d22
2069 changed files with 581500 additions and 0 deletions

62
src/world/Container.h Executable file
View File

@@ -0,0 +1,62 @@
#ifndef NET_MINECRAFT_WORLD__Container_H__
#define NET_MINECRAFT_WORLD__Container_H__
//package net.minecraft.world;
#include "item/ItemInstance.h"
#include <string>
#include <vector>
class Player;
class ContainerType {
public:
static const int NONE = -9;
static const int INVENTORY = -1;
static const int CONTAINER = 0;
static const int WORKBENCH = 1;
static const int FURNACE = 2;
static const int TRAP = 3;
static const int ENCHANTMENT = 4;
static const int BREWING_STAND = 5;
};
class Container
{
public:
static const int LARGE_MAX_STACK_SIZE = 64;
Container(int containerType)
: containerId(-1),
containerType(containerType)
{}
virtual ~Container() {}
virtual ItemInstance* getItem(int slot) = 0;
virtual void setItem(int slot, ItemInstance* item) = 0;
virtual ItemInstance removeItem(int slot, int i) = 0;
virtual std::string getName() const = 0;
virtual int getContainerSize() const = 0;
virtual int getMaxStackSize() const = 0;
virtual bool stillValid(Player* player) = 0;
virtual void startOpen() = 0;
virtual void stopOpen() = 0;
virtual std::vector<ItemInstance> getSlotCopies() {
std::vector<ItemInstance> items;
ItemInstance NullItem;
for (int i = 0; i < getContainerSize(); ++i) {
ItemInstance* item = getItem(i);
items.push_back( item? *item : NullItem );
}
return items;
}
int containerId;
int containerType;
};
#endif /*NET_MINECRAFT_WORLD__Container_H__*/

14
src/world/Difficulty.h Executable file
View File

@@ -0,0 +1,14 @@
#ifndef NET_MINECRAFT_WORLD__Difficulty_H__
#define NET_MINECRAFT_WORLD__Difficulty_H__
//package net.minecraft.world;
class Difficulty {
public:
static const int PEACEFUL = 0;
static const int EASY = 1;
static const int NORMAL = 2;
static const int HARD = 3;
};
#endif /*NET_MINECRAFT_WORLD__Difficulty_H__*/

71
src/world/Direction.cpp Executable file
View File

@@ -0,0 +1,71 @@
#include "Direction.h"
#include "Facing.h"
const int Direction::DIRECTION_FACING[4] = {
Facing::SOUTH,
Facing::WEST,
Facing::NORTH,
Facing::EAST
};
const int Direction::FACING_DIRECTION[6] = {
Direction::UNDEFINED,
Direction::UNDEFINED,
Direction::NORTH,
Direction::SOUTH,
Direction::WEST,
Direction::EAST
};
const int Direction::DIRECTION_OPPOSITE[4] = {
Direction::NORTH,
Direction::EAST,
Direction::SOUTH,
Direction::WEST
};
const int Direction::RELATIVE_DIRECTION_FACING[4][6] = {
// south
{ Facing::DOWN,
Facing::UP,
Facing::SOUTH,
Facing::NORTH,
Facing::EAST,
Facing::WEST },
// west
{ Facing::DOWN,
Facing::UP,
Facing::EAST,
Facing::WEST,
Facing::NORTH,
Facing::SOUTH },
// north
{ Facing::DOWN,
Facing::UP,
Facing::NORTH,
Facing::SOUTH,
Facing::WEST,
Facing::EAST },
// east
{ Facing::DOWN,
Facing::UP,
Facing::WEST,
Facing::EAST,
Facing::SOUTH,
Facing::NORTH }
};
// declared in Facing.h
const int Facing::OPPOSITE_FACING[6] = {
Facing::UP, Facing::DOWN, Facing::SOUTH, Facing::NORTH, Facing::EAST, Facing::WEST
};
const int Facing::STEP_X[6] = {
0, 0, 0, 0, -1, 1
};
const int Facing::STEP_Y[6] = {
-1, 1, 0, 0, 0, 0
};
const int Facing::STEP_Z[6] = {
0, 0, -1, 1, 0, 0
};

28
src/world/Direction.h Executable file
View File

@@ -0,0 +1,28 @@
#ifndef NET_MINECRAFT__Direction_H__
#define NET_MINECRAFT__Direction_H__
//package net.minecraft;
class Direction
{
public:
static const int UNDEFINED = -1;
static const int SOUTH = 0;
static const int WEST = 1;
static const int NORTH = 2;
static const int EAST = 3;
// for [direction] it gives [tile-face]
static const int DIRECTION_FACING[4];
// for a given face gives direction
static const int FACING_DIRECTION[6];
// for [direction] it gives [opposite direction]
static const int DIRECTION_OPPOSITE[4];
// for [direction][world-facing] it gives [tile-facing]
static const int RELATIVE_DIRECTION_FACING[4][6];
};
#endif /*NET_MINECRAFT__Direction_H__*/

34
src/world/Facing.h Executable file
View File

@@ -0,0 +1,34 @@
#ifndef NET_MINECRAFT__Facing_H__
#define NET_MINECRAFT__Facing_H__
//package net.minecraft;
class Facing
{
public:
static const int DOWN = 0;
static const int UP = 1;
static const int NORTH = 2;
static const int SOUTH = 3;
static const int WEST = 4;
static const int EAST = 5;
static const char* toString(int face) {
if (face == DOWN) return "Down";
if (face == UP ) return "Up";
if (face == NORTH) return "North";
if (face == SOUTH) return "South";
if (face == WEST) return "West";
if (face == EAST) return "East";
return "Unknown facing";
}
// implemented in Direction.cpp
static const int OPPOSITE_FACING[6];
static const int STEP_X[6];
static const int STEP_Y[6];
static const int STEP_Z[6];
};
#endif /*NET_MINECRAFT__Facing_H__*/

193
src/world/Pos.h Executable file
View File

@@ -0,0 +1,193 @@
#ifndef NET_MINECRAFT_WORLD_Pos_H__
#define NET_MINECRAFT_WORLD_Pos_H__
//package net.minecraft;
#include <sstream>
class Pos
{
public:
Pos()
: x(0),
y(0),
z(0)
{}
Pos(int x, int y, int z)
: x(x),
y(y),
z(z)
{}
Pos(const Pos& position)
: x(position.x),
y(position.y),
z(position.z)
{}
__inline bool operator==(const Pos& rhs) const {
return x == rhs.x && y == rhs.y && z == rhs.z;
}
bool operator<(const Pos& rhs) const {
return compareTo(rhs) < 0;
}
static int createHashCode(int x, int y, int z) {
return x + (z << 8) + (y << 16);
}
int hashCode() const {
return x + (z << 8) + (y << 16);
}
int compareTo(const Pos& pos) const {
return hashCode() - pos.hashCode();
}
Pos offset(int x, int y, int z) const {
return Pos(this->x + x, this->y + y, this->z + z);
}
void set(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
}
void set(const Pos& pos) {
x = pos.x;
y = pos.y;
z = pos.z;
}
Pos above() const {
return Pos(x, y + 1, z);
}
Pos above(int steps) const {
return Pos(x, y + steps, z);
}
Pos below() const {
return Pos(x, y - 1, z);
}
Pos below(int steps) const {
return Pos(x, y - steps, z);
}
Pos north() const {
return Pos(x, y, z - 1);
}
Pos north(int steps) const {
return Pos(x, y, z - steps);
}
Pos south() const {
return Pos(x, y, z + 1);
}
Pos south(int steps) const {
return Pos(x, y, z + steps);
}
Pos west() const {
return Pos(x - 1, y, z);
}
Pos west(int steps) const {
return Pos(x - 1, y, z);
}
Pos east() const {
return Pos(x + 1, y, z);
}
Pos east(int steps) const {
return Pos(x + steps, y, z);
}
void move(int x, int y, int z) {
this->x += x;
this->y += y;
this->z += z;
}
void move(const Pos& pos) {
x += pos.x;
y += pos.y;
z += pos.z;
}
void moveX(int steps) {
x += steps;
}
void moveY(int steps) {
y += steps;
}
void moveZ(int steps) {
z += steps;
}
void moveUp(int steps) {
y += steps;
}
void moveUp() {
++y;
}
void moveDown(int steps) {
y -= steps;
}
void moveDown() {
--y;
}
void moveEast(int steps) {
x += steps;
}
void moveEast() {
++x;
}
void moveWest(int steps) {
x -= steps;
}
void moveWest() {
--x;
}
void moveNorth(int steps) {
z -= steps;
}
void moveNorth() {
--z;
}
void moveSouth(int steps) {
z += steps;
}
void moveSouth() {
++z;
}
std::string toString() const {
std::stringstream ss;
ss << "Pos(" << x << "," << y << "," << z << ")";
return ss.str();
}
int x, y, z;
};
#endif /*NET_MINECRAFT_WORLD_Pos_H__*/

36
src/world/PosTranslator.h Executable file
View File

@@ -0,0 +1,36 @@
#ifndef NET_MINECRAFT_WORLD_PosTranslator_H__
#define NET_MINECRAFT_WORLD_PosTranslator_H__
//package net.minecraft;
class IPosTranslator {
public:
virtual ~IPosTranslator() {}
virtual void to(int& x, int& y, int& z) = 0;
virtual void to(float& x, float& y, float& z) = 0;
virtual void from(int& x, int& y, int& z) = 0;
virtual void from(float& x, float& y, float& z) = 0;
};
class OffsetPosTranslator: public IPosTranslator {
public:
OffsetPosTranslator()
: xo(0),
yo(0),
zo(0)
{}
OffsetPosTranslator(float xo, float yo, float zo)
: xo(xo),
yo(yo),
zo(zo)
{}
void to (float& x, float& y, float& z) { x += xo; y += yo; z += zo; }
void to (int& x, int& y, int& z) { x += (int)xo; y += (int)yo; z += (int)zo; }
void from(float& x, float& y, float& z) { x -= xo; y -= yo; z -= zo; }
void from(int& x, int& y, int& z) { x -= (int)xo; y -= (int)yo; z -= (int)zo; }
float xo, yo, zo;
};
#endif /*NET_MINECRAFT_WORLD_PosTranslator_H__*/

53
src/world/entity/AgableMob.cpp Executable file
View File

@@ -0,0 +1,53 @@
#include "AgableMob.h"
AgableMob::AgableMob( Level* level )
: super(level),
age(-1)
{
entityData.define(DATA_FLAGS_ID, (SynchedEntityData::TypeChar)0);
setAge(0);
}
int AgableMob::getAge() {
return age;
}
void AgableMob::setAge( int age ) {
if (this->age < 0 && age >= 0) {
entityData.clearFlag<SynchedEntityData::TypeChar>(DATA_FLAGS_ID, DATAFLAG_ISBABY);
}
else if (this->age >= 0 && age < 0) {
entityData.setFlag<SynchedEntityData::TypeChar>(DATA_FLAGS_ID, DATAFLAG_ISBABY);
}
this->age = age;
}
void AgableMob::addAdditonalSaveData( CompoundTag* tag ) {
super::addAdditonalSaveData(tag);
tag->putInt("Age", getAge());
}
void AgableMob::readAdditionalSaveData( CompoundTag* tag ) {
super::readAdditionalSaveData(tag);
setAge(tag->getInt("Age"));
}
void AgableMob::aiStep() {
super::aiStep();
//@note: keeping this for now, since we don't use breeding anyway
// and it feels better to have animals at age 0 then 99999999
// if we decide to actually use it.
if (age < 0)
setAge(age + 1);
else if (age > 0)
setAge(age - 1);
}
bool AgableMob::isBaby() {
if (!level->isClientSide) {
return age < 0;
} else {
return entityData.getFlag<SynchedEntityData::TypeChar>(DATA_FLAGS_ID, DATAFLAG_ISBABY);
}
}

30
src/world/entity/AgableMob.h Executable file
View File

@@ -0,0 +1,30 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__AgableMob_H__
#define NET_MINECRAFT_WORLD_ENTITY__AgableMob_H__
#include "PathfinderMob.h"
//package net.minecraft.world.entity;
class AgableMob: public PathfinderMob
{
typedef PathfinderMob super;
public:
AgableMob(Level* level);
int getAge();
void setAge(int age);
bool isBaby();
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
void aiStep();
private:
int age;
static const int DATA_FLAGS_ID = 14;
// Flags values are bit shifted
static const int DATAFLAG_ISBABY = 0;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__AgableMob_H__*/

9
src/world/entity/Creature.h Executable file
View File

@@ -0,0 +1,9 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__Creature_H__
#define NET_MINECRAFT_WORLD_ENTITY__Creature_H__
//package net.minecraft.world.entity;
class Creature {
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__Creature_H__*/

963
src/world/entity/Entity.cpp Executable file
View File

@@ -0,0 +1,963 @@
#include "Entity.h"
#include "EntityPos.h"
#include "../level/Level.h"
#include "../level/tile/LiquidTile.h"
#include "item/ItemEntity.h"
#include "../item/ItemInstance.h"
#include "../../nbt/CompoundTag.h"
#include "../../util/PerfTimer.h"
int
Entity::entityCounter = 0;
Random
Entity::sharedRandom(getEpochTimeS());
Entity::Entity( Level* level )
: level(level),
viewScale(1.0f),
blocksBuilding(false),
onGround(false),
wasInWater(false),
collision(false),
hurtMarked(false),
slide(true),
isStuckInWeb(false),
removed(false),
reallyRemoveIfPlayer(false),
canRemove(true), //@todo: remove
noPhysics(false),
firstTick(true),
bbWidth(0.6f),
bbHeight(1.8f),
heightOffset(0 / 16.0f),
bb(0,0,0,0,0,0),
ySlideOffset(0),
fallDistance(0),
footSize(0),
invulnerableTime(0),
pushthrough(0),
airCapacity(TOTAL_AIR_SUPPLY),
airSupply(TOTAL_AIR_SUPPLY),
xOld(0),yOld(0),zOld(0),
horizontalCollision(false), verticalCollision(false),
x(0), y(0), z(0),
xo(0),yo(0),zo(0),xd(0),yd(0),zd(0),
xRot(0), yRot(0),
xRotO(0), yRotO(0),
xChunk(0), yChunk(0), zChunk(0),
inChunk(false),
fireImmune(false),
onFire(0),
flameTime(1),
walkDist(0), walkDistO(0),
tickCount(0),
entityRendererId(ER_DEFAULT_RENDERER),
nextStep(1),
makeStepSound(true),
invisible(false)
{
_init();
entityId = ++entityCounter;
//ref = Ref<Entity>::create(this);
setPos(0, 0, 0);
}
Entity::~Entity() {
//if (ref->isUnique())
// delete ref;
}
SynchedEntityData* Entity::getEntityData() {
return NULL;
}
const SynchedEntityData* Entity::getEntityData() const {
return NULL;
}
bool Entity::isInWall() {
int xt = Mth::floor(x);
int yt = Mth::floor(y + getHeadHeight());
int zt = Mth::floor(z);
return level->isSolidBlockingTile(xt, yt, zt);
}
void Entity::resetPos(bool clearMore) {
if (level == NULL) return;
while (y > 0) {
setPos(x, y, z);
if (level->getCubes(this, bb).size() == 0) break;
y += 1;
}
xd = yd = zd = 0;
xRot = 0;
}
bool Entity::isInWater() {
return level->checkAndHandleWater(bb.grow(0, -0.4f, 0), Material::water, this);
}
bool Entity::isInLava() {
return level->containsMaterial(bb.grow(-0.1f, -0.4f, -0.1f), Material::lava);
}
bool Entity::isFree(float xa, float ya, float za, float grow) {
AABB box = bb.grow(grow, grow, grow).cloneMove(xa, ya, za);
const std::vector<AABB>& aABBs = level->getCubes(this, box);
if (aABBs.size() > 0) return false;
if (level->containsAnyLiquid(box)) return false;
return true;
}
bool Entity::isFree(float xa, float ya, float za) {
AABB box = bb.cloneMove(xa, ya, za);
const std::vector<AABB>& aABBs = level->getCubes(this, box);
if (aABBs.size() > 0) return false;
if (level->containsAnyLiquid(box)) return false;
return true;
}
//static void __attribute__((noinline)) setPositionFromBbox(Entity* e) { // @RPI
// const AABB& bb = e->bb;
// e->x = (e->bb.x0 + e->bb.x1) / 2.0f;
// e->y = bb.y0 + e->heightOffset - e->ySlideOffset;
// e->z = (bb.z0 + bb.z1) / 2.0f;
//}
/*public*/
void Entity::move(float xa, float ya, float za) {
//if (std::abs(xa) + std::abs(ya) + std::abs(za) < 0.00001f) //@RPI
// return;
if (noPhysics) {
bb.move(xa, ya, za);
x = (bb.x0 + bb.x1) / 2.0f;
y = bb.y0 + heightOffset - ySlideOffset;
z = (bb.z0 + bb.z1) / 2.0f;
return;
}
TIMER_PUSH("move");
float xo = x;
float zo = z;
if (isStuckInWeb) {
isStuckInWeb = false;
xa *= .25f;
ya *= .05f;
za *= .25f;
xd = .0f;
yd = .0f;
zd = .0f;
}
float xaOrg = xa;
float yaOrg = ya;
float zaOrg = za;
AABB bbOrg = bb;
bool sneaking = onGround && isSneaking();
if (sneaking) {
float d = 0.05f;
while (xa != 0 && level->getCubes(this, bb.cloneMove(xa, -1.0, 0)).empty()) {
if (xa < d && xa >= -d) xa = 0;
else if (xa > 0) xa -= d;
else xa += d;
xaOrg = xa;
}
while (za != 0 && level->getCubes(this, bb.cloneMove(0, -1.0, za)).empty()) {
if (za < d && za >= -d) za = 0;
else if (za > 0) za -= d;
else za += d;
zaOrg = za;
}
while (xa != 0 && za != 0 && level->getCubes(this, bb.cloneMove(xa, -1.0, za)).empty()) {
if (xa < d && xa >= -d) xa = 0;
else if (xa > 0) xa -= d;
else xa += d;
if (za < d && za >= -d) za = 0;
else if (za > 0) za -= d;
else za += d;
xaOrg = xa;
zaOrg = za;
}
}
std::vector<AABB>& aABBs = level->getCubes(this, bb.expand(xa, ya, za));
// LAND FIRST, then x and z
for (unsigned int i = 0; i < aABBs.size(); i++)
ya = aABBs[i].clipYCollide(bb, ya);
bb.move(0, ya, 0);
if (!slide && yaOrg != ya) {
xa = ya = za = 0;
}
bool og = onGround || (yaOrg != ya && yaOrg < 0);
for (unsigned int i = 0; i < aABBs.size(); i++)
xa = aABBs[i].clipXCollide(bb, xa);
bb.move(xa, 0, 0);
if (!slide && xaOrg != xa) {
xa = ya = za = 0;
}
for (unsigned int i = 0; i < aABBs.size(); i++)
za = aABBs[i].clipZCollide(bb, za);
bb.move(0, 0, za);
if (!slide && zaOrg != za) {
xa = ya = za = 0;
}
if (footSize > 0 && og && (ySlideOffset < 0.05f) && ((xaOrg != xa) || (zaOrg != za))) {
float xaN = xa;
float yaN = ya;
float zaN = za;
xa = xaOrg;
ya = footSize;
za = zaOrg;
AABB normal = bb;
bb.set(bbOrg);
aABBs = level->getCubes(this, bb.expand(xa, ya, za));
// LAND FIRST, then x and z
for (unsigned int i = 0; i < aABBs.size(); i++)
ya = aABBs[i].clipYCollide(bb, ya);
bb.move(0, ya, 0);
if (!slide && yaOrg != ya) {
xa = ya = za = 0;
}
for (unsigned int i = 0; i < aABBs.size(); i++)
xa = aABBs[i].clipXCollide(bb, xa);
bb.move(xa, 0, 0);
if (!slide && xaOrg != xa) {
xa = ya = za = 0;
}
for (unsigned int i = 0; i < aABBs.size(); i++)
za = aABBs[i].clipZCollide(bb, za);
bb.move(0, 0, za);
if (!slide && zaOrg != za) {
xa = ya = za = 0;
}
if (xaN * xaN + zaN * zaN >= xa * xa + za * za) {
xa = xaN;
ya = yaN;
za = zaN;
bb.set(normal);
} else {
ySlideOffset += 0.5f;
}
}
TIMER_POP_PUSH("rest");
x = (bb.x0 + bb.x1) / 2.0f;
y = bb.y0 + heightOffset - ySlideOffset;
z = (bb.z0 + bb.z1) / 2.0f;
horizontalCollision = (xaOrg != xa) || (zaOrg != za);
verticalCollision = (yaOrg != ya);
onGround = yaOrg != ya && yaOrg < 0;
collision = horizontalCollision || verticalCollision;
checkFallDamage(ya, onGround);
if (xaOrg != xa) xd = 0;
if (yaOrg != ya) yd = 0;
if (zaOrg != za) zd = 0;
float xm = x - xo;
float zm = z - zo;
if (makeStepSound && !sneaking) {
walkDist += Mth::sqrt(xm * xm + zm * zm) * 0.6f;
int xt = Mth::floor(x);
int yt = Mth::floor(y - 0.2f - this->heightOffset);
int zt = Mth::floor(z);
int t = level->getTile(xt, yt, zt);
if (t == 0) {
int under = level->getTile(xt, yt-1, zt);
if (Tile::fence->id == under || Tile::fenceGate->id == under) {
t = under;
}
}
if (walkDist > nextStep && t > 0) {
nextStep = ((int) walkDist) + 1;
playStepSound(xt, yt, zt, t);
//Tile::tiles[t]->stepOn(level, xt, yt, zt, this); //@todo: step
}
}
int x0 = Mth::floor(bb.x0);
int y0 = Mth::floor(bb.y0);
int z0 = Mth::floor(bb.z0);
int x1 = Mth::floor(bb.x1);
int y1 = Mth::floor(bb.y1);
int z1 = Mth::floor(bb.z1);
if (level->hasChunksAt(x0, y0, z0, x1, y1, z1)) {
for (int x = x0; x <= x1; x++)
for (int y = y0; y <= y1; y++)
for (int z = z0; z <= z1; z++) {
int t = level->getTile(x, y, z);
if (t > 0) {
Tile::tiles[t]->entityInside(level, x, y, z, this);
}
}
}
ySlideOffset *= 0.4f;
bool water = this->isInWater();
if (level->containsFireTile(bb)) {
burn(1);
if (!water) {
onFire++;
if (onFire == 0) onFire = 20 * 15;
}
} else {
if (onFire <= 0) {
onFire = -flameTime;
}
}
if (water && onFire > 0) {
//level.playSound(this-> "random.fizz", 0.7f, 1.6f + (random.nextFloat() - random.nextFloat()) * 0.4f);
onFire = -flameTime;
}
TIMER_POP();
}
void Entity::makeStuckInWeb() {
isStuckInWeb = true;
fallDistance = 0;
}
/*public virtual*/
bool Entity::isUnderLiquid(const Material* material) {
float yp = y + getHeadHeight();
int xt = Mth::floor(x);
int yt = Mth::floor((float)Mth::floor(yp));
int zt = Mth::floor(z);
int t = level->getTile(xt, yt, zt);
if (t != 0 && Tile::tiles[t]->material == material) {
float hh = LiquidTile::getHeight(level->getData(xt, yt, zt)) - 1 / 9.0f;
float h = yt + 1 - hh;
return yp < h;
}
return false;
}
/*protected virtual*/
void Entity::setPos(EntityPos* pos)
{
if (pos->move) setPos(pos->x, pos->y, pos->z);
else setPos(x, y, z);
if (pos->rot) setRot(pos->yRot, pos->xRot);
else setRot(yRot, xRot);
}
void Entity::setPos( float x, float y, float z )
{
this->x = x;
this->y = y;
this->z = z;
float w = bbWidth / 2;
float h = bbHeight;
bb.set(x - w, y - heightOffset + ySlideOffset, z - w, x + w, y - heightOffset + ySlideOffset + h, z + w);
}
/*virtual*/
float Entity::getBrightness(float a) {
int xTile = Mth::floor(x);
float hh = (bb.y1 - bb.y0) * 0.66f;
int yTile = Mth::floor(y - this->heightOffset + hh);
int zTile = Mth::floor(z);
if (level->hasChunksAt(Mth::floor(bb.x0), Mth::floor(bb.y0), Mth::floor(bb.z0), Mth::floor(bb.x1), Mth::floor(bb.y1), Mth::floor(bb.z1))) {
return level->getBrightness(xTile, yTile, zTile);
}
return 0;
}
bool Entity::operator==( Entity& rhs )
{
return entityId == rhs.entityId;
}
int Entity::hashCode()
{
return entityId;
}
void Entity::remove()
{
removed = true;
}
void Entity::setSize( float w, float h )
{
bbWidth = w;
bbHeight = h;
}
void Entity::setRot( float yRot, float xRot )
{
this->yRot = yRotO = yRot;
this->xRot = xRotO = xRot;
}
void Entity::turn( float xo, float yo )
{
float xRotOld = xRot;
float yRotOld = yRot;
yRot += xo * 0.15f;
xRot -= yo * 0.15f;
if (xRot < -90) xRot = -90;
if (xRot > 90) xRot = 90;
xRotO += xRot - xRotOld;
yRotO += yRot - yRotOld;
}
void Entity::interpolateTurn( float xo, float yo )
{
yRot += xo * 0.15f;
xRot -= yo * 0.15f;
if (xRot < -90) xRot = -90;
if (xRot > 90) xRot = 90;
}
void Entity::tick()
{
baseTick();
}
void Entity::baseTick()
{
TIMER_PUSH("entityBaseTick");
tickCount++;
walkDistO = walkDist;
xo = x;
yo = y;
zo = z;
xRotO = xRot;
yRotO = yRot;
if (isInWater()) {
if (!wasInWater && !firstTick) {
float speed = sqrt(xd * xd * 0.2f + yd * yd + zd * zd * 0.2f) * 0.2f;
if (speed > 1) speed = 1;
level->playSound(this, "random.splash", speed, 1 + (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.4f);
float yt = floorf(bb.y0);
for (int i = 0; i < 1 + bbWidth * 20; i++) {
float xo = (sharedRandom.nextFloat() * 2 - 1) * bbWidth;
float zo = (sharedRandom.nextFloat() * 2 - 1) * bbWidth;
level->addParticle(PARTICLETYPE(bubble), x + xo, yt + 1, z + zo, xd, yd - sharedRandom.nextFloat() * 0.2f, zd);
}
//for (int i = 0; i < 1 + bbWidth * 20; i++) {
// float xo = (sharedRandom.nextFloat() * 2 - 1) * bbWidth;
// float zo = (sharedRandom.nextFloat() * 2 - 1) * bbWidth;
// level->addParticle(PARTICLETYPE(splash), x + xo, yt + 1, z + zo, xd, yd, zd);
//}
}
fallDistance = 0;
wasInWater = true;
onFire = 0;
} else {
wasInWater = false;
}
if (level->isClientSide) {
onFire = 0;
} else {
if (onFire > 0) {
if (fireImmune) {
onFire -= 4;
if (onFire < 0) onFire = 0;
} else {
if (onFire % 20 == 0) {
hurt(NULL, 1);
}
onFire--;
}
}
}
if (isInLava()) {
lavaHurt();
}
if (y < -64) {
outOfWorld();
}
//if (!level->isOnline) {
// setSharedFlag(FLAG_ONFIRE, onFire > 0);
//}
firstTick = false;
TIMER_POP();
}
void Entity::outOfWorld()
{
remove();
}
void Entity::checkFallDamage( float ya, bool onGround )
{
if (onGround) {
if (fallDistance > 0) {
if(isMob()) {
int xt = Mth::floor(x);
int yt = Mth::floor(y - 0.2f - heightOffset);
int zt = Mth::floor(z);
int t = level->getTile(xt, yt, zt);
if (t == 0 && level->getTile(xt, yt - 1, zt) == Tile::fence->id) {
t = level->getTile(xt, yt - 1, zt);
}
if (t > 0) {
Tile::tiles[t]->fallOn(level, xt, yt, zt, this, fallDistance);
}
}
causeFallDamage(fallDistance);
fallDistance = 0;
}
} else {
if (ya < 0) fallDistance -= ya;
}
}
void Entity::causeFallDamage( float fallDamage2 )
{
}
float Entity::getHeadHeight()
{
return 0;
}
void Entity::moveRelative( float xa, float za, float speed )
{
float dist = sqrt(xa * xa + za * za);
if (dist < 0.01f) return;
if (dist < 1) dist = 1;
dist = speed / dist;
xa *= dist;
za *= dist;
float sin_ = (float) sin(yRot * Mth::PI / 180);
float cos_ = (float) cos(yRot * Mth::PI / 180);
xd += xa * cos_ - za * sin_;
zd += za * cos_ + xa * sin_;
}
void Entity::setLevel( Level* level )
{
this->level = level;
}
void Entity::moveTo( float x, float y, float z, float yRot, float xRot )
{
this->xOld = this->xo = this->x = x;
this->yOld = this->yo = this->y = y + heightOffset;
this->zOld = this->zo = this->z = z;
this->yRot = this->yRotO = yRot;
this->xRot = this->xRotO = xRot;
this->setPos(this->x, this->y, this->z);
}
float Entity::distanceTo( Entity* e )
{
float xd = (float) (x - e->x);
float yd = (float) (y - e->y);
float zd = (float) (z - e->z);
return sqrt(xd * xd + yd * yd + zd * zd);
}
float Entity::distanceTo( float x2, float y2, float z2 )
{
float xd = (x - x2);
float yd = (y - y2);
float zd = (z - z2);
return sqrt(xd * xd + yd * yd + zd * zd);
}
float Entity::distanceToSqr( float x2, float y2, float z2 )
{
float xd = (x - x2);
float yd = (y - y2);
float zd = (z - z2);
return xd * xd + yd * yd + zd * zd;
}
float Entity::distanceToSqr( Entity* e )
{
float xd = x - e->x;
float yd = y - e->y;
float zd = z - e->z;
return xd * xd + yd * yd + zd * zd;
}
void Entity::playerTouch( Player* player )
{
}
void Entity::push( Entity* e )
{
float xa = e->x - x;
float za = e->z - z;
float dd = Mth::absMax(xa, za);
if (dd >= 0.01f) {
dd = sqrt(dd);
xa /= dd;
za /= dd;
float pow = 1 / dd;
if (pow > 1) pow = 1;
xa *= pow;
za *= pow;
xa *= 0.05f;
za *= 0.05f;
xa *= 1 - pushthrough;
za *= 1 - pushthrough;
this->push(-xa, 0, -za);
e->push(xa, 0, za);
}
}
void Entity::push( float xa, float ya, float za )
{
xd += xa;
yd += ya;
zd += za;
}
void Entity::markHurt()
{
this->hurtMarked = true;
}
bool Entity::hurt( Entity* source, int damage )
{
markHurt();
return false;
}
void Entity::reset() {
this->_init();
}
void Entity::_init() {
xo = xOld = x;
yo = yOld = y;
zo = zOld = z;
xRotO = xRot;
yRotO = yRot;
onFire = 0;
removed = false;
fallDistance = 0;
}
bool Entity::intersects( float x0, float y0, float z0, float x1, float y1, float z1 )
{
return bb.intersects(x0, y0, z0, x1, y1, z1);
}
bool Entity::isPickable()
{
return false;
}
bool Entity::isPushable()
{
return false;
}
bool Entity::isShootable()
{
return false;
}
void Entity::awardKillScore( Entity* victim, int score )
{
}
bool Entity::shouldRender( Vec3& c )
{
if (invisible) return false;
float xd = x - c.x;
float yd = y - c.y;
float zd = z - c.z;
float distance = xd * xd + yd * yd + zd * zd;
return shouldRenderAtSqrDistance(distance);
}
bool Entity::shouldRenderAtSqrDistance( float distance )
{
float size = bb.getSize();
size *= 64.0f * viewScale;
return distance < size * size;
}
bool Entity::isCreativeModeAllowed()
{
return false;
}
float Entity::getShadowHeightOffs()
{
return bbHeight / 2;
}
bool Entity::isAlive()
{
return !removed;
}
bool Entity::interact( Player* player )
{
return false;
}
void Entity::lerpTo( float x, float y, float z, float yRot, float xRot, int steps )
{
setPos(x, y, z);
setRot(yRot, xRot);
}
float Entity::getPickRadius()
{
return 0.1f;
}
void Entity::lerpMotion( float xd, float yd, float zd )
{
this->xd = xd;
this->yd = yd;
this->zd = zd;
}
void Entity::animateHurt()
{
}
void Entity::setEquippedSlot( int slot, int item, int auxValue )
{
}
bool Entity::isSneaking()
{
return false;
}
bool Entity::isPlayer()
{
return false;
}
void Entity::lavaHurt() {
if (fireImmune) {
} else {
hurt(NULL, 4);
onFire = 30 * SharedConstants::TicksPerSecond;
}
}
// AABB getCollideBox() {
// return NULL;
// }
void Entity::burn(int dmg) {
if (!fireImmune) {
hurt(NULL, dmg);
}
}
// std::string getTexture() {
// return NULL;
// }
bool Entity::save(CompoundTag* entityTag) {
int id = getEntityTypeId();
if (removed || id == 0) {
return false;
}
entityTag->putInt("id", id);
saveWithoutId(entityTag);
return true;
}
void Entity::saveWithoutId(CompoundTag* entityTag) {
entityTag->put("Pos", ListTagFloatAdder (x) (y) (z).tag);
entityTag->put("Motion", ListTagFloatAdder (xd) (yd) (zd).tag);
entityTag->put("Rotation", ListTagFloatAdder (yRot) (xRot).tag);
entityTag->putFloat("FallDistance", fallDistance);
entityTag->putShort("Fire", (short) onFire);
entityTag->putShort("Air", (short) airSupply);
entityTag->putBoolean("OnGround", onGround);
addAdditonalSaveData(entityTag);
}
bool Entity::load( CompoundTag* tag )
{
ListTag* pos = tag->getList("Pos");
ListTag* motion = tag->getList("Motion");
ListTag* rotation = tag->getList("Rotation");
setPos(0, 0, 0);
xd = motion->getFloat(0);
yd = motion->getFloat(1);
zd = motion->getFloat(2);
if (Mth::abs(xd) > 10.0) {
xd = 0;
}
if (Mth::abs(yd) > 10.0) {
yd = 0;
}
if (Mth::abs(zd) > 10.0) {
zd = 0;
}
float xx = pos->getFloat(0);
float yy = pos->getFloat(1);
float zz = pos->getFloat(2);
// Add a small padding if standing next to the world edges
const float padding = bbWidth * 0.5f + 0.001f;
xx = Mth::clamp(xx, padding, (float)LEVEL_WIDTH - padding);
zz = Mth::clamp(zz, padding, (float)LEVEL_DEPTH - padding);
xo = xOld = x = xx;
yo = yOld = y = yy;
zo = zOld = z = zz;
yRotO = yRot = fmod( rotation->getFloat(0), 360.0f);
xRotO = xRot = fmod( rotation->getFloat(1), 360.0f);
fallDistance= tag->getFloat("FallDistance");
onFire = tag->getShort("Fire");
airSupply = tag->getShort("Air");
onGround = tag->getBoolean("OnGround");
setPos(x, y, z);
readAdditionalSaveData(tag);
return (tag->errorState == 0);
}
// /*protected*/ const String getEncodeId() {
// return EntityIO.getEncodeId(this->;
// }
ItemEntity* Entity::spawnAtLocation(int resource, int count) {
return spawnAtLocation(resource, count, 0);
}
ItemEntity* Entity::spawnAtLocation(int resource, int count, float yOffs) {
return spawnAtLocation(new ItemInstance(resource, count, 0), yOffs);
}
ItemEntity* Entity::spawnAtLocation(ItemInstance* itemInstance, float yOffs) {
ItemEntity* ie = new ItemEntity(level, x, y + yOffs, z, *itemInstance);
{ //@todo:itementity
delete itemInstance;
itemInstance = NULL;
}
ie->throwTime = 10;
level->addEntity(ie);
return ie;
}
bool Entity::isOnFire() {
return onFire > 0;// || getSharedFlag(FLAG_ONFIRE);
}
bool Entity::interactPreventDefault() {
return false;
}
// AABB getCollideAgainstBox(Entity entity) {
// return NULL;
// }
// Vec3 getLookAngle() {
// return NULL;
// }
// void prepareCustomTextures() {
// }
// ItemInstance[] getEquipmentSlots() {
// return NULL;
// }
bool Entity::isItemEntity() {
return false;
}
bool Entity::isHangingEntity() {
return false;
}
int Entity::getAuxData() {
return 0;
}
void Entity::playStepSound( int xt, int yt, int zt, int t ) {
const Tile::SoundType* soundType = Tile::tiles[t]->soundType;
if (level->getTile(xt, yt + 1, zt) == Tile::topSnow->id) {
soundType = Tile::topSnow->soundType;
level->playSound(this, soundType->getStepSound(), soundType->getVolume() * 0.25f, soundType->getPitch()); // was * 0.15f
} else if (!Tile::tiles[t]->material->isLiquid()) {
level->playSound(this, soundType->getStepSound(), soundType->getVolume() * 0.25f, soundType->getPitch());
}
}

226
src/world/entity/Entity.h Executable file
View File

@@ -0,0 +1,226 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__Entity_H__
#define NET_MINECRAFT_WORLD_ENTITY__Entity_H__
//package net.minecraft.world.entity;
class Level;
class Player;
class EntityPos;
class Material;
class ItemEntity;
class ItemInstance;
class CompoundTag;
#include "EntityRendererId.h"
#include "../phys/AABB.h"
#include "../../SharedConstants.h"
//#include "../../util/MemUtils.h"
#include "../../util/Mth.h"
#include "../../util/Random.h"
class SynchedEntityData;
class Entity
{
public:
static int entityCounter;
static const int TOTAL_AIR_SUPPLY = 15 * SharedConstants::TicksPerSecond;
Entity(Level* level);
virtual ~Entity();
void _init();
virtual void reset(); // { super::reset(); _init(); }
int hashCode();
bool operator==(Entity& rhs);
virtual void setLevel(Level* level);
virtual void remove();
virtual void setPos(float x, float y, float z);
virtual void move(float xa, float ya, float za);
virtual void moveTo(float x, float y, float z, float yRot, float xRot);
virtual void moveRelative(float xa, float za, float speed);
virtual void lerpTo(float x, float y, float z, float yRot, float xRot, int steps);
virtual void lerpMotion(float xd, float yd, float zd);
virtual void turn(float xo, float yo);
virtual void interpolateTurn(float xo, float yo);
virtual void tick();
virtual void baseTick();
virtual bool intersects(float x0, float y0, float z0, float x1, float y1, float z1);
virtual bool isFree(float xa, float ya, float za, float grow);
virtual bool isFree(float xa, float ya, float za);
virtual bool isInWall();
virtual bool isInWater();
virtual bool isInLava();
virtual bool isUnderLiquid(const Material* material);
virtual void makeStuckInWeb();
virtual float getHeadHeight();
virtual float getShadowHeightOffs();
virtual float getBrightness(float a);
float distanceTo(Entity* e);
float distanceTo(float x2, float y2, float z2);
float distanceToSqr(float x2, float y2, float z2);
float distanceToSqr(Entity* e);
virtual bool interactPreventDefault();
virtual bool interact(Player* player);
virtual void playerTouch(Player* player);
virtual void push(Entity* e);
virtual void push(float xa, float ya, float za);
virtual bool isPickable();
virtual bool isPushable();
virtual bool isShootable();
virtual bool isSneaking();
virtual bool isAlive();
virtual bool isOnFire();
virtual bool isPlayer();
virtual bool isCreativeModeAllowed();
virtual bool shouldRender(Vec3& c);
virtual bool shouldRenderAtSqrDistance(float distance);
virtual bool hurt(Entity* source, int damage);
virtual void animateHurt();
virtual void handleEntityEvent(char eventId) {}
virtual float getPickRadius();
virtual ItemEntity* spawnAtLocation(int resource, int count);
virtual ItemEntity* spawnAtLocation(int resource, int count, float yOffs);
// @attn: right now this means a pointer that spawnAtLocation takes ownership of
virtual ItemEntity* spawnAtLocation(ItemInstance* itemInstance, float yOffs);
virtual void awardKillScore(Entity* victim, int score);
virtual void setEquippedSlot(int slot, int item, int auxValue);
virtual bool save(CompoundTag* entityTag);
virtual void saveWithoutId(CompoundTag* entityTag);
virtual bool load(CompoundTag* entityTag);
virtual SynchedEntityData* getEntityData();
virtual const SynchedEntityData* getEntityData() const;
__inline bool isEntityType(int type) { return getEntityTypeId() == type; }
virtual int getEntityTypeId() const = 0;
virtual int getCreatureBaseType() const { return 0; }
virtual EntityRendererId queryEntityRenderer() { return ER_DEFAULT_RENDERER; }
virtual bool isMob() { return false; }
// I hate myself
virtual bool isItemEntity();
// Me 2
virtual bool isHangingEntity();
virtual int getAuxData();
protected:
virtual void setRot(float yRot, float xRot);
virtual void setSize(float w, float h);
virtual void setPos(EntityPos* pos);
virtual void resetPos(bool clearMore);
virtual void outOfWorld();
virtual void checkFallDamage(float ya, bool onGround);
virtual void causeFallDamage(float fallDamage2);
virtual void markHurt();
virtual void burn(int dmg);
virtual void lavaHurt();
virtual void readAdditionalSaveData(CompoundTag* tag) = 0;
virtual void addAdditonalSaveData(CompoundTag* tag) = 0;
virtual void playStepSound( int xt, int yt, int zt, int t );
public:
float x, y, z;
int xChunk, yChunk, zChunk;
int entityId;
float viewScale;
Level* level;
float xo, yo, zo;
float xd, yd, zd;
float yRot, xRot;
float yRotO, xRotO;
AABB bb;
float heightOffset; // = 0 / 16.0f;
float bbWidth;
float bbHeight;
float walkDistO;
float walkDist;
float xOld, yOld, zOld;
float ySlideOffset;
float footSize;
float pushthrough;
int tickCount;
int invulnerableTime;
int airSupply;
int onFire;
int flameTime;
EntityRendererId entityRendererId;
//Ref<Entity>* ref;
// bool hovered = false;
// std::string customTextureUrl;
// std::string customTextureUrl2;
// /*protected*/ bool fireImmune = false;
float fallDistance;
bool blocksBuilding;
bool inChunk;
bool onGround;
bool horizontalCollision, verticalCollision;
bool collision;
bool hurtMarked;
bool slide;
bool removed;
bool noPhysics;
bool canRemove;
bool invisible;
bool reallyRemoveIfPlayer;
protected:
static Random sharedRandom;
int airCapacity;
bool makeStepSound;
bool wasInWater;
bool fireImmune;
protected:
bool firstTick;
int nextStep;
static const int DATA_AIR_SUPPLY_ID = 1;
bool isStuckInWeb;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__Entity_H__*/

23
src/world/entity/EntityEvent.h Executable file
View File

@@ -0,0 +1,23 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__EntityEvent_H__
#define NET_MINECRAFT_WORLD_ENTITY__EntityEvent_H__
//package net.minecraft.world.entity;
class EntityEvent {
public:
static const char JUMP = 1;
static const char HURT = 2;
static const char DEATH = 3;
static const char START_ATTACKING = 4;
static const char STOP_ATTACKING = 5;
static const char TAMING_FAILED = 6;
static const char TAMING_SUCCEEDED = 7;
static const char SHAKE_WETNESS = 8;
static const char USE_ITEM_COMPLETE = 9;
static const char EAT_GRASS = 10;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__EntityEvent_H__*/

View File

@@ -0,0 +1,57 @@
#include "EntityFactory.h"
#include "MobFactory.h"
#include "../../nbt/CompoundTag.h"
#include "item/PrimedTnt.h"
#include "projectile/Arrow.h"
#include "projectile/ThrownEgg.h"
#include "projectile/Snowball.h"
#include "Painting.h"
#include "item/FallingTile.h"
Entity* EntityFactory::CreateEntity( int typeId, Level* level )
{
switch (typeId) {
case EntityTypes::IdItemEntity: return new ItemEntity(level);
case EntityTypes::IdFallingTile:return new FallingTile(level);
case EntityTypes::IdPrimedTnt: return new PrimedTnt(level);
case EntityTypes::IdArrow: return new Arrow(level);
case EntityTypes::IdThrownEgg: return new ThrownEgg(level);
case EntityTypes::IdSnowball: return new Snowball(level);
case EntityTypes::IdPainting: return new Painting(level);
}
return NULL;
}
Entity* EntityFactory::loadEntity( CompoundTag* tag, Level* level )
{
if (!tag) return NULL;
if (!tag->contains("id")) return NULL;
int id = tag->getInt("id");
Entity* e = NULL;
if (id < 0) {
LOGE("Negative ItemId: %d at MobFactory::loadEntity\n", id);
} else if (id < 64) {
e = MobFactory::CreateMob(id, level);
} else {
e = CreateEntity(id, level);
}
if (e) {
e->load(tag);
// Add "fixes" here :p
if (e->isItemEntity()) {
const ItemInstance& item = ((ItemEntity*)e)->item;
// Remove items out of range, and now invalid
if(item.isNull() || item.id < 0 || item.id >= Item::MAX_ITEMS || !Item::items[item.id]) {
delete e;
e = NULL;
}
}
}
return e;
}

View File

@@ -0,0 +1,15 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__EntityFactory_H__
#define NET_MINECRAFT_WORLD_ENTITY__EntityFactory_H__
class Level;
class Entity;
class CompoundTag;
class EntityFactory
{
public:
static Entity* CreateEntity(int typeId, Level* level);
static Entity* loadEntity(CompoundTag* tag, Level* level);
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__EntityFactory_H__*/

84
src/world/entity/EntityPos.h Executable file
View File

@@ -0,0 +1,84 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__EntityPos_H__
#define NET_MINECRAFT_WORLD_ENTITY__EntityPos_H__
//package net.minecraft.world.entity;
#include "Entity.h"
class EntityPos
{
public:
float x, y, z;
float yRot, xRot;
bool rot;
bool move;
EntityPos(float x, float y, float z, float yRot, float xRot)
: rot(false),
move(false)
{
this->x = x;
this->y = y;
this->z = z;
this->yRot = yRot;
this->xRot = xRot;
rot = true;
move = true;
}
EntityPos(float x, float y, float z)
{
this->x = x;
this->y = y;
this->z = z;
move = true;
rot = false;
}
EntityPos(float yRot, float xRot)
{
this->yRot = yRot;
this->xRot = xRot;
rot = true;
move = false;
}
// EntityPos lerp(Entity* e, float f)
// {
// float xd = e->x+(x-e->x)*f;
// float yd = e->y+(y-e->y)*f;
// float zd = e->z+(z-e->z)*f;
//
// float yrdd = yRot-e->yRot;
// float xrdd = xRot-e->xRot;
//
// while (yrdd>=180) yrdd-=360;
// while (yrdd<-180) yrdd+=360;
// while (xrdd>=180) xrdd-=360;
// while (xrdd<-180) xrdd+=360;
//
// float yrd = e->yRot+yrdd*f;
// float xrd = e->xRot+xrdd*f;
//
// while (yrd>=180) yrd-=360;
// while (yrd<-180) yrd+=360;
// while (xrd>=180) xrd-=360;
// while (xrd<-180) xrd+=360;
//
// if (rot && move)
// {
// return /*new*/ EntityPos(xd, yd, zd, yrd, xrd);
// }
// if (move)
// {
// return /*new*/ EntityPos(xd, yd, zd);
// }
// if (rot)
// {
// return /*new*/ EntityPos(yrd, xrd);
// }
// return NULL;
// }
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__EntityPos_H__*/

View File

@@ -0,0 +1,28 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__EntityRendererId_H__
#define NET_MINECRAFT_WORLD_ENTITY__EntityRendererId_H__
enum EntityRendererId {
ER_DEFAULT_RENDERER,
ER_QUERY_RENDERER,
ER_TNT_RENDERER,
ER_HUMANOID_RENDERER,
ER_ITEM_RENDERER,
ER_TRIPODCAMERA_RENDERER,
ER_CHICKEN_RENDERER,
ER_COW_RENDERER,
ER_PIG_RENDERER,
ER_SHEEP_RENDERER,
ER_SHEEP_FUR_RENDERER,
ER_ZOMBIE_RENDERER,
ER_SKELETON_RENDERER,
ER_SPIDER_RENDERER,
ER_CREEPER_RENDERER,
ER_ARROW_RENDERER,
ER_PLAYER_RENDERER,
ER_THROWNEGG_RENDERER,
ER_SNOWBALL_RENDERER,
ER_PAINTING_RENDERER,
ER_FALLINGTILE_RENDERER
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__EntityRendererId_H__*/

129
src/world/entity/EntityTypes.h Executable file
View File

@@ -0,0 +1,129 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__EntityTypes_H__
#define NET_MINECRAFT_WORLD_ENTITY__EntityTypes_H__
#include "../../util/Mth.h"
//? 3 bits ?
class BaseTypes {
public:
static const int Entity = 1;
static const int Item = 2;
};
//? 4 bits ?
class EntityTypes {
public:
/*
static const int Mob = 1;
static const int Particle = 2;
static const int ItemEntity = 3;
*/
// Forgive me, but I'm not sure I have time to finish my RTTI
// implementation -> this. Mobs up to 63, rest on top of that
static const int IdItemEntity = 64;
static const int IdPrimedTnt = 65;
static const int IdFallingTile = 66;
static const int IdArrow = 80;
static const int IdSnowball = 81;
static const int IdThrownEgg= 82;
static const int IdPainting = 83;
};
// For now; mobs 0-63
// Those constants are unfortunately the same "type" as EntityTypes
// @todo: fix something nicer (but keep the IDs)
class MobTypes {
public:
static const int BaseEnemy = 1;
static const int BaseCreature = 2;
static const int BaseWaterCreature = 3;
static const int Chicken = 10;
static const int Cow = 11;
static const int Pig = 12;
static const int Sheep = 13;
static const int Zombie = 32;
static const int Creeper = 33;
static const int Skeleton = 34;
static const int Spider = 35;
static const int PigZombie = 36;
};
struct ParticleType {
enum Id {
none,
bubble,
crit,
smoke,
explode,
flame,
lava,
largesmoke,
reddust,
iconcrack,
snowballpoof,
largeexplode,
hugeexplosion
};
};
#define PARTICLETYPE(x) (ParticleType::x,#x)
//
//
//
/*
class ClassTree {
public:
static bool isEntityType(int type, int entityType) {
return getBaseType(type) == BaseTypes::Entity? getSubType(1, type) == entityType : false;
}
static bool isBaseType(int type, int baseType) {
return getBaseType(type) == getBaseType(baseType);
}
static int getBaseType(int type) {
return getSubType(0, type);
}
static int getSubType(int subTypeIndex, int type) {
return type & (0xff << (subTypeIndex << 3));
}
static bool isSameTypeUpTo(int a, int b, int hierarchy) {
if (hierarchy < 0) hierarchy = Mth::Min(getHierarchyLevel(a), getHierarchyLevel(b));
for (int i = 0; i <= hierarchy; ++i)
if (getSubType(i, a) != getSubType(i, b)) return false;
return true;
}
static int getHierarchyLevel(int type) {
if (type & 0xff) {
if (type & 0xff00) {
if (type & 0xff0000) {
if (type & 0xff000000) return 3;
return 2;
}
return 1;
}
return 0;
}
return -1;
}
static int createBaseType(int type) {
return _create(type);
}
static int createEntityType(int type) {
return _create(BaseTypes::Entity, type);
}
static int createMobType(int type) {
return _create(BaseTypes::Entity, EntityTypes::Mob, type);
}
static int _create(int base, int a = 0, int b = 0, int c = 0) {
return base | (a << 8) | (b << 16) | (c << 24);
}
};
*/
#endif /*NET_MINECRAFT_WORLD_ENTITY__EntityTypes_H__*/

71
src/world/entity/FlyingMob.cpp Executable file
View File

@@ -0,0 +1,71 @@
#include "FlyingMob.h"
#include "../level/Level.h"
#include "../level/tile/Tile.h"
#include "../../util/Mth.h"
FlyingMob::FlyingMob( Level* level )
: super(level)
{
}
void FlyingMob::travel( float xa, float ya )
{
if (isInWater()) {
moveRelative(xa, ya, 0.02f);
move(xd, yd, zd);
xd *= 0.80f;
yd *= 0.80f;
zd *= 0.80f;
} else if (isInLava()) {
moveRelative(xa, ya, 0.02f);
move(xd, yd, zd);
xd *= 0.50f;
yd *= 0.50f;
zd *= 0.50f;
} else {
float friction = 0.91f;
if (onGround) {
friction = 0.6f * 0.91f;
int t = level->getTile(Mth::floor(x), Mth::floor(bb.y0 - 0.5f), Mth::floor(z));
if (t > 0) {
friction = Tile::tiles[t]->friction * 0.91f;
}
}
float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction);
moveRelative(xa, ya, (onGround ? 0.1f * friction2 : 0.02f));
friction = 0.91f;
if (onGround) {
friction = 0.6f * 0.91f;
int t = level->getTile(Mth::floor(x), Mth::floor(bb.y0 - 0.5f), Mth::floor(z));
if (t > 0) {
friction = Tile::tiles[t]->friction * 0.91f;
}
}
move(xd, yd, zd);
xd *= friction;
yd *= friction;
zd *= friction;
}
walkAnimSpeedO = walkAnimSpeed;
float xxd = x - xo;
float zzd = z - zo;
float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4;
if (wst > 1) wst = 1;
walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f;
walkAnimPos += walkAnimSpeed;
}
bool FlyingMob::onLadder()
{
return false;
}
void FlyingMob::causeFallDamage( float distance )
{
}

24
src/world/entity/FlyingMob.h Executable file
View File

@@ -0,0 +1,24 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__FlyingMob_H__
#define NET_MINECRAFT_WORLD_ENTITY__FlyingMob_H__
//package net.minecraft.world.entity;
#include "Mob.h"
class Level;
class FlyingMob: public Mob
{
typedef Mob super;
public:
FlyingMob(Level* level);
void travel(float xa, float ya);
bool onLadder();
protected:
void causeFallDamage(float distance);
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__FlyingMob_H__*/

View File

@@ -0,0 +1,234 @@
#include "HangingEntity.h"
#include "../Direction.h"
#include "../level/Level.h"
#include "../level/material/Material.h"
HangingEntity::HangingEntity( Level* level )
: super(level) {
init();
}
HangingEntity::HangingEntity( Level* level, int xTile, int yTile, int zTile, int dir )
: super(level), xTile(xTile), yTile(yTile), zTile(zTile) {
init();
}
void HangingEntity::setPosition( int x, int y, int z ) {
xTile = x;
yTile = y;
zTile = z;
}
void HangingEntity::init() {
heightOffset = 0;
setSize(0.5f, 0.5f);
dir = 0;
checkInterval = 0;
}
void HangingEntity::setDir( int dir ) {
//printf("HangingEntity dir: %d\n", dir);
this->dir = dir;
yRotO = yRot = float(dir * 90);
float w = float(getWidth());
float h = float(getHeight());
float d = float(getWidth());
if (dir == Direction::NORTH || dir == Direction::SOUTH) {
d = 2.0f;
yRot = yRotO = float(Direction::DIRECTION_OPPOSITE[dir] * 90);
} else {
w = 2.0f;
}
w /= 32.0f;
h /= 32.0f;
d /= 32.0f;
float x = xTile + 0.5f;
float y = yTile + 0.5f;
float z = zTile + 0.5f;
float offset = 0.5f + 1.0f / 16.0f;
if (dir == Direction::NORTH) z -= offset;
if (dir == Direction::WEST) x -= offset;
if (dir == Direction::SOUTH) z += offset;
if (dir == Direction::EAST) x += offset;
if (dir == Direction::NORTH) x -= offs(getWidth());
if (dir == Direction::WEST) z += offs(getWidth());
if (dir == Direction::SOUTH) x += offs(getWidth());
if (dir == Direction::EAST) z -= offs(getWidth());
y += offs(getHeight());
setPos(x, y, z);
float ss = -(0.5f / 16.0f);
bb.set(x - w - ss, y - h - ss, z - d - ss, x + w + ss, y + h + ss, z + d + ss);
}
float HangingEntity::offs( int w ) {
if(w == 32) return 0.5f;
if(w == 64) return 0.5f;
return 0;
}
void HangingEntity::tick() {
if(checkInterval++ == 20 * 5 && !level->isClientSide) {
checkInterval = 0;
if(!removed && !survives()) {
remove();
dropItem();
}
}
}
bool HangingEntity::survives() {
if (!level->getCubes(this, bb).empty()) {
return false;
} else {
int ws = Mth::Max(1, getWidth() / 16);
int hs = Mth::Max(1, getHeight() / 16);
int xt = xTile;
int yt = yTile;
int zt = zTile;
if (dir == Direction::NORTH) xt = Mth::floor(x - getWidth() / 32.0f);
if (dir == Direction::WEST) zt = Mth::floor(z - getWidth() / 32.0f);
if (dir == Direction::SOUTH) xt = Mth::floor(x - getWidth() / 32.0f);
if (dir == Direction::EAST) zt = Mth::floor(z - getWidth() / 32.0f);
yt = Mth::floor(y - getHeight() / 32.0f);
for (int ss = 0; ss < ws; ++ss) {
for (int yy = 0; yy < hs; ++yy) {
const Material* m;
if (dir == Direction::NORTH || dir == Direction::SOUTH) {
m = level->getMaterial(xt + ss, yt + yy, zTile);
} else {
m = level->getMaterial(xTile, yt + yy, zt + ss);
}
if (!m->isSolid())
return false;
}
EntityList entities = level->getEntities(this, bb);
for(EntityList::iterator ei = entities.begin(); ei != entities.end(); ++ei) {
Entity* entity = *(ei);
if(entity->isHangingEntity())
return false;
}
}
}
return true;
}
bool HangingEntity::isHangingEntity() {
return true;
}
bool HangingEntity::isPickable() {
return true;
}
bool HangingEntity::interact(Player* player) {
if(!removed && !level->isClientSide) {
if(player != NULL
&& player->inventory != NULL
&& player->inventory->getSelected() != NULL
&& player->inventory->getSelected()->id == Item::bow->id)
return false;
remove();
markHurt();
if(player != NULL && !player->abilities.instabuild)
dropItem();
return true;
} else {
return !removed;
}
}
void HangingEntity::move( float xa, float ya, float za ) {
if(!level->isClientSide && !removed && (xa * xa + ya * ya + za * za) > 0) {
dropItem();
remove();
}
}
void HangingEntity::push( float xa, float ya, float za ) {
if (!level->isClientSide && !removed && (xa * xa + ya * ya + za * za) > 0) {
dropItem();
remove();
}
}
void HangingEntity::addAdditonalSaveData( CompoundTag* tag ) {
tag->putByte("Direction", (char) dir);
tag->putInt("TileX", xTile);
tag->putInt("TileY", yTile);
tag->putInt("TileZ", zTile);
// Back compat
switch (dir) {
case Direction::NORTH:
tag->putByte("Dir", char(0));
break;
case Direction::WEST:
tag->putByte("Dir", char(1));
break;
case Direction::SOUTH:
tag->putByte("Dir", char(2));
break;
case Direction::EAST:
tag->putByte("Dir", char(3));
break;
}
}
void HangingEntity::readAdditionalSaveData( CompoundTag* tag ) {
if (tag->contains("Direction")) {
dir = tag->getByte("Direction");
} else {
switch (tag->getByte("Dir")) {
case 0:
dir = Direction::NORTH;
break;
case 1:
dir = Direction::WEST;
break;
case 2:
dir = Direction::SOUTH;
break;
case 3:
dir = Direction::EAST;
break;
}
}
xTile = tag->getInt("TileX");
yTile = tag->getInt("TileY");
zTile = tag->getInt("TileZ");
setDir(dir);
}
float HangingEntity::getBrightness( float a ) {
int xTile = Mth::floor(x);
int zTile = Mth::floor(z);
//if (dir == Direction::NORTH) xTile--;
//if (dir == Direction::WEST) zTile++;
//if (dir == Direction::SOUTH) xTile++;
//if (dir == Direction::EAST) zTile--;
if (level->hasChunkAt(xTile, 0, zTile)) {
int yTile = Mth::floor(y);
return level->getBrightness(xTile, yTile, zTile);
}
return 0;
}
bool HangingEntity::hurt( Entity* source, int damage ) {
if(!removed && !level->isClientSide) {
remove();
markHurt();
Player* player = Player::asPlayer(source);
if(player != NULL && player->abilities.instabuild) {
return true;
}
dropItem();
}
return true;
}

View File

@@ -0,0 +1,36 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__HangingEntity_H__
#define NET_MINECRAFT_WORLD_ENTITY__HangingEntity_H__
#include "Entity.h"
class HangingEntity : public Entity {
typedef Entity super;
public:
HangingEntity(Level* level);
HangingEntity(Level* level, int xTile, int yTile, int zTile, int dir);
void init();
void setDir(int dir);
void setPosition(int x, int y, int z);
virtual void tick();
virtual bool survives();
bool isPickable();
bool interact(Player* player);
void move(float xa, float ya, float za);
void push(float xa, float ya, float za);
virtual void addAdditonalSaveData(CompoundTag* tag);
virtual void readAdditionalSaveData(CompoundTag* tag);
virtual int getWidth() = 0;
virtual int getHeight() = 0;
virtual void dropItem() = 0;
virtual bool isHangingEntity();
virtual float getBrightness(float a);
virtual bool hurt(Entity* source, int damage);
private:
float offs(int w);
public:
int dir;
int xTile, yTile, zTile;
private:
int checkInterval;
};
#endif /* NET_MINECRAFT_WORLD_ENTITY__HangingEntity_H__ */

1144
src/world/entity/Mob.cpp Executable file

File diff suppressed because it is too large Load Diff

249
src/world/entity/Mob.h Executable file
View File

@@ -0,0 +1,249 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__Mob_H__
#define NET_MINECRAFT_WORLD_ENTITY__Mob_H__
//package net.minecraft.world.entity;
#include <string>
#include "Entity.h"
#include "EntityTypes.h"
#include "SynchedEntityData.h"
class CompoundTag;
class Level;
class CompundTag;
class MoveControl;
class JumpControl;
class PathNavigation;
class GoalSelector;
class Sensing;
class Mob: public Entity
{
typedef Entity super;
public:
static const int ATTACK_DURATION = 5;
static const int SWING_DURATION = 8;
Mob(Level* level);
virtual ~Mob();
void _init();
virtual void reset();
virtual void knockback(Entity* source, int dmg, float xd, float zd);
virtual void die(Entity* source);
virtual bool canSee(Entity* target);
virtual bool onLadder();
virtual void spawnAnim();
virtual std::string getTexture();
virtual bool isAlive();
virtual bool isPickable();
virtual bool isPushable();
virtual bool isShootable();
MoveControl* getMoveControl();
JumpControl* getJumpControl();
virtual bool isSleeping();
virtual bool isWaterMob();
virtual bool isSneaking();
virtual void setSneaking(bool value);
virtual float getHeadHeight();
virtual float getVoicePitch();
virtual void playAmbientSound();
virtual int getAmbientSoundInterval();
virtual int getItemInHandIcon(ItemInstance* item, int layer) {
return item->getIcon();
}
virtual void lerpTo(float x, float y, float z, float yRot, float xRot, int steps);
void setYya( float yya );
float getSpeed();
void setSpeed(float speed);
void setJumping(bool jump);
virtual void tick();
virtual void baseTick();
virtual void superTick();
virtual void heal(int heal);
virtual int getMaxHealth() { return 10; }
virtual bool hurt(Entity* source, int dmg);
virtual void actuallyHurt(int dmg);
virtual void animateHurt();
virtual int getArmorValue();
virtual HitResult pick(float range, float a);
virtual void travel(float xa, float ya);
virtual void updateWalkAnim();
virtual void aiStep();
virtual SynchedEntityData* getEntityData();
virtual const SynchedEntityData* getEntityData() const;
virtual void addAdditonalSaveData(CompoundTag* entityTag);
virtual void readAdditionalSaveData(CompoundTag* tag);
virtual void lookAt(Entity* e, float yMax, float xMax);
virtual bool isLookingAtAnEntity();
//virtual Entity* getLookingAt();
virtual void beforeRemove();
virtual bool canSpawn();
virtual float getAttackAnim(float a);
virtual Vec3 getPos(float a);
virtual Vec3 getLookAngle();
virtual Vec3 getViewVector(float a);
virtual int getMaxSpawnClusterSize();
virtual bool isMob() { return true; }
virtual bool isBaby() { return false; }
virtual void handleEntityEvent(char id);
virtual ItemInstance* getCarriedItem() {return NULL;}
virtual int getUseItemDuration() {return 0;}
virtual void swing();
protected:
virtual void causeFallDamage(float distance);
virtual void outOfWorld();
virtual bool removeWhenFarAway();
virtual int getDeathLoot();
virtual void dropDeathLoot();
virtual bool isImmobile();
virtual void jumpFromGround();
virtual void updateAi();
virtual void newServerAiStep();
virtual void setSize(float w, float h);
virtual int getMaxHeadXRot();
virtual float getSoundVolume();
virtual const char* getAmbientSound();
virtual std::string getHurtSound();
virtual std::string getDeathSound();
virtual float getWalkingSpeedModifier();
virtual int getDamageAfterArmorAbsorb(int damage);
virtual void hurtArmor(int damage);
bool interpolateOnly();
virtual bool useNewAi();
bool getSharedFlag(int flag);
void setSharedFlag(int flag, bool value);
void checkDespawn(Mob* nearestBlocking);
void checkDespawn();
void updateAttackAnim();
private:
float rotlerp(float a, float b, float max);
public:
int invulnerableDuration;
float timeOffs;
float rotA;
float yBodyRot, yBodyRotO;
//bool interpolateOnly;
float oAttackAnim, attackAnim;
int health;
int lastHealth;
int hurtTime;
int hurtDuration;
float hurtDir;
int deathTime;
int attackTime;
float oTilt, tilt;
int lookTime;
float fallTime;
float walkAnimSpeedO;
float walkAnimSpeed;
float walkAnimPos;
Vec3 aimDirection;
int arrowCount;
int removeArrowTime;
Random random;
Sensing* sensing;
//@note: This is a temporary fix for DamageSource that bypasses armor
// or creating two functions, that does virtually the same
// except one bypasses armor. It's enough virtual calls and chains
// of super-calls for me to think this is safer.
bool bypassArmor;
protected:
SynchedEntityData entityData;
bool swinging;
int swingTime;
int noActionTime;
float xxa, yya, yRotA;
float defaultLookAngle;
float runSpeed;
float walkingSpeed;
float flyingSpeed;
std::string textureName;
std::string modelName;
int deathScore;
float oRun, run;
float animStep, animStepO;
float rotOffs;
float bobStrength;
float renderOffset;
int lSteps;
float lx, ly, lz, lyr, lxr;
int lastHurt;
int dmgSpill;
float sentX, sentY, sentZ, sentRotX, sentRotY;
float sentXd, sentYd, sentZd;
//bool hasHair;
bool allowAlpha;
bool jumping;
bool autoSendPosRot;
MoveControl* moveControl;
JumpControl* jumpControl;
PathNavigation* navigation;
GoalSelector* goalSelector;
GoalSelector* targetSelector;
private:
int lookingAtId;
int ambientSoundTime;
float speed;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__Mob_H__*/

View File

@@ -0,0 +1,50 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__MobCategory_H__
#define NET_MINECRAFT_WORLD_ENTITY__MobCategory_H__
#include "EntityTypes.h"
#include "MobCategory.h"
#include "../level/material/Material.h"
const MobCategory MobCategory::monster(
MobTypes::BaseEnemy,
10,
20,
false);
//
const MobCategory MobCategory::creature(
MobTypes::BaseCreature,
10,
15,
true);
//
const MobCategory MobCategory::waterCreature(
MobTypes::BaseWaterCreature,
5,
10,
true);
//
// Init an array with all defined MobCategory'ies
//
const MobCategory* const MobCategory::values[] = {
&MobCategory::monster,
&MobCategory::creature,
&MobCategory::waterCreature
};
/*static*/
void MobCategory::initMobCategories() {
monster.setMaterial(Material::air);
creature.setMaterial(Material::air);
waterCreature.setMaterial(Material::water);
}
const int MobCategory::numValues = sizeof(values) / sizeof(values[0]);
#endif /*NET_MINECRAFT_WORLD_ENTITY__MobCategory_H__*/

60
src/world/entity/MobCategory.h Executable file
View File

@@ -0,0 +1,60 @@
//package net.minecraft.world.entity;
#include "../../platform/log.h"
class Material;
class MobCategory
{
public:
static void initMobCategories();
//
static const MobCategory monster;
static const MobCategory creature;
static const MobCategory waterCreature;
//@todo: rewrite to std::vector with [] init
static const MobCategory* const values[];
static const int numValues;
int getBaseClassId() const {
return _baseClassId;
}
int getMaxInstancesPerChunk() const {
return _max;
}
int getMaxInstancesPerLevel() const {
return _maxPerLevel;
}
const Material* getSpawnPositionMaterial() const {
return _spawnPositionMaterial;
}
bool isFriendly() const {
return _isFriendly;
}
private:
const int _baseClassId;
const int _max;
const int _maxPerLevel;
mutable const Material* _spawnPositionMaterial;
const bool _isFriendly;
MobCategory(int baseClassId, int max, int maxPerLevel, bool isFriendly)
: _baseClassId(baseClassId),
_max(max),
_maxPerLevel(maxPerLevel),
_spawnPositionMaterial(NULL),
_isFriendly(isFriendly)
{
//LOGI("Creating a Mobcategory: %d, %d, %p, %d ", _baseClassId, _max, _spawnPositionMaterial, _isFriendly);
}
void setMaterial(const Material* material) const {
_spawnPositionMaterial = material;
}
};

90
src/world/entity/MobFactory.h Executable file
View File

@@ -0,0 +1,90 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__MobFactory_H__
#define NET_MINECRAFT_WORLD_ENTITY__MobFactory_H__
#include "EntityTypes.h"
#include "animal/AnimalInclude.h"
#include "monster/MonsterInclude.h"
class MobFactory {
public:
static Mob* CreateMob(int mobType, Level* level) {
//LOGI("Trying to create a mob with type: %d!\n", mobType);
Mob* mob = NULL;
switch(mobType) {
// Animals
case MobTypes::Chicken:
mob = new Chicken(level);
break;
case MobTypes::Cow:
mob = new Cow(level);
break;
case MobTypes::Pig:
mob = new Pig(level);
break;
case MobTypes::Sheep:
mob = new Sheep(level);
break;
// Monsters
case MobTypes::Creeper:
mob = new Creeper(level);
break;
case MobTypes::Zombie:
mob = new Zombie(level);
break;
case MobTypes::Skeleton:
mob = new Skeleton(level);
break;
case MobTypes::Spider:
mob = new Spider(level);
break;
case MobTypes::PigZombie:
mob = new PigZombie(level);
break;
default:
LOGE("Unknown mob type requested: %d\n", mobType);
break;
}
if (mob) {
mob->health = mob->getMaxHealth();
}
return mob;
}
static void clearStaticTestMobs() {
getStaticTestMob(0, NULL);
}
// @huge @attn: Those needs to be cleared for every new level
static Mob* getStaticTestMob(int mobType, Level* level) {
static std::map<int, Mob*> _mobs;
static Level* lastLevel = NULL;
bool wantClear = (mobType == 0) && (level == NULL);
bool newLevel = (level != lastLevel);
lastLevel = level;
// We either want to clear all mobs, or a new level is created
if (wantClear || newLevel) {
for (std::map<int, Mob*>::iterator it = _mobs.begin(); it != _mobs.end(); ++it)
delete it->second;
_mobs.clear();
if (wantClear) return NULL;
}
std::map<int, Mob*>::iterator it = _mobs.find(mobType);
if (it != _mobs.end())
return it->second;
// Didn't exist, add it
Mob* mob = CreateMob(mobType, level);
_mobs.insert(std::make_pair(mobType, mob));
return mob;
}
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__MobFactory_H__*/

84
src/world/entity/Motive.cpp Executable file
View File

@@ -0,0 +1,84 @@
#include "Motive.h"
const Motive Motive::Kebab("Kebab", 16, 16, 0 * 16, 0 * 16);
const Motive Motive::Aztec("Aztec", 16, 16, 1 * 16, 0 * 16);
const Motive Motive::Alban("Alban", 16, 16, 2 * 16, 0 * 16);
const Motive Motive::Aztec2("Aztec2", 16, 16, 3 * 16, 0 * 16);
const Motive Motive::Bomb("Bomb", 16, 16, 4 * 16, 0 * 16);
const Motive Motive::Plant("Plant", 16, 16, 5 * 16, 0 * 16);
const Motive Motive::Wasteland("Wasteland", 16, 16, 6 * 16, 0 * 16);
const Motive Motive::Pool("Pool", 32, 16, 0 * 16, 2 * 16);
const Motive Motive::Courbet("Courbet", 32, 16, 2 * 16, 2 * 16);
const Motive Motive::Sea("Sea", 32, 16, 4 * 16, 2 * 16);
const Motive Motive::Sunset("Sunset", 32, 16, 6 * 16, 2 * 16);
const Motive Motive::Creebet("Creebet", 32, 16, 8 * 16, 2 * 16);
const Motive Motive::Wanderer("Wanderer", 16, 32, 0 * 16, 4 * 16);
const Motive Motive::Graham("Graham", 16, 32, 1 * 16, 4 * 16);
const Motive Motive::Match("Match", 32, 32, 0 * 16, 8 * 16);
const Motive Motive::Bust("Bust", 32, 32, 2 * 16, 8 * 16);
const Motive Motive::Stage("Stage", 32, 32, 4 * 16, 8 * 16);
const Motive Motive::Void("Void", 32, 32, 6 * 16, 8 * 16);
const Motive Motive::SkullAndRoses("SkullAndRoses", 32, 32, 8 * 16, 8 * 16);
const Motive Motive::Fighters("Fighters", 64, 32, 0 * 16, 6 * 16);
const Motive Motive::Pointer("Pointer", 64, 64, 0 * 16, 12 * 16);
const Motive Motive::Pigscene("Pigscene", 64, 64, 4 * 16, 12 * 16);
const Motive Motive::BurningSkull("BurningSkull", 64, 64, 8 * 16, 12 * 16);
const Motive Motive::Skeleton("Skeleton", 64, 48, 12 * 16, 4 * 16);
const Motive Motive::DonkeyKong("DonkeyKong", 64, 48, 12 * 16, 7 * 16);
const Motive Motive::Earth("Earth", 32, 32, 0 * 16, 10 * 16, false);
const Motive Motive::Wind("Wind", 32, 32, 2 * 16, 10 * 16, false);
const Motive Motive::Fire("Fire", 32, 32, 4 * 16, 10 * 16, false);
const Motive Motive::Water("Water", 32, 32, 6 * 16, 10 * 16, false);
const Motive* Motive::DefaultImage = &Motive::Kebab;
std::vector<const Motive*> Motive::getAllMotivesAsList() {
std::vector<const Motive*> motives;
motives.push_back(&Kebab);
motives.push_back(&Aztec2);
motives.push_back(&Alban);
motives.push_back(&Bomb);
motives.push_back(&Plant);
motives.push_back(&Wasteland);
motives.push_back(&Pool);
motives.push_back(&Courbet);
motives.push_back(&Sea);
motives.push_back(&Sunset);
motives.push_back(&Creebet);
motives.push_back(&Wanderer);
motives.push_back(&Graham);
motives.push_back(&Match);
motives.push_back(&Bust);
motives.push_back(&Stage);
motives.push_back(&Void);
motives.push_back(&SkullAndRoses);
motives.push_back(&Fighters);
motives.push_back(&Pointer);
motives.push_back(&Pigscene);
motives.push_back(&BurningSkull);
motives.push_back(&Skeleton);
motives.push_back(&DonkeyKong);
motives.push_back(&Earth);
motives.push_back(&Wind);
motives.push_back(&Fire);
motives.push_back(&Water);
return motives;
}
const Motive* Motive::getMotiveByName( const std::string& name ) {
std::vector<const Motive*> allMovies = getAllMotivesAsList();
for(std::vector<const Motive*>::iterator i = allMovies.begin(); i != allMovies.end(); ++i) {
if((*i)->name == name)
return *i;
}
return DefaultImage;
}
Motive::Motive( std::string name, int w, int h, int uo, int vo, bool isPublic /*= true*/ )
: name(name),
w(w),
h(h),
uo(uo),
vo(vo),
isPublic(isPublic)
{}

53
src/world/entity/Motive.h Executable file
View File

@@ -0,0 +1,53 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__Motive_H__
#define NET_MINECRAFT_WORLD_ENTITY__Motive_H__
#include <string>
#include <vector>
class Motive {
public:
static const int MAX_MOTIVE_NAME_LENGTH = 13; // "SkullAndRoses".length();
Motive(std::string name, int w, int h, int uo, int vo, bool isPublic = true);
static std::vector<const Motive*> getAllMotivesAsList();
static const Motive* getMotiveByName(const std::string& name);
public:
const std::string name;
const int w, h;
const int uo, vo;
const bool isPublic;
static const Motive* DefaultImage;
static const Motive Kebab;
static const Motive Aztec;
static const Motive Alban;
static const Motive Aztec2;
static const Motive Bomb;
static const Motive Plant;
static const Motive Wasteland;
static const Motive Pool;
static const Motive Courbet;
static const Motive Sea;
static const Motive Sunset;
static const Motive Creebet;
static const Motive Wanderer;
static const Motive Graham;
static const Motive Match;
static const Motive Bust;
static const Motive Stage;
static const Motive Void;
static const Motive SkullAndRoses;
static const Motive Fighters;
static const Motive Pointer;
static const Motive Pigscene;
static const Motive BurningSkull;
static const Motive Skeleton;
static const Motive DonkeyKong;
static const Motive Earth;
static const Motive Wind;
static const Motive Fire;
static const Motive Water;
};
#endif /* NET_MINECRAFT_WORLD_ENTITY__Motive_H__ */

77
src/world/entity/Painting.cpp Executable file
View File

@@ -0,0 +1,77 @@
#include "Painting.h"
#include "../level/Level.h"
#include "../item/Item.h"
Painting::Painting( Level* level ) : super(level) {
entityRendererId = ER_PAINTING_RENDERER;
}
Painting::Painting( Level* level, int xTile, int yTile, int zTile, int dir )
: super(level, xTile, yTile, zTile, dir) {
setRandomMotive(dir);
entityRendererId = ER_PAINTING_RENDERER;
}
Painting::Painting( Level* level, int x, int y, int z, int dir, const std::string& motiveName )
: super(level, x, y, z, dir) {
motive = Motive::getMotiveByName(motiveName);
setDir(dir);
entityRendererId = ER_PAINTING_RENDERER;
}
void Painting::addAdditonalSaveData( CompoundTag* tag ) {
if(motive != NULL) {
tag->putString("Motive", motive->name);
}
super::addAdditonalSaveData(tag);
}
void Painting::readAdditionalSaveData( CompoundTag* tag ) {
std::string motiveName = tag->getString("Motive");
motive = Motive::getMotiveByName(motiveName);
super::readAdditionalSaveData(tag);
}
int Painting::getWidth() {
return motive->w;
}
int Painting::getHeight() {
return motive->h;
}
void Painting::dropItem() {
if(level->getLevelData()->getGameType() != GameType::Creative)
spawnAtLocation(Item::painting->id, 1);
}
int Painting::getEntityTypeId() const {
return EntityTypes::IdPainting;
}
void Painting::setRandomMotive( int dir ) {
std::vector<const Motive*> allMotives = Motive::getAllMotivesAsList();
std::vector<const Motive*> survivableMotives;
for(std::vector<const Motive*>::iterator i = allMotives.begin(); i != allMotives.end(); ++i) {
if (!(*i)->isPublic)
continue;
motive = *i;
setDir(dir);
if(survives()) {
survivableMotives.push_back(*i);
}
}
if(!survivableMotives.empty()) {
this->motive = survivableMotives[sharedRandom.nextInt(survivableMotives.size())];
setDir(dir);
}
else {
this->motive = Motive::DefaultImage;
setDir(dir);
}
}
bool Painting::isPickable() {
return true;
}

27
src/world/entity/Painting.h Executable file
View File

@@ -0,0 +1,27 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__Painting_H__
#define NET_MINECRAFT_WORLD_ENTITY__Painting_H__
#include "Motive.h"
#include "HangingEntity.h"
class Painting : public HangingEntity {
typedef HangingEntity super;
public:
Painting(Level* level);
Painting(Level* level, int xTile, int yTile, int zTile, int dir);
Painting(Level* level, int x, int y, int z, int dir, const std::string& motiveName);
void setRandomMotive( int dir );
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
int getWidth();
int getHeight();
void dropItem();
int getEntityTypeId() const;
bool isPickable();
public:
const Motive* motive;
};
#endif /* NET_MINECRAFT_WORLD_ENTITY__Painting_H__ */

View File

@@ -0,0 +1,248 @@
#include "PathfinderMob.h"
#include "../level/Level.h"
#include "../phys/Vec3.h"
#include "../../util/Mth.h"
#include "../../util/PerfTimer.h"
#include "../../SharedConstants.h"
#include "ai/Sensing.h"
PathfinderMob::PathfinderMob( Level* level )
: super(level),
//navigation(this, level, 16),
attackTargetId(0),
holdGround(false),
fleeTime(0)//,
//pathfinderMask(0)
{
sensing = new Sensing(this);
navigation = new PathNavigation(this, level, 16);
}
PathfinderMob::~PathfinderMob() {
delete navigation;
delete sensing;
}
bool PathfinderMob::canSpawn()
{
return super::canSpawn() && getWalkTargetValue(Mth::floor(x), Mth::floor(bb.y0), Mth::floor(z)) >= 0;
}
bool PathfinderMob::isPathFinding()
{
return !path.isEmpty();
}
void PathfinderMob::setPath( Path& path )
{
this->path = path;
}
Entity* PathfinderMob::getAttackTarget()
{
if (attackTargetId == 0) return NULL;
return level->getEntity(attackTargetId);
}
void PathfinderMob::setAttackTarget( Entity* attacker )
{
attackTargetId = attacker? attacker->entityId : 0;
}
bool PathfinderMob::shouldHoldGround()
{
return false;
}
void PathfinderMob::updateAi()
{
TIMER_PUSH("ai");
if (fleeTime > 0) fleeTime--;
holdGround = shouldHoldGround();
float maxDist = 16;
Entity* attackTarget = NULL;
if (attackTargetId == 0) {
attackTarget = findAttackTarget();
if (attackTarget != NULL) {
level->findPath(&path, this, attackTarget, maxDist, false, false);//(pathfinderMask&CAN_OPEN_DOORS) != 0, (pathfinderMask&AVOID_WATER) != 0);
attackTargetId = attackTarget->entityId;
//LOGI("path.empty: %d\n", path.isEmpty());
}
} else {
attackTarget = level->getEntity(attackTargetId);
if (!attackTarget || !attackTarget->isAlive()) {
attackTargetId = 0;
attackTarget = NULL;
} else {
attackTargetId = attackTarget->entityId;
float d = attackTarget->distanceTo(this);
if (canSee(attackTarget)) {
checkHurtTarget(attackTarget, d);
} else {
checkCantSeeTarget(attackTarget, d);
}
}
}
TIMER_POP();
/*
* if (holdGround) { xxa = 0; yya = 0; jumping = false; return; }
*/
bool doStroll = false;
if (!holdGround && (attackTarget != NULL && (path.isEmpty() || random.nextInt(20) == 0))) {
level->findPath(&path, this, attackTarget, maxDist, false, false);//(pathfinderMask&CAN_OPEN_DOORS) != 0, (pathfinderMask&AVOID_WATER) != 0);
} else if (!holdGround) {
if (path.isEmpty() && (random.nextInt(180) == 0)) {
doStroll = true;
} else {
if (random.nextInt(120) == 0) doStroll = true;
else if (fleeTime > 0 && (fleeTime&7) == 1) doStroll = true;
}
}
if (doStroll) {
if (noActionTime < SharedConstants::TicksPerSecond * 5) {
findRandomStrollLocation();
}
}
int yFloor = Mth::floor(bb.y0 + .5f);
bool inWater = isInWater();
bool inLava = isInLava();
xRot = 0;
if (path.isEmpty() || random.nextInt(100) == 0) {
//super::serverAiStep();
super::updateAi();
return;
}
TIMER_PUSH("followpath");
Vec3 target = path.currentPos(this);
float r = bbWidth * 2;
bool looping = true;
while (looping && target.distanceToSqr(x, target.y, z) < r * r) {
path.next();
if (path.isDone()) {
looping = false;
path.destroy();
} else target = path.currentPos(this);
}
jumping = false;
if (looping) {
float xd = target.x - x;
float zd = target.z - z;
float yd = target.y - yFloor;
// float yRotOld = yRot;
float yRotD = (float) (Mth::atan2(zd, xd) * 180 / Mth::PI) - 90;
float rotDiff = yRotD - yRot;
yya = runSpeed;
while (rotDiff < -180)
rotDiff += 360;
while (rotDiff >= 180)
rotDiff -= 360;
if (rotDiff > MAX_TURN) {
rotDiff = MAX_TURN;
// yya *= 0.2;
}
if (rotDiff < -MAX_TURN) {
rotDiff = -MAX_TURN;
// yya *= 0.2;
}
yRot += rotDiff;
if (holdGround) {
if (attackTarget != NULL) {
float xd2 = attackTarget->x - x;
float zd2 = attackTarget->z - z;
float oldyRot = yRot;
yRot = (float) (Mth::atan2(zd2, xd2) * 180 / Mth::PI) - 90;
rotDiff = ((oldyRot - yRot) + 90) * Mth::PI / 180;
xxa = -Mth::sin(rotDiff) * yya * 1.0f;
yya = Mth::cos(rotDiff) * yya * 1.0f;
}
}
if (yd > 0) {
jumping = true;
}
}
if (attackTarget != NULL) {
lookAt(attackTarget, 30, 30);
}
if (this->horizontalCollision && !isPathFinding()) jumping = true;
if (random.nextFloat() < 0.8f && (inWater || inLava)) jumping = true;
TIMER_POP();
}
void PathfinderMob::findRandomStrollLocation()
{
TIMER_PUSH("stroll");
bool hasBest = false;
int xBest = -1;
int yBest = -1;
int zBest = -1;
float best = -99999;
for (int i = 0; i < 10; i++) {
int xt = Mth::floor(x + random.nextInt(13) - 6);
int yt = Mth::floor(y + random.nextInt(7) - 3);
int zt = Mth::floor(z + random.nextInt(13) - 6);
float value = getWalkTargetValue(xt, yt, zt);
if (value > best) {
best = value;
xBest = xt;
yBest = yt;
zBest = zt;
hasBest = true;
}
}
if (hasBest) {
//LOGI("Finding a new strolling location! %d, %d, %d (%d, %d, %d) for %p\n", xBest, yBest, zBest, (int)x, (int)y, (int)z, this);
level->findPath(&path, this, xBest, yBest, zBest, 10, false, false);//(pathfinderMask&CAN_OPEN_DOORS) != 0, (pathfinderMask&AVOID_WATER) != 0);
}
TIMER_POP();
}
void PathfinderMob::checkHurtTarget( Entity* target, float d )
{
}
void PathfinderMob::checkCantSeeTarget( Entity* target, float d )
{
if (d > 32)
attackTargetId = 0;
}
float PathfinderMob::getWalkTargetValue( int x, int y, int z )
{
return 0;
}
Entity* PathfinderMob::findAttackTarget()
{
return NULL;
}
float PathfinderMob::getWalkingSpeedModifier() {
float speed = super::getWalkingSpeedModifier();
if (fleeTime > 0) speed *= 2;
return speed;
}
int PathfinderMob::getNoActionTime() {
return noActionTime;
}
PathNavigation* PathfinderMob::getNavigation() {
return navigation;
}

View File

@@ -0,0 +1,65 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__PathfinderMob_H__
#define NET_MINECRAFT_WORLD_ENTITY__PathfinderMob_H__
//package net.minecraft.world.entity;
#include "Mob.h"
#include "ai/PathNavigation.h"
#include "../level/pathfinder/Path.h"
class Level;
class Sensing;
typedef struct AIData {
AIData():
target(NULL)
{}
Mob* target;
} AIData;
class PathfinderMob: public Mob
{
typedef Mob super;
static const int MAX_TURN = 30;
public:
PathfinderMob(Level* level);
~PathfinderMob();
bool canSpawn();
bool isPathFinding();
void setPath(Path& path);
virtual Entity* getAttackTarget();
virtual void setAttackTarget(Entity* attacker);
virtual float getWalkTargetValue(int x, int y, int z);
int getNoActionTime();
PathNavigation* getNavigation();
protected:
virtual Entity* findAttackTarget();
virtual void checkHurtTarget(Entity* target, float d);
virtual void checkCantSeeTarget(Entity* target, float d);
virtual float getWalkingSpeedModifier();
virtual bool shouldHoldGround();
void updateAi();
virtual void findRandomStrollLocation();
int attackTargetId;
bool holdGround;
int fleeTime;
public:
static const int CAN_OPEN_DOORS = 1;
static const int AVOID_WATER = 2;
//int pathfinderMask;
private:
Path path;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__PathfinderMob_H__*/

17
src/world/entity/SharedFlags.h Executable file
View File

@@ -0,0 +1,17 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__SharedFlags_H__
#define NET_MINECRAFT_WORLD_ENTITY__SharedFlags_H__
class SharedFlagsInformation {
public:
enum SharedFlags {
FLAG_ONFIRE = 0,
FLAG_SNEAKING = 1,
FLAG_RIDING = 2,
FLAG_SPRINTING = 3,
FLAG_USINGITEM = 4
};
typedef char SharedFlagsInformationType;
static const unsigned int DATA_SHARED_FLAGS_ID = 0;
};
#endif /* NET_MINECRAFT_WORLD_ENTITY__SharedFlags_H__ */

View File

@@ -0,0 +1,250 @@
#include "SynchedEntityData.h"
SynchedEntityData::SynchedEntityData()
: _isDirty(false)
{
}
SynchedEntityData::~SynchedEntityData()
{
for (Map::iterator it = itemsById.begin(); it != itemsById.end(); ++it) {
delete (it->second);
}
}
void SynchedEntityData::pack( DataList* items, IDataOutput* output )
{
if (items != NULL) {
for (DataList::iterator it = items->begin(); it != items->end(); ++it)
writeDataItem(output, *it);
}
// add an eof
output->writeByte(EOF_MARKER);
}
SynchedEntityData::DataList SynchedEntityData::packDirty()
{
DataList result;
if (_isDirty) {
for (Map::iterator it = itemsById.begin(); it != itemsById.end(); ++it) {
DataItem* dataItem = it->second;
if (dataItem->isDirty()) {
dataItem->setDirty(false);
result.push_back(dataItem);
}
}
}
_isDirty = false;
return result;
}
void SynchedEntityData::packAll( IDataOutput* output ) const
{
for (Map::const_iterator cit = itemsById.begin(); cit != itemsById.end(); ++cit)
SynchedEntityData::writeDataItem(output, cit->second);
// add an eof
output->writeByte(EOF_MARKER);
}
SynchedEntityData::DataList SynchedEntityData::unpack( IDataInput* input )
{
DataList result;
int currentHeader = input->readByte();
while (currentHeader != EOF_MARKER) {
// split type and id
int itemType = (currentHeader & TYPE_MASK) >> TYPE_SHIFT;
int itemId = (currentHeader & MAX_ID_VALUE);
//LOGI("unpacking: %d, %d\n", itemId, itemType);
DataItem* item = NULL;
switch (itemType) {
case TYPE_BYTE:
item = new DataItem2<TypeChar>(itemType, itemId, input->readByte());
break;
case TYPE_SHORT:
item = new DataItem2<TypeShort>(itemType, itemId, input->readShort());
break;
case TYPE_INT:
item = new DataItem2<TypeInt>(itemType, itemId, input->readInt());
break;
case TYPE_FLOAT:
item = new DataItem2<TypeFloat>(itemType, itemId, input->readFloat());
break;
case TYPE_STRING:
item = new DataItem2<std::string>(itemType, itemId, input->readString());
break;
case TYPE_ITEMINSTANCE: {
int id = input->readShort();
int count = input->readByte();
int auxValue = input->readShort();
item = new DataItem2<ItemInstance>(itemType, itemId, ItemInstance(id, count, auxValue));
}
break;
case TYPE_POS: {
int x = input->readInt();
int y = input->readInt();
int z = input->readInt();
item = new DataItem2<Pos>(itemType, itemId, Pos(x, y, z));
}
break;
}
result.push_back(item);
currentHeader = input->readByte();
}
return result;
}
void SynchedEntityData::assignValues( DataList* items )
{
for (DataList::const_iterator it = items->begin(); it != items->end(); ++it) {
//for (DataItem item : items) {
DataItem* item = *it;
Map::iterator jt = itemsById.find(item->getId());
//DataItem dataItem = itemsById.get(item.getId());
if (jt != itemsById.end()) {
switch (item->getType()) {
case TYPE_BYTE : set(jt->second, ((DataItem2<TypeChar>*)item)->data); break;
case TYPE_SHORT: set(jt->second, ((DataItem2<TypeShort>*)item)->data); break;
case TYPE_INT : set(jt->second, ((DataItem2<TypeInt>*)item)->data); break;
case TYPE_FLOAT: set(jt->second, ((DataItem2<TypeFloat>*)item)->data); break;
case TYPE_STRING:set(jt->second, ((DataItem2<std::string>*)item)->data); break;
case TYPE_ITEMINSTANCE: set(jt->second, ((DataItem2<ItemInstance>*)item)->data); break;
case TYPE_POS: set(jt->second, ((DataItem2<Pos>*)item)->data); break;
default:
LOGE("Incorrect type id: %d\n", item->getType());
break;
}
//dataItem->setValue(item->getValue());
}
}
}
void SynchedEntityData::writeDataItem( IDataOutput* output, const DataItem* dataItem )
{
//LOGI("write: %d, %d\n", dataItem->getId(), dataItem->getType());
// pack type and id
//LOGI("dataItem: %d\n", dataItem);
int header = ((dataItem->getType() << TYPE_SHIFT) | (dataItem->getId() & MAX_ID_VALUE)) & 0xff;
output->writeByte(header);
// write value
switch (dataItem->getType()) {
case TYPE_BYTE:
output->writeByte(((DataItem2<TypeChar>*)dataItem)->data);
break;
case TYPE_SHORT:
output->writeShort(((DataItem2<TypeShort>*)dataItem)->data);
break;
case TYPE_INT:
output->writeInt(((DataItem2<TypeInt>*)dataItem)->data);
break;
case TYPE_FLOAT:
output->writeFloat(((DataItem2<TypeFloat>*)dataItem)->data);
break;
case TYPE_STRING:
output->writeString(((DataItem2<std::string>*)dataItem)->data);
break;
case TYPE_ITEMINSTANCE: {
const ItemInstance& instance = ((DataItem2<ItemInstance>*)dataItem)->data;
output->writeShort(instance.getItem()->id);
output->writeByte(instance.count);
output->writeShort(instance.getAuxValue());
}
break;
case TYPE_POS: {
const Pos& instance = ((DataItem2<Pos>*)dataItem)->data;
output->writeInt(instance.x);
output->writeInt(instance.y);
output->writeInt(instance.z);
}
break;
}
}
SynchedEntityData::TypeChar SynchedEntityData::getByte( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_BYTE) {
return ((DataItem2<TypeChar>*)it->second)->data;
}
}
return 0;
}
SynchedEntityData::TypeShort SynchedEntityData::getShort( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_SHORT) {
return ((DataItem2<TypeShort>*)it->second)->data;
}
}
return 0;
}
SynchedEntityData::TypeInt SynchedEntityData::getInt( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_INT) {
return ((DataItem2<TypeInt>*)it->second)->data;
}
}
return 0;
}
SynchedEntityData::TypeFloat SynchedEntityData::getFloat( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_FLOAT) {
return ((DataItem2<TypeFloat>*)it->second)->data;
}
}
return 0;
}
std::string SynchedEntityData::getString( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_STRING) {
return ((DataItem2<std::string>*)it->second)->data;
}
}
return "";
}
ItemInstance SynchedEntityData::getItemInstance( int id )
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_ITEMINSTANCE) {
return ((DataItem2<ItemInstance>*)it->second)->data;
}
}
return ItemInstance();
}
Pos SynchedEntityData::getPos( int id ) const
{
Map::const_iterator it = itemsById.find(id);
if (it != itemsById.end()) {
if (it->second->getType() == TYPE_POS) {
return ((DataItem2<Pos>*)it->second)->data;
}
}
return Pos();
}

View File

@@ -0,0 +1,251 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY__SynchedEntityData_H__
#define NET_MINECRAFT_WORLD_ENTITY__SynchedEntityData_H__
//package net.minecraft.world.entity;
#include "../Pos.h"
#include "../../network/Packet.h"
#include "../item/ItemInstance.h"
#include "../item/Item.h"
#include "../../util/DataIO.h"
class DataItem {
public:
DataItem(int type, int id)
: type(type),
id(id),
dirty(true)
{}
virtual ~DataItem() {}
int getId() const {
return id;
}
int getType() const {
return type;
}
bool isDirty() const {
return dirty;
}
void setDirty(bool dirty) {
this->dirty = dirty;
}
virtual bool isDataEqual(const DataItem& rhs) const {
return type == rhs.type;
}
private:
const int type;
const int id;
bool dirty;
/*
public:
// attn: Very un-typesafe, but let's go
union {
char _char;
short _short;
int _int;
float _float;
std::string _String;
ItemInstance _ItemInstance;
Pos _Pos;
};
*/
};
template <class T>
class DataItem2: public DataItem {
typedef DataItem super;
public:
DataItem2(int type, int id, const T& data)
: super(type, id),
data(data)
{}
void setFlag(int flag) {
data = data | (1 << flag);
}
void clearFlag(int flag) {
data = data & ~(1 << flag);
}
bool getFlag(int flag) const {
return (data & (1 << flag)) != 0;
}
T data;
};
/*
#define DataItemClassFactory2(Postfix, TypeName) \
class DataItem_ ## Postfix : public DataItem { \
typedef DataItem super; \
public: \
DataItem_ ## Postfix(int type, int id, #Typename data) \
: super(type), \
data(data) \
{} \
virtual bool isDataEqual(const DataItem& rhs) const { \
if (!super::isDataEqual(rhs)) return; \
return data == (DataItem_ ## #Postfix&)rhs.data; \
} \
#Typename data; \
};
#define DataItemClassFactory(TypeName) DataItemClassFactory2(TypeName, TypeName)
DataItemClassFactory(char);
DataItemClassFactory(short);
DataItemClassFactory(int);
DataItemClassFactory(float);
DataItemClassFactory(ItemInstance);
DataItemClassFactory(Pos);
DataItemClassFactory2(String, std::string);
*/
class SynchedEntityData {
public:
static const int MAX_STRING_DATA_LENGTH = 64;
static const int EOF_MARKER = 0x7f;
typedef signed char TypeChar;
typedef short TypeShort;
typedef int TypeInt;
typedef float TypeFloat;
private:
static const int TYPE_BYTE = 0;
static const int TYPE_SHORT = 1;
static const int TYPE_INT = 2;
static const int TYPE_FLOAT = 3;
static const int TYPE_STRING = 4;
// special types (max possible value is 7):
static const int TYPE_ITEMINSTANCE = 5;
static const int TYPE_POS = 6;
// must have enough bits to fit the type
static const int TYPE_MASK = 0xe0;
static const int TYPE_SHIFT = 5;
// the id value must fit in the remaining bits
static const int MAX_ID_VALUE = ~TYPE_MASK & 0xff;
typedef std::map<int, DataItem*> Map;
public:
typedef std::vector<DataItem*> DataList;
SynchedEntityData();
~SynchedEntityData();
int getTypeId(const TypeChar &) { return TYPE_BYTE; }
int getTypeId(const TypeShort&) { return TYPE_SHORT; }
int getTypeId(const TypeInt&) { return TYPE_INT; }
int getTypeId(const TypeFloat&) { return TYPE_FLOAT; }
int getTypeId(const std::string&){ return TYPE_STRING; }
int getTypeId(const ItemInstance&){return TYPE_ITEMINSTANCE; }
int getTypeId(const Pos&) { return TYPE_POS; }
template <class T>
void define(int idd, const T& value) {
int type = getTypeId(value);
if (idd > MAX_ID_VALUE) {
LOGE("Data value id is too big with %d! (Max is %d )", idd, MAX_ID_VALUE);
return;
}
if (itemsById.find(idd) != itemsById.end()) {
LOGE("Duplicate id value for %d!\n", idd);
return;
}
DataItem* dataItem = new DataItem2<T>(type, idd, value);
itemsById.insert(std::make_pair(idd, dataItem));
}
TypeChar getByte(int id) const;
TypeShort getShort(int id) const;
TypeInt getInt(int id) const;
TypeFloat getFloat(int id) const;
std::string getString(int id) const;
ItemInstance getItemInstance(int id);
Pos getPos(int id) const;
template <class T>
void set(int id, const T& value) {
set( itemsById[id], value );
}
template <class T>
void set(DataItem* dataItem, const T& value) {
if (!dataItem) {
LOGE("DataItem not found!\n");
return;
}
if (dataItem->getType() == getTypeId(value)) {
DataItem2<T>* d = (DataItem2<T>*)dataItem;
if (!(d->data == value)) {
d->data = value;
dataItem->setDirty(true);
_isDirty = true;
}
} else {
LOGE("ERROR: Id %d is not correct type. %d != %d!\n", dataItem->getId(), dataItem->getType(), getTypeId(value));
}
}
/// Set flag bit. Flag is bit shifted.
template <class T>
void setFlag(int id, int flag) {
DataItem2<T>* item = (DataItem2<T>*)itemsById[id];
bool oldFlag = item->getFlag(flag);
item->setFlag(flag);
if(item->getFlag(flag) != oldFlag) {
markDirty(id);
}
}
/// Clear flag bit. Flag is bit shifted.
template <class T>
void clearFlag(int id, int flag) {
DataItem2<T>* item = (DataItem2<T>*)itemsById[id];
bool oldFlag = item->getFlag(flag);
item->clearFlag(flag);
if(item->getFlag(flag) != oldFlag) {
markDirty(id);
}
}
/// Get flag bit. Flag is bit shifted.
template <class T>
bool getFlag(int id, int flag) {
const DataItem2<T>* item = (const DataItem2<T>*)itemsById[id];
return item->getFlag(flag);
}
void markDirty(int id) {
itemsById[id]->setDirty(true);
_isDirty = true;
}
bool isDirty() const {
return _isDirty;
}
static void pack(DataList* items, IDataOutput* output);
DataList packDirty();
void packAll(IDataOutput* output) const;
static DataList unpack(IDataInput* input);
/**
* Assigns values from a list of data items.
*
* @param items
*/
void assignValues(DataList* items);
private:
static void writeDataItem(IDataOutput* output, const DataItem* dataItem);
Map itemsById;
bool _isDirty;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY__SynchedEntityData_H__*/

View File

@@ -0,0 +1,346 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI__PathNavigation_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI__PathNavigation_H__
//package net.minecraft.world.entity.ai;
#include "../Mob.h"
#include "../../level/Level.h"
#include "../../level/material/Material.h"
#include "../../level/tile/Tile.h"
#include "../../phys/Vec3.h"
#include <cmath>
#include "../../level/pathfinder/Path.h"
#include "control/MoveControl.h"
#include "../../../util/MemUtils.h"
class PathNavigation
{
public:
PathNavigation(Mob* mob, Level* level, float maxDist)
: mob(mob),
level(level),
maxDist(maxDist),
_tick(0),
avoidSun(false),
avoidWater(false),
canOpenDoors(false),
lastStuckCheckPos(0, 0, 0),
speed(0),
lastStuckCheck(0),
path(NULL),
deletePath(false)
{
}
~PathNavigation() {
deletePathIfNeeded();
}
void setSpeed(float speed) {
this->speed = speed;
}
Path* createPath(float x, float y, float z) {
//LOGI("can update? %d\n", canUpdatePath());
if (!canUpdatePath())
return NULL;
Path* p = new Path();
level->findPath(p, mob, Mth::floor(x), (int) y, Mth::floor(z), maxDist, canOpenDoors, avoidWater);
return p;
}
bool moveTo(float x, float y, float z, float speed) {
Path* newPath = createPath(x, y, z);
return moveTo(newPath, speed, true);
}
Path* createPath(Mob* target) {
//LOGI("can update? %d\n", canUpdatePath());
if (!canUpdatePath())
return NULL;
Path* p = new Path();
level->findPath(p, mob, target, maxDist, canOpenDoors, avoidWater);
return p;
}
bool moveTo(Mob* target, float speed) {
Path* newPath = createPath(target);
//if (!newPath->isEmpty()) return moveTo(newPath, speed);
if (newPath) return moveTo(newPath, speed, true);
else return false;
}
bool moveTo(Path* newPath, float speed, bool navIsPathOwner) {
if (newPath == NULL) {
deletePathIfNeeded();
return false;
}
//if (newPath.isEmpty()) {
// path.destroy();
// return false;
//}
Node* last = newPath->last();
if (last) {
//LOGI("> %d, %d, %d in %d steps\n", last->x, last->y, last->z, newPath->getSize());
}
/*if (!newPath->sameAs(path))*/ {
// Delete old path if we are owner
deletePathIfNeeded();
path = newPath;
deletePath = navIsPathOwner;
}
if (avoidSun) trimPathFromSun();
if (path->getSize() == 0) return false;
this->speed = speed;
Vec3 mobPos = getTempMobPos();
lastStuckCheck = _tick;
lastStuckCheckPos.x = mobPos.x;
lastStuckCheckPos.y = mobPos.y;
lastStuckCheckPos.z = mobPos.z;
return true;
}
//Ref<Path>* getPath() {
Path* getPath() {
return path;
}
void tick() {
++_tick;
//LOGI("hehe %d\n", _tick);
if (isDone()) return;
//LOGI("hehe2 %d %d\n", _tick, canUpdatePath());
if (canUpdatePath()) updatePath();
//LOGI("hehe3 %d\n", _tick);
if (isDone()) {
//LOGI("done!\n");
return;
}
Vec3 target = path->currentPos(mob);
//LOGI("hehe4 %d\n", _tick);
mob->getMoveControl()->setWantedPosition(target.x, target.y, target.z, speed);
}
bool isDone() {
return !path || path->isDone();// path == NULL || path.isDone();
}
void stop() {
deletePathIfNeeded();
//if (path) {
// path->destroy(); //@?
// path = NULL;
//}
}
private:
void deletePathIfNeeded() {
//return;
if (deletePath && path) {
LOGI("nav-deleting %p (%d)\n", path, path->id);
delete path;
//deletePath = false;
}
path = NULL;
}
void updatePath() {
Vec3 mobPos = getTempMobPos();
Path& path = *this->path;
// find first elevations in path
int firstElevation = path.getSize();
for (int i = path.getIndex(); i < path.getSize(); ++i)
if (path.get(i)->y != (int) mobPos.y) {
firstElevation = i;
break;
}
// remove those within way point radius (this is not optimal, should
// check canWalkDirectly also) possibly only check next as well
float waypointRadiusSqr = mob->bbWidth * mob->bbWidth;
for (int i = path.getIndex(); i < firstElevation; ++i) {
if (mobPos.distanceToSqr(path.getPos(mob, i)) < waypointRadiusSqr) {
path.setIndex(i + 1);
}
}
// smooth remaining on same elevation
int sx = (int)ceil(mob->bbWidth);
int sy = (int) mob->bbHeight + 1;
int sz = sx;
for (int i = firstElevation - 1; i >= path.getIndex(); --i) {
if (canMoveDirectly(mobPos, path.getPos(mob, i), sx, sy, sz)) {
path.setIndex(i);
break;
}
}
// stuck detection (probably pushed off path)
if (_tick - lastStuckCheck > 100) {
if (mobPos.distanceToSqr(lastStuckCheckPos) < 1.5 * 1.5) stop();
lastStuckCheck = _tick;
lastStuckCheckPos.x = mobPos.x;
lastStuckCheckPos.y = mobPos.y;
lastStuckCheckPos.z = mobPos.z;
}
}
Vec3 getTempMobPos() {
return Vec3(mob->x, (float)getSurfaceY(), mob->z);
}
int getSurfaceY() {
if (!mob->isInWater()) return (int) (mob->bb.y0 + 0.5);
int surface = (int) (mob->bb.y0);
int tileId = level->getTile(Mth::floor(mob->x), surface, Mth::floor(mob->z));
int steps = 0;
while (tileId == Tile::water->id || tileId == Tile::calmWater->id) {
++surface;
tileId = level->getTile(Mth::floor(mob->x), surface, Mth::floor(mob->z));
if (++steps > 16) return (int) (mob->bb.y0);
}
return surface;
}
bool canUpdatePath() {
return mob->onGround || isInLiquid();
}
bool isInLiquid() {
return mob->isInWater() || mob->isInLava();
}
void trimPathFromSun() {
if (level->canSeeSky(Mth::floor(mob->x), (int) (mob->bb.y0 + 0.5), Mth::floor(mob->z)))
return;
Path& path = *this->path;
for (int i = 0; i < path.getSize(); ++i) {
Node* n = path.get(i);
if (level->canSeeSky(n->x, n->y, n->z)) {
path.setSize(i - 1);
return;
}
}
}
bool canMoveDirectly(const Vec3& startPos, const Vec3& stopPos, int sx, int sy, int sz) {
int gridPosX = Mth::floor(startPos.x);
int gridPosZ = Mth::floor(startPos.z);
float dirX = stopPos.x - startPos.x;
float dirZ = stopPos.z - startPos.z;
float distSqr = dirX * dirX + dirZ * dirZ;
if (distSqr < 0.00001f) return false;
float nf = Mth::invSqrt(distSqr);
dirX *= nf;
dirZ *= nf;
sx += 2;
sz += 2;
if (!canWalkOn(gridPosX, (int) startPos.y, gridPosZ, sx, sy, sz, startPos, dirX, dirZ)) return false;
sx -= 2;
sz -= 2;
float deltaX = 1 / Mth::abs(dirX);
float deltaZ = 1 / Mth::abs(dirZ);
float maxX = gridPosX * 1 - startPos.x;
float maxZ = gridPosZ * 1 - startPos.z;
if (dirX >= 0) maxX += 1;
if (dirZ >= 0) maxZ += 1;
maxX /= dirX;
maxZ /= dirZ;
int stepX = dirX < 0 ? -1 : 1;
int stepZ = dirZ < 0 ? -1 : 1;
int gridGoalX = Mth::floor(stopPos.x);
int gridGoalZ = Mth::floor(stopPos.z);
int currentDirX = gridGoalX - gridPosX;
int currentDirZ = gridGoalZ - gridPosZ;
while (currentDirX * stepX > 0 || currentDirZ * stepZ > 0) {
if (maxX < maxZ) {
maxX += deltaX;
gridPosX += stepX;
currentDirX = gridGoalX - gridPosX;
} else {
maxZ += deltaZ;
gridPosZ += stepZ;
currentDirZ = gridGoalZ - gridPosZ;
}
if (!canWalkOn(gridPosX, (int) startPos.y, gridPosZ, sx, sy, sz, startPos, dirX, dirZ)) return false;
}
return true;
}
bool canWalkOn(int x, int y, int z, int sx, int sy, int sz, const Vec3& startPos, float goalDirX, float goalDirZ) {
int startX = x - sx / 2;
int startZ = z - sz / 2;
if (!canWalkAbove(startX, y, startZ, sx, sy, sz, startPos, goalDirX, goalDirZ)) return false;
// lava or water or air under
for (int xx = startX; xx < startX + sx; xx++) {
for (int zz = startZ; zz < startZ + sz; zz++) {
float dirX = xx + 0.5f - startPos.x;
float dirZ = zz + 0.5f - startPos.z;
if (dirX * goalDirX + dirZ * goalDirZ < 0) continue;
int tile = level->getTile(xx, y - 1, zz);
if (tile <= 0) return false;
const Material* m = Tile::tiles[tile]->material;
if (m == Material::water && !mob->isInWater()) return false;
if (m == Material::lava) return false;
}
}
return true;
}
bool canWalkAbove(int startX, int startY, int startZ, int sx, int sy, int sz, const Vec3& startPos, float goalDirX, float goalDirZ) {
for (int xx = startX; xx < startX + sx; xx++) {
for (int yy = startY; yy < startY + sy; yy++) {
for (int zz = startZ; zz < startZ + sz; zz++) {
float dirX = xx + 0.5f - startPos.x;
float dirZ = zz + 0.5f - startPos.z;
if (dirX * goalDirX + dirZ * goalDirZ < 0) continue;
int tile = level->getTile(xx, yy, zz);
if (tile <= 0) continue;
if (Tile::tiles[tile]->material->blocksMotion()) return false;
//if (!Tile::tiles[tile]->isPathfindable(level, xx, yy, zz)) return false; //@todo
}
}
}
return true;
}
private:
Mob* mob;
Level* level;
Path* path;
bool deletePath;
Ref<Path>* refPath;
float speed;
float maxDist;
int _tick;
int lastStuckCheck;
Vec3 lastStuckCheckPos;
public:
bool avoidWater;
bool avoidSun;
bool canOpenDoors;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI__PathNavigation_H__*/

38
src/world/entity/ai/Sensing.h Executable file
View File

@@ -0,0 +1,38 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_SENSING__Sensing_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_SENSING__Sensing_H__
//package net.minecraft.world.entity.ai.sensing;
#include <set>
class Sensing {
typedef std::set<Entity*> EntitySet;
public:
Sensing(Mob* mob)
: mob(mob)
{}
void tick() {
seen.clear();
unseen.clear();
}
bool canSee(Entity* target) {
EntitySet::const_iterator cSeen = seen.find(target);
if (cSeen != seen.end()) return true;
EntitySet::const_iterator cUnseen = unseen.find(target);
if (cUnseen != unseen.end()) return false;
bool canSee = mob->canSee(target);
if (canSee) seen.insert(target);
else unseen.insert(target);
return canSee;
}
private:
Mob* mob;
EntitySet seen;
EntitySet unseen;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_SENSING__Sensing_H__*/

View File

@@ -0,0 +1,13 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__Control_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__Control_H__
//package net.minecraft.world.entity->ai.control;
class Control {
public:
static const int MoveControlFlag = 1;
static const int LookControlFlag = 2;
static const int JumpControlFlag = 4;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__Control_H__*/

View File

@@ -0,0 +1,31 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__JumpControl_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__JumpControl_H__
//package net.minecraft.world.entity.ai.control;
#include "Control.h"
#include "../../Mob.h"
class JumpControl: public Control
{
public:
JumpControl(Mob* mob)
: mob(mob),
_jump(false)
{
}
void jump() {
_jump = true;
}
void tick() {
mob->setJumping(_jump);
_jump = false;
}
private:
Mob* mob;
bool _jump;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__JumpControl_H__*/

View File

@@ -0,0 +1,70 @@
#include "MoveControl.h"
#include "JumpControl.h"
const float MoveControl::MAX_TURN = 30;
const float MoveControl::MIN_SPEED = 0.0005f;
const float MoveControl::MIN_SPEED_SQR = MIN_SPEED * MIN_SPEED;
MoveControl::MoveControl( Mob* mob )
: mob(mob),
wantedX(mob->x),
wantedY(mob->y),
wantedZ(mob->z),
_hasWanted(false)
{
}
bool MoveControl::hasWanted() {
return _hasWanted;
}
float MoveControl::getSpeed() {
return speed;
}
void MoveControl::setWantedPosition( float x, float y, float z, float speed ) {
//LOGI("> %f, %f, %f\n", x, y, z);
wantedX = x;
wantedY = y;
wantedZ = z;
this->speed = speed;
_hasWanted = true;
}
void MoveControl::tick() {
mob->setYya(0);
if (!_hasWanted) return;
_hasWanted = false;
int yFloor = Mth::floor(mob->bb.y0 + 0.5f);
float xd = wantedX - mob->x;
float zd = wantedZ - mob->z;
float yd = wantedY - yFloor;
float dd = xd * xd + yd * yd + zd * zd;
if (dd < MIN_SPEED_SQR) return;
float yRotD = (float) (std::atan2(zd, xd) * 180 / Mth::PI) - 90;
mob->yRot = rotlerp(mob->yRot, yRotD, MAX_TURN);
mob->setSpeed(speed);
if (yd > 0 && xd * xd + zd * zd < 1) mob->getJumpControl()->jump();
}
float MoveControl::rotlerp( float a, float b, float max ) {
float diff = b - a;
while (diff < -180)
diff += 360;
while (diff >= 180)
diff -= 360;
if (diff > max) {
diff = max;
}
if (diff < -max) {
diff = -max;
}
return a + diff;
}

View File

@@ -0,0 +1,38 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__MoveControl_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__MoveControl_H__
//package net.minecraft.world.entity.ai.control;
#include "Control.h"
#include "../../Mob.h"
#include "../../../../util/Mth.h"
class MoveControl: public Control
{
static const float MAX_TURN;
public:
static const float MIN_SPEED;
static const float MIN_SPEED_SQR;
MoveControl(Mob* mob);
bool hasWanted();
float getSpeed();
void setWantedPosition(float x, float y, float z, float speed);
void tick();
private:
float rotlerp(float a, float b, float max);
Mob* mob;
float wantedX;
float wantedY;
float wantedZ;
float speed;
bool _hasWanted;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_CONTROL__MoveControl_H__*/

View File

@@ -0,0 +1,57 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__BreakDoorGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__BreakDoorGoal_H__
//package net.minecraft.world.entity->ai.goal;
#include "DoorInteractGoal.h"
#include "../../Entity.h"
#include "../../../level/tile/LevelEvent.h"
#include "../../../level/Level.h"
#include "../../../level/tile/DoorTile.h"
#include "../../../../SharedConstants.h"
class BreakDoorGoal: public DoorInteractGoal
{
typedef DoorInteractGoal super;
public:
BreakDoorGoal(Monster* mob)
: super(mob)
{}
bool canUse() {
if (!super::canUse()) return false;
return !doorTile->isOpen(mob->level, doorX, doorY, doorZ);
}
void start() {
super::start();
breakTime = SharedConstants::TicksPerSecond * 12;
}
bool canContinueToUse() {
float d = mob->distanceToSqr((float)doorX, (float)doorY, (float)doorZ);
return breakTime >= 0 && !doorTile->isOpen(mob->level, doorX, doorY, doorZ) && d < 2 * 2;
}
void tick() {
super::tick();
if (mob->random.nextInt(20) == 0) {
//mob->level->levelEvent(LevelEvent::SOUND_ZOMBIE_WOODEN_DOOR, doorX, doorY, doorZ, 0);
}
//LOGI("time: %d\n", breakTime);
if (--breakTime == 0) {
/*if (mob->level->difficulty == Difficulty.HARD)*/ {
mob->level->setTile(doorX, doorY, doorZ, 0);
//mob->level->levelEvent(LevelEvent::SOUND_ZOMBIE_DOOR_CRASH, doorX, doorY, doorZ, 0);
//mob->level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, doorX, doorY, doorZ, doorTile->id);
}
}
}
private:
int breakTime;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__BreakDoorGoal_H__*/

View File

@@ -0,0 +1,79 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__DoorInteractGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__DoorInteractGoal_H__
//package net.minecraft.world.entity.ai.goal;
#include "Goal.h"
#include "../PathNavigation.h"
#include "../../monster/Monster.h"
#include "../../../level/pathfinder/Path.h"
#include "../../../level/tile/DoorTile.h"
class DoorInteractGoal: public Goal
{
public:
DoorInteractGoal(Monster* mob)
: mob(mob)
{}
bool canUse() {
if (!mob->horizontalCollision) return false;
PathNavigation* pathNav = mob->getNavigation();
Path* path = pathNav->getPath();
if (path == NULL || path->isDone() || !pathNav->canOpenDoors)
return false;
for (int i = 0; i < Mth::Min(path->getIndex() + 2, path->getSize()); ++i) {
Node* n = path->get(i);
doorX = n->x;
doorY = n->y + 1;
doorZ = n->z;
if (mob->distanceToSqr((float)doorX, mob->y, (float)doorZ) > 1.5f * 1.5f) continue;
doorTile = getDoorTile(doorX, doorY, doorZ);
if (doorTile == NULL) continue;
return true;
}
doorX = Mth::floor(mob->x);
doorY = Mth::floor(mob->y + 1);
doorZ = Mth::floor(mob->z);
doorTile = getDoorTile(doorX, doorY, doorZ);
return doorTile != NULL;
}
bool canContinueToUse() {
return !passed;
}
void start() {
passed = false;
doorOpenDirX = doorX + 0.5f - mob->x;
doorOpenDirZ = doorZ + 0.5f - mob->z;
}
void tick() {
float newDoorDirX = doorX + 0.5f - mob->x;
float newDoorDirZ = doorZ + 0.5f - mob->z;
float dot = doorOpenDirX * newDoorDirX + doorOpenDirZ * newDoorDirZ;
if (dot < 0) {
passed = true;
}
}
private:
DoorTile* getDoorTile(int x, int y, int z) {
int tileId = mob->level->getTile(x, y, z);
if (tileId != Tile::door_wood->id) return NULL;
DoorTile* doorTile = (DoorTile*) Tile::tiles[tileId];
return doorTile;
}
protected:
Monster* mob;
int doorX, doorY, doorZ;
DoorTile* doorTile;
private:
bool passed;
float doorOpenDirX, doorOpenDirZ;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__DoorInteractGoal_H__*/

44
src/world/entity/ai/goal/Goal.h Executable file
View File

@@ -0,0 +1,44 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__Goal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__Goal_H__
//package net.minecraft.world.entity.ai.goal;
class Goal
{
public:
Goal()
: _requiredControlFlags(0)
{}
virtual ~Goal() {}
virtual bool canUse() = 0;
virtual bool canContinueToUse() {
return canUse();
}
virtual bool canInterrupt() {
return true;
}
virtual void start() {
}
virtual void stop() {
}
virtual void tick() {
}
void setRequiredControlFlags(int requiredControlFlags) {
_requiredControlFlags = requiredControlFlags;
}
int getRequiredControlFlags() {
return _requiredControlFlags;
}
private:
int _requiredControlFlags;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__Goal_H__*/

View File

@@ -0,0 +1,104 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__GoalSelector_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__GoalSelector_H__
//package net.minecraft.world.entity.ai.goal;
#include "Goal.h"
#include <vector>
class GoalSelector
{
class InternalGoal {
public:
InternalGoal(short prio, Goal* goal)
: prio(prio),
goal(goal),
isUsing(false)
{}
Goal* goal;
short prio;
bool isUsing;
};
public:
~GoalSelector() {
for (unsigned int i = 0; i < goals.size(); ++i) {
delete goals[i].goal;
}
}
void addGoal(int prio, Goal* goal) {
goals.push_back(InternalGoal(prio, goal));
}
void tick() {
std::vector<InternalGoal> toStart;
for (std::vector<InternalGoal>::iterator it = goals.begin(); it != goals.end(); ++it) {
InternalGoal& ig = *it;
bool isUsing = ig.isUsing; //usingGoals.contains(ig);
if (isUsing) {
if (!canUseInSystem(ig) || !ig.goal->canContinueToUse()) {
ig.goal->stop();
ig.isUsing = false;
//usingGoals.remove(ig);
} else continue;
}
if (!canUseInSystem(ig) || !ig.goal->canUse()) {
continue;
}
toStart.push_back(ig);
//usingGoals.add(ig);
ig.isUsing = true;
}
//bool debug = false;
//if (debug && toStart.size() > 0) printf("Starting: ");
for (std::vector<InternalGoal>::iterator it = toStart.begin(); it != toStart.end(); ++it) {
InternalGoal& ig = *it;
//if (debug) printf(" %s, ", ig.goal.toString() + ", ");
ig.goal->start();
}
//if (debug && usingGoals.size() > 0) printf("Running: ");
for (std::vector<InternalGoal>::iterator it = goals.begin(); it != goals.end(); ++it) {
InternalGoal& ig = *it;
if (ig.isUsing) {
//if (debug) printf(" %s\n", ig.goal.toString());
ig.goal->tick();
}
}
}
//std::vector<InternalGoal>& getRunningGoals() {
// return usingGoals;
//}
private:
bool canUseInSystem(InternalGoal& goal) {
for (std::vector<InternalGoal>::iterator it = goals.begin(); it != goals.end(); ++it) {
InternalGoal& ig = *it;
if (ig.goal == goal.goal && ig.prio == goal.prio)
continue;
if (goal.prio >= ig.prio) {
if (ig.isUsing && !canCoExist(goal, ig))
return false;
} else if (ig.isUsing && !ig.goal->canInterrupt())
return false;
}
return true;
}
bool canCoExist(InternalGoal& goalA, InternalGoal& goalB) {
return (goalA.goal->getRequiredControlFlags() & goalB.goal->getRequiredControlFlags()) == 0;
}
std::vector<InternalGoal> goals;
//std::vector<InternalGoal> usingGoals;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__GoalSelector_H__*/

View File

@@ -0,0 +1,94 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__MeleeAttackGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__MeleeAttackGoal_H__
//package net.minecraft.world.entity.ai.goal;
#include "Goal.h"
#include "../control/Control.h"
#include "../../monster/Monster.h"
#include "../../../level/Level.h"
#include "../../../level/pathfinder/Path.h"
#include "../Sensing.h"
class MeleeAttackGoal: public Goal
{
public:
MeleeAttackGoal(Monster* mob, float speed, bool trackTarget, int attackType = 0)
: mob(mob),
level(mob->level),
speed(speed),
trackTarget(trackTarget),
attackTime(0),
attackType(attackType),
path(NULL)
{
setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag);
}
~MeleeAttackGoal() {
if (path) {
LOGI("mag-deleting %p (%d)\n", path, path->id);
delete path;
}
}
/*@Override*/
bool canUse() {
Mob* bestTarget = mob->getTarget();
if (bestTarget == NULL) return false;
if (attackType != 0 && !mob->isPlayer()) return false; //!attackType.isAssignableFrom(bestTarget.getClass())) return false;
target = bestTarget;
if (path) {
LOGI("mag-canuse-deleting %p (%d)\n", path, path->id);
delete path;
}
path = mob->getNavigation()->createPath(target);
return path != NULL;
}
bool canContinueToUse() {
Mob* bestTarget = mob->getTarget();
if (bestTarget == NULL) return false;
if (attackType != 0 && !mob->isPlayer()) return false;//!attackType.isAssignableFrom(bestTarget.getClass())) return false;
target = bestTarget;
if (!trackTarget) return !mob->getNavigation()->isDone();
return true;
}
void start() {
mob->getNavigation()->moveTo(path, speed, false);
}
void stop() {
target = NULL;
mob->getNavigation()->stop();
}
void tick() {
//mob->getLookControl().setLookAt(target, 30, 30);
if (trackTarget || mob->sensing->canSee(target)) {
//LOGI("target: %p @ %f, %f, %f\n", target, target->x, target->y, target->z);
mob->getNavigation()->moveTo(target, speed);
}
attackTime = Mth::Max(attackTime - 1, 0);
float meleeRadiusSqr = (mob->bbWidth * 2) * (mob->bbWidth * 2);
if (mob->distanceToSqr(target->x, target->bb.y0, target->z) > meleeRadiusSqr) return;
if (attackTime > 0) return;
attackTime = 20;
mob->doHurtTarget(target);
}
private:
Level* level;
Monster* mob;
Mob* target;
int attackTime;
float speed;
Path* path;
int attackType;
bool trackTarget;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__MeleeAttackGoal_H__*/

View File

@@ -0,0 +1,54 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__RandomStrollGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__RandomStrollGoal_H__
//package net.minecraft.world.entity.ai.goal;
#include "Goal.h"
#include "../../../../SharedConstants.h"
#include "../../PathfinderMob.h"
#include "../control/Control.h"
#include "../util/RandomPos.h"
#include "../../../phys/Vec3.h"
#include "../util/RandomPos.h"
class RandomStrollGoal: public Goal
{
public:
RandomStrollGoal(PathfinderMob* mob, float speed)
: mob(mob),
speed(speed)
{
setRequiredControlFlags(Control::MoveControlFlag);
}
/*@Override*/
bool canUse() {
if (mob->getNoActionTime() >= SharedConstants::TicksPerSecond * 5) return false;
if (mob->random.nextInt(120) != 0) return false;
Vec3 pos;
if (!RandomPos::getPos(pos, mob, 10, 7)) return false;
wantedX = pos.x;
wantedY = pos.y;
wantedZ = pos.z;
return true;
}
/*@Override*/
bool canContinueToUse() {
return !mob->getNavigation()->isDone();
}
/*@Override*/
void start() {
mob->getNavigation()->moveTo(wantedX, wantedY, wantedZ, speed);
}
private:
PathfinderMob* mob;
float wantedX, wantedY, wantedZ;
float speed;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL__RandomStrollGoal_H__*/

View File

@@ -0,0 +1,41 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__HurtByTargetGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__HurtByTargetGoal_H__
//package net.minecraft.world.entity->ai.goal.target;
#include "TargetGoal.h"
#include "../../../../phys/AABB.h"
class HurtByTargetGoal: public TargetGoal
{
typedef TargetGoal super;
public:
HurtByTargetGoal(Monster* mob, bool alertSameType)
: super(mob, 16, false),
alertSameType(alertSameType)
{
setRequiredControlFlags(TargetGoal::TargetFlag);
}
bool canUse() {
return canAttack(mob->getLastHurtByMob(), true);
}
void start() {
mob->setTarget(mob->getLastHurtByMob());
// if (alertSameType) {
// List<Entity> nearby = mob.level->getEntitiesOfClass(mob.getClass(), AABB.newTemp(mob.x, mob.y, mob.z, mob.x + 1, mob.y + 1, mob.z + 1).grow(within, 4, within));
// for (Entity* ent : nearby) {
// Mob other = (Mob) ent;
// if (this->mob == other) continue;
// if (other.getTarget() != NULL) continue;
// other.setTarget(mob.getLastHurtByMob());
// }
// }
}
private:
bool alertSameType;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__HurtByTargetGoal_H__*/

View File

@@ -0,0 +1,42 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__NearestAttackableTargetGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__NearestAttackableTargetGoal_H__
//package net.minecraft.world.entity->ai.goal.target;
/* import net.minecraft.world.entity->* */
#include "TargetGoal.h"
#include "../../../player/Player.h"
class NearestAttackableTargetGoal: public TargetGoal
{
typedef TargetGoal super;
public:
NearestAttackableTargetGoal(Monster* mob, int targetType, float within, int randomInterval, bool mustSee)
: super(mob, within, mustSee),
targetType(targetType),
randomInterval(randomInterval)
{
setRequiredControlFlags(TargetGoal::TargetFlag);
}
bool canUse() {
if (randomInterval > 0 && mob->random.nextInt(randomInterval) != 0) return false;
Mob* potentialTarget = NULL;
if (targetType == 1) potentialTarget = mob->level->getNearestPlayer(mob, within); //@todo: targetType
//else potentialTarget = (Mob*) mob->level->getClosestEntityOfClass(targetType, mob->bb.grow(within, 4, within), mob);
if (!canAttack(potentialTarget, false)) return false;
target = potentialTarget;
return true;
}
void start() {
mob->setTarget(target);
}
private:
Mob* target;
int targetType;
int randomInterval;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__NearestAttackableTargetGoal_H__*/

View File

@@ -0,0 +1,57 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__TargetGoal_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__TargetGoal_H__
//package net.minecraft.world.entity->ai.goal.target;
/* import net.minecraft.world.entity->* */
#include "../Goal.h"
/* import net.minecraft.world.entity->monster.* */
#include "../../../player/Player.h"
class Monster;
class TargetGoal: public Goal
{
public:
static const int TargetFlag = 1;
TargetGoal(Monster* mob, float within, bool mustSee)
: mob(mob),
within(within),
mustSee(mustSee)
{}
bool canContinueToUse() {
Mob* target = mob->getTarget();
if (target == NULL) return false;
if (!target->isAlive()) return false;
if (mob->distanceToSqr(target) > within * within) return false;
if (mustSee && !mob->sensing->canSee(target)) return false;
return true;
}
void stop() {
mob->setTarget(NULL);
}
protected:
bool canAttack(Mob* target, bool allowInvulnerable) {
if (target == NULL) return false;
if (target == mob) return false;
if (!target->isAlive()) return false;
if (target->bb.y1 <= mob->bb.y0 || target->bb.y0 >= mob->bb.y1) return false;
//if (target instanceof Creeper || target instanceof Ghast) return false;
if (target->isPlayer()) {
if (!allowInvulnerable && ((Player*) target)->abilities.invulnerable) return false;
}
if (mustSee && !mob->sensing->canSee(target)) return false;
return true;
}
Monster* mob;
float within;
bool mustSee;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_GOAL_TARGET__TargetGoal_H__*/

View File

@@ -0,0 +1,71 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_AI_UTIL__RandomPos_H__
#define NET_MINECRAFT_WORLD_ENTITY_AI_UTIL__RandomPos_H__
//package net.minecraft.world.entity.ai.util;
#include "../../PathfinderMob.h"
#include "../../../phys/Vec3.h"
class RandomPos
{
public:
static bool getPos(Vec3& outPos, PathfinderMob* mob, int xzDist, int yDist) {
return generateRandomPos(outPos, mob, xzDist, yDist, NULL);
}
static bool getPosTowards(Vec3& outPos, PathfinderMob* mob, int xzDist, int yDist, Vec3* towardsPos) {
Vec3 tempDir(towardsPos->x, towardsPos->y, towardsPos->z);
tempDir.subSelf(mob->x, mob->y, mob->z);
return generateRandomPos(outPos, mob, xzDist, yDist, &tempDir);
}
static bool getPosAvoid(Vec3& outPos, PathfinderMob* mob, int xzDist, int yDist, Vec3* avoidPos) {
Vec3 tempDir(mob->x, mob->y, mob->z);
tempDir.subSelf(avoidPos->x, avoidPos->y, avoidPos->z);
return generateRandomPos(outPos, mob, xzDist, yDist, &tempDir);
}
private:
static bool generateRandomPos(Vec3& outPos, PathfinderMob* mob, int xzDist, int yDist, Vec3* dir) {
Random& random = mob->random;
bool hasBest = false;
int xBest = 0, yBest = 0, zBest = 0;
float best = -99999;
// bool restrict;
// if (mob->hasRestriction()) {
// float restDist = mob->getRestrictCenter().dist((int) Math.floor(mob->x), (int) Math.floor(mob->y), (int) Math.floor(mob->z)) + 4;
// restrict = restDist < mob->getRestrictRadius() + xzDist;
// } else restrict = false;
for (int i = 0; i < 10; i++) {
int xt = random.nextInt(2 * xzDist) - xzDist;
int yt = random.nextInt(2 * yDist) - yDist;
int zt = random.nextInt(2 * xzDist) - xzDist;
if (dir != NULL && xt * dir->x + zt * dir->z < 0) continue;
xt += Mth::floor(mob->x);
yt += Mth::floor(mob->y);
zt += Mth::floor(mob->z);
//if (restrict && !mob->isWithinRestriction(xt, yt, zt)) continue;
float value = mob->getWalkTargetValue(xt, yt, zt);
if (value > best) {
best = value;
xBest = xt;
yBest = yt;
zBest = zt;
hasBest = true;
}
}
if (hasBest) {
outPos.set((float)xBest, (float)yBest, (float)zBest);
return true;
}
return false;
}
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_AI_UTIL__RandomPos_H__*/

View File

@@ -0,0 +1,92 @@
#include "Animal.h"
#include "../../level/Level.h"
#include "../../level/tile/Tile.h"
#include "../../../nbt/CompoundTag.h"
Animal::Animal( Level* level )
: super(level),
inLove(0)
{
//entityData.define(DATA_AGE_ID, (SynchedEntityData::TypeInt)0);
}
bool Animal::hurt( Entity* source, int dmg )
{
fleeTime = 3 * SharedConstants::TicksPerSecond;
attackTargetId = 0;
inLove = 0;
return super::hurt(source, dmg);
}
bool Animal::canSpawn()
{
int xt = Mth::floor(x);
int yt = Mth::floor(bb.y0);
int zt = Mth::floor(z);
return level->getTile(xt, yt - 1, zt) == ((Tile*)Tile::grass)->id && level->getRawBrightness(xt, yt, zt) > 8 && super::canSpawn();
}
int Animal::getAmbientSoundInterval()
{
return 12 * SharedConstants::TicksPerSecond;
}
float Animal::getWalkTargetValue( int x, int y, int z )
{
if (level->getTile(x, y - 1, z) == ((Tile*)Tile::grass)->id) return 10;
return level->getBrightness(x, y, z) - 0.5f;
}
Entity* Animal::findAttackTarget()
{
return NULL;
//if (fleeTime > 0)
// return NULL;
//int inLove = -1;
//float r = 8;
//if (inLove > 0) {
// EntityList others;
// level->getEntitiesOfType(getEntityTypeId(), bb.expand(r, r, r), others);
// for (unsigned int i = 0; i < others.size(); i++) {
// Animal* p = (Animal*) others[i];
// if (p != this && p->inLove > 0) {
// return p;
// }
// }
//} else {
// /* if (getAge() == 0) {
// List<Entity> players = level.getEntitiesOfClass(Player.class, bb.expand(r, r, r));
// for (int i = 0; i < players.size(); i++) {
// Player p = (Player) players.get(i);
// if (p.getSelectedItem() != null && this.isFood(p.getSelectedItem())) {
// return p;
// }
// }
// } else if (getAge() > 0) {
// EntityList others;
// level->getEntitiesOfType(getEntityTypeId(), bb.expand(r, r, r), others);
// for (unsigned int i = 0; i < others.size(); i++) {
// Animal* p = (Animal*) others[i];
// if (p != this && p->getAge() < 0) {
// return p;
// }
// }
// }
// */
//}
//return NULL;
}
bool Animal::removeWhenFarAway()
{
return false;
}
int Animal::getCreatureBaseType() const {
return MobTypes::BaseCreature;
}

View File

@@ -0,0 +1,39 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Animal_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Animal_H__
//package net.minecraft.world.entity.animal;
#include "../AgableMob.h"
#include "../Creature.h"
class Level;
class Entity;
class CompoundTag;
class Animal: public AgableMob,
public Creature
{
typedef AgableMob super;
public:
Animal(Level* level);
//@Override
bool hurt(Entity* source, int dmg);
bool canSpawn();
int getAmbientSoundInterval();
int getCreatureBaseType() const;
bool removeWhenFarAway();
protected:
float getWalkTargetValue(int x, int y, int z);
Entity* findAttackTarget();
private:
int inLove;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Animal_H__*/

View File

@@ -0,0 +1,10 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__AnimalInclude_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__AnimalInclude_H__
#include "Animal.h"
#include "Chicken.h"
#include "Cow.h"
#include "Pig.h"
#include "Sheep.h"
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__AnimalInclude_H__*/

View File

@@ -0,0 +1,113 @@
#include "Chicken.h"
#include "../../item/Item.h"
#include "../../level/Level.h"
Chicken::Chicken( Level* level )
: super(level),
sheared(false),
flap(0),
oFlap(0),
flapSpeed(0),
oFlapSpeed(0),
flapping(1),
eggTime(0)
{
entityRendererId = ER_CHICKEN_RENDERER;
textureName = "mob/chicken.png";
setSize(0.3f, 0.7f);
eggTime = random.nextInt(SharedConstants::TicksPerSecond * 60 * 5) + SharedConstants::TicksPerSecond * 60 * 5;
}
int Chicken::getEntityTypeId() const
{
return MobTypes::Chicken;
}
int Chicken::getMaxHealth()
{
return 4;
}
void Chicken::aiStep()
{
super::aiStep();
oFlap = flap;
oFlapSpeed = flapSpeed;
flapSpeed += (onGround ? -1 : 4) * 0.3f;
if (flapSpeed < 0) flapSpeed = 0;
if (flapSpeed > 1) flapSpeed = 1;
if (!onGround && flapping < 1) flapping = 1;
flapping *= 0.9f;
if (!onGround && yd < 0) {
yd *= 0.6f;
}
flap += flapping * 2;
//@todo
//if (!isBaby()) {
// if (!level->isClientSide && --eggTime <= 0) {
// level->playSound(this, "mob.chickenplop", 1.0f, (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f);
// spawnAtLocation(Item::egg->id, 1);
// eggTime = random.nextInt(SharedConstants::TicksPerSecond * 60 * 5) + SharedConstants::TicksPerSecond * 60 * 5;
// }
//}
}
void Chicken::addAdditonalSaveData( CompoundTag* tag )
{
super::addAdditonalSaveData(tag);
}
void Chicken::readAdditionalSaveData( CompoundTag* tag )
{
super::readAdditionalSaveData(tag);
}
void Chicken::causeFallDamage( float distance )
{
}
const char* Chicken::getAmbientSound()
{
return "mob.chicken";
}
std::string Chicken::getHurtSound()
{
return "mob.chickenhurt";
}
std::string Chicken::getDeathSound()
{
return "mob.chickenhurt";
}
//int Chicken::getDeathLoot()
//{
// return Item::feather->id;
//}
void Chicken::dropDeathLoot( /*bool wasKilledByPlayer, int playerBonusLevel*/ )
{
//// drop some feathers
int count = random.nextInt(3);// + random.nextInt(1 + playerBonusLevel);
for (int i = 0; i < count; i++) {
spawnAtLocation(Item::feather->id, 1);
}
//// and some meat
//if (isOnFire()) spawnAtLocation(Item::chicken_cooked->id, 1); //@fire
//else
spawnAtLocation(Item::chicken_raw->id, 1);
}
Animal* Chicken::getBreedOffspring( Animal* target )
{
return new Chicken(level);
}

View File

@@ -0,0 +1,49 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Chicken_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Chicken_H__
//package net.minecraft.world.entity.animal;
#include "Animal.h"
class CompoundTag;
class Level;
class Chicken: public Animal
{
typedef Animal super;
public:
Chicken(Level* level);
int getEntityTypeId() const;
/*@Override*/
int getMaxHealth();
void aiStep();
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
protected:
void causeFallDamage(float distance);
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
//int getDeathLoot();
//@Override
void dropDeathLoot(/*bool wasKilledByPlayer, int playerBonusLevel*/);
//@Override
Animal* getBreedOffspring(Animal* target);
public:
bool sheared;
float flap;
float flapSpeed;
float oFlapSpeed, oFlap;
float flapping;
int eggTime;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Chicken_H__*/

79
src/world/entity/animal/Cow.cpp Executable file
View File

@@ -0,0 +1,79 @@
#include "Cow.h"
#include "../player/Player.h"
#include "../../level/Level.h"
#include "../../item/Item.h"
Cow::Cow( Level* level )
: super(level)
{
entityRendererId = ER_COW_RENDERER;
textureName = "mob/cow.png";
setSize(0.9f, 1.3f);
}
int Cow::getEntityTypeId() const {
return MobTypes::Cow;
}
int Cow::getMaxHealth() {
return 10;
}
void Cow::addAdditonalSaveData( CompoundTag* tag ) {
super::addAdditonalSaveData(tag);
}
void Cow::readAdditionalSaveData( CompoundTag* tag ) {
super::readAdditionalSaveData(tag);
}
bool Cow::interact( Player* player ) {
//ItemInstance item = player->inventory.getSelected();
//if (item != NULL && item.id == Item::bucket_empty->id) {
// player->inventory.setItem(player->inventory.selected, /*new*/ ItemInstance(Item::milk));
// return true;
//}
return super::interact(player);
}
const char* Cow::getAmbientSound() {
return "mob.cow";
}
std::string Cow::getHurtSound() {
return "mob.cowhurt";
}
std::string Cow::getDeathSound() {
return "mob.cowhurt";
}
float Cow::getSoundVolume() {
return 0.4f;
}
int Cow::getDeathLoot() {
return Item::leather->id;
}
void Cow::dropDeathLoot( /*bool wasKilledByPlayer, int playerBonusLevel*/ ) {
// drop some leather
int count = random.nextInt(3);// + random.nextInt(1 + playerBonusLevel);
for (int i = 0; i < count; i++) {
spawnAtLocation(Item::leather->id, 1);
}
// and some meat
count = random.nextInt(3) + 1;
for (int i = 0; i < count; i++) {
// if (isOnFire()) { //@fire
// spawnAtLocation(Item::beef_cooked->id, 1);
// } else {
spawnAtLocation(Item::beef_raw->id, 1);
// }
}
}
Animal* Cow::getBreedOffspring( Animal* target ) {
return new Cow(level);
}

42
src/world/entity/animal/Cow.h Executable file
View File

@@ -0,0 +1,42 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Cow_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Cow_H__
//package net.minecraft.world.entity.animal;
#include "Animal.h"
class Level;
class Player;
class CompoundTag;
class Cow: public Animal
{
typedef Animal super;
public:
Cow(Level* level);
int getEntityTypeId() const;
/*@Override*/
int getMaxHealth();
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
bool interact(Player* player);
protected:
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
float getSoundVolume();
int getDeathLoot();
/*@Override*/
void dropDeathLoot(/*bool wasKilledByPlayer, int playerBonusLevel*/);
/*@Override*/
Animal* getBreedOffspring(Animal* target);
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Cow_H__*/

70
src/world/entity/animal/Pig.cpp Executable file
View File

@@ -0,0 +1,70 @@
#include "Pig.h"
#include "../player/Player.h"
Pig::Pig( Level* level ) : super(level)
{
entityRendererId = ER_PIG_RENDERER;
textureName = "mob/pig.png";
setSize(0.9f, 0.9f);
//entityData.define(DATA_SADDLE_ID, (SynchedEntityData::TypeChar) 0);
}
int Pig::getEntityTypeId() const
{
return MobTypes::Pig;
}
bool Pig::interact( Player* player )
{
return false;
}
bool Pig::hasSaddle()
{
return false;
//return (entityData.getByte(DATA_SADDLE_ID) & 1) != 0;
}
void Pig::setSaddle( bool value )
{
//if (value) {
// entityData.set(DATA_SADDLE_ID, (char) 1);
//} else {
// entityData.set(DATA_SADDLE_ID, (char) 0);
//}
}
const char* Pig::getAmbientSound()
{
return "mob.pig";
}
std::string Pig::getHurtSound()
{
return "mob.pig";
}
std::string Pig::getDeathSound()
{
return "mob.pigdeath";
}
int Pig::getDeathLoot()
{
//@fire
//if (isOnFire())
// return Item::porkChop_cooked->id;
return Item::porkChop_raw->id;
}
Animal* Pig::getBreedOffspring( Animal* target )
{
return new Pig(level);
}
int Pig::getMaxHealth()
{
return 10;
}

48
src/world/entity/animal/Pig.h Executable file
View File

@@ -0,0 +1,48 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Pig_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Pig_H__
//package net.minecraft.world.entity.animal;
#include "Animal.h"
class Player;
class Pig: public Animal
{
typedef Animal super;
static const int DATA_SADDLE_ID = 16;
public:
Pig(Level* level);
int getEntityTypeId() const;
//void addAdditonalSaveData(CompoundTag* tag) {
// super::addAdditonalSaveData(tag);
// tag->putBoolean("Saddle", hasSaddle());
//}
//void readAdditionalSaveData(CompoundTag* tag) {
// super::readAdditionalSaveData(tag);
// setSaddle(tag->getBoolean("Saddle"));
//}
bool interact(Player* player);
int getMaxHealth();
bool hasSaddle();
void setSaddle(bool value);
protected:
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
int getDeathLoot();
//@Override
Animal* getBreedOffspring(Animal* target);
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Pig_H__*/

252
src/world/entity/animal/Sheep.cpp Executable file
View File

@@ -0,0 +1,252 @@
#include "Sheep.h"
#include "../../item/DyePowderItem.h"
#include "../../level/tile/LevelEvent.h"
const float Sheep::COLOR[][3] = {
{ 1.0f, 1.0f, 1.0f }, // white
{ 0.95f, 0.7f, 0.2f }, // orange
{ 0.9f, 0.5f, 0.85f }, // magenta
{ 0.6f, 0.7f, 0.95f }, // light blue
{ 0.9f, 0.9f, 0.2f }, // yellow
{ 0.5f, 0.8f, 0.1f }, // light green
{ 0.95f, 0.7f, 0.8f }, // pink
{ 0.3f, 0.3f, 0.3f }, // gray
{ 0.6f, 0.6f, 0.6f }, // silver
{ 0.3f, 0.6f, 0.7f }, // cyan
{ 0.7f, 0.4f, 0.9f }, // purple
{ 0.2f, 0.4f, 0.8f }, // blue
{ 0.5f, 0.4f, 0.3f }, // brown
{ 0.4f, 0.5f, 0.2f }, // green
{ 0.8f, 0.3f, 0.3f }, // red
{ 0.1f, 0.1f, 0.1f }, // black
};
const int Sheep::NumColors = sizeof(Sheep::COLOR) / sizeof(Sheep*);
Sheep::Sheep( Level* level )
: super(level),
eatAnimationTick(0)
{
entityRendererId = ER_SHEEP_RENDERER;
this->textureName = "mob/sheep.png";
this->setSize(0.9f, 1.3f);
// sheared and color share a char
entityData.define(DATA_WOOL_ID, (SynchedEntityData::TypeChar) 0);
}
int Sheep::getMaxHealth()
{
return 8;
}
void Sheep::aiStep()
{
super::aiStep();
if (eatAnimationTick > 0) {
eatAnimationTick--;
}
}
void Sheep::handleEntityEvent( char id )
{
if (id == EntityEvent::EAT_GRASS) {
eatAnimationTick = EAT_ANIMATION_TICKS;
} else {
super::handleEntityEvent(id);
}
}
float Sheep::getHeadEatPositionScale( float a )
{
if (eatAnimationTick <= 0) {
return 0;
}
if (eatAnimationTick >= 4 && eatAnimationTick <= (EAT_ANIMATION_TICKS - 4)) {
return 1;
}
if (eatAnimationTick < 4) {
return ((float) eatAnimationTick - a) / 4.f;
}
return -((float) (eatAnimationTick - EAT_ANIMATION_TICKS) - a) / 4.f;
}
float Sheep::getHeadEatAngleScale( float a )
{
if (eatAnimationTick > 4 && eatAnimationTick <= (EAT_ANIMATION_TICKS - 4)) {
float scale = ((float) (eatAnimationTick - 4) - a) / (float) (EAT_ANIMATION_TICKS - 8);
return Mth::PI * .20f + Mth::PI * .07f * Mth::sin(scale * 28.7f);
}
if (eatAnimationTick > 0) {
return Mth::PI * .20f;
}
return ((xRot / (float) (180 / Mth::PI)));
}
bool Sheep::interact( Player* player )
{
ItemInstance* item = player->inventory->getSelected();
if (item && item->id == ((Item*)Item::shears)->id && !isSheared() && !isBaby()) {
if (!level->isClientSide) {
setSheared(true);
int count = 1 + random.nextInt(3);
for (int i = 0; i < count; i++) {
ItemEntity* ie = spawnAtLocation(new ItemInstance(Tile::cloth->id, 1, getColor()), 1);
ie->yd += random.nextFloat() * 0.05f;
ie->xd += (random.nextFloat() - random.nextFloat()) * 0.1f;
ie->zd += (random.nextFloat() - random.nextFloat()) * 0.1f;
}
}
item->hurt(1);
}
return super::interact(player);
}
void Sheep::addAdditonalSaveData( CompoundTag* tag )
{
super::addAdditonalSaveData(tag);
tag->putBoolean("Sheared", isSheared());
tag->putByte("Color", (char) getColor());
}
void Sheep::readAdditionalSaveData( CompoundTag* tag )
{
super::readAdditionalSaveData(tag);
setSheared(tag->getBoolean("Sheared"));
setColor((int) tag->getByte("Color"));
}
int Sheep::getColor() const
{
return (int) (entityData.getByte(DATA_WOOL_ID) & 0x0f);
}
void Sheep::setColor( int color )
{
char current = entityData.getByte(DATA_WOOL_ID);
entityData.set(DATA_WOOL_ID, (SynchedEntityData::TypeChar) ((current & 0xf0) | (color & 0x0f)));
}
bool Sheep::isSheared() const
{
return (entityData.getByte(DATA_WOOL_ID) & 0x10) != 0;
}
void Sheep::setSheared( bool value )
{
char current = entityData.getByte(DATA_WOOL_ID);
if (value) {
entityData.set(DATA_WOOL_ID, (SynchedEntityData::TypeChar) (current | 0x10));
} else {
entityData.set(DATA_WOOL_ID, (SynchedEntityData::TypeChar) (current & ~0x10));
}
}
int Sheep::getSheepColor( Random* random )
{
int nextInt = random->nextInt(100);
if (nextInt < 5)
return 15 - DyePowderItem::BLACK;
if (nextInt < 10)
return 15 - DyePowderItem::GRAY;
if (nextInt < 15)
return 15 - DyePowderItem::SILVER;
if (nextInt < 18)
return 15 - DyePowderItem::BROWN;
if (random->nextInt(500) == 0)
return 15 - DyePowderItem::PINK;
return 0; // white
}
int Sheep::getEntityTypeId() const
{
return MobTypes::Sheep;
}
void Sheep::dropDeathLoot(/* bool wasKilledByPlayer, int playerBonusLevel*/ )
{
if (!isSheared()) {
// killing a non-sheared sheep will drop a single block of cloth
spawnAtLocation(new ItemInstance(Tile::cloth->id, 1, getColor()), 0);
}
}
int Sheep::getDeathLoot()
{
return Tile::cloth->id;
}
void Sheep::jumpFromGround()
{
if (eatAnimationTick <= 0) {
super::jumpFromGround();
}
}
void Sheep::updateAi()
{
super::updateAi();
if (!isPathFinding() && eatAnimationTick <= 0 && ((isBaby() && random.nextInt(50) == 0) || random.nextInt(1000) == 0)) {
int xx = Mth::floor(x);
int yy = Mth::floor(y);
int zz = Mth::floor(z);
if (/*(level->getTile(xx, yy, zz) == Tile::tallgrass->id && level->getData(xx, yy, zz) == TallGrass::TALL_GRASS) || */ level->getTile(xx, yy - 1, zz) == ((Tile*)Tile::grass)->id) {
eatAnimationTick = EAT_ANIMATION_TICKS;
level->broadcastEntityEvent(this, EntityEvent::EAT_GRASS);
}
} else if (eatAnimationTick == 4) {
int xx = Mth::floor(x);
int yy = Mth::floor(y);
int zz = Mth::floor(z);
bool ate = false;
/* if (level->getTile(xx, yy, zz) == Tile::tallgrass->id) {
level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, xx, yy, zz, Tile::tallgrass->id + TallGrass::TALL_GRASS * 256);
level->setTile(xx, yy, zz, 0);
ate = true;
} else */if (level->getTile(xx, yy - 1, zz) == ((Tile*)Tile::grass)->id) {
level->levelEvent(NULL, LevelEvent::PARTICLES_DESTROY_BLOCK, xx, yy - 1, zz, ((Tile*)Tile::grass)->id);
level->setTile(xx, yy - 1, zz, Tile::dirt->id);
ate = true;
}
if (ate) {
setSheared(false);
if (isBaby()) {
// remove a minute from aging
int age = getAge() + SharedConstants::TicksPerSecond * 60;
if (age > 0) {
age = 0;
}
setAge(age);
}
}
}
}
bool Sheep::shouldHoldGround()
{
return eatAnimationTick > 0;
}
const char* Sheep::getAmbientSound()
{
return "mob.sheep";
}
std::string Sheep::getHurtSound()
{
return "mob.sheep";
}
std::string Sheep::getDeathSound()
{
return "mob.sheep";
}

91
src/world/entity/animal/Sheep.h Executable file
View File

@@ -0,0 +1,91 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Sheep_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Sheep_H__
//package net.minecraft.world.entity.animal;
#include "Animal.h"
#include "../EntityEvent.h"
#include "../item/ItemEntity.h"
#include "../player/Player.h"
#include "../../item/ItemInstance.h"
#include "../../level/Level.h"
#include "../../../SharedConstants.h"
#include "../../../util/Mth.h"
#include "../../item/Item.h"
#include "../../../nbt/CompoundTag.h"
class Sheep: public Animal
{
typedef Animal super;
static const int EAT_ANIMATION_TICKS = SharedConstants::TicksPerSecond * 2;
static const int DATA_WOOL_ID = 16;
public:
static const float COLOR[][3];
static const int NumColors;
Sheep(Level* level);
/*@Override*/
int getMaxHealth();
/*@Override*/
void aiStep();
//*@Override*/
void handleEntityEvent(char id);
float getHeadEatPositionScale(float a);
float getHeadEatAngleScale(float a);
/*@Override*/
bool interact(Player* player);
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
int getColor() const;
void setColor(int color);
static int getSheepColor(Random* random);
bool isSheared() const;
void setSheared(bool value);
int getEntityTypeId() const;
protected:
/*@Override*/
void dropDeathLoot(/*bool wasKilledByPlayer, int playerBonusLevel*/);
/*@Override*/
int getDeathLoot();
/*@Override*/
void jumpFromGround();
/*@Override*/
void updateAi();
/*@Override*/
bool shouldHoldGround();
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
// /*@Override*/
// Animal getBreedOffspring(Animal target) {
// Sheep otherSheep = (Sheep) target;
// Sheep sheep = /*new*/ Sheep(level);
// if (random.nextBoolean()) sheep.setColor(getColor());
// else sheep.setColor(otherSheep.getColor());
// return sheep;
// }
private:
int eatAnimationTick;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__Sheep_H__*/

View File

@@ -0,0 +1,48 @@
#include "WaterAnimal.h"
#include "../../level/Level.h"
WaterAnimal::WaterAnimal( Level* level )
: super(level)
{
}
bool WaterAnimal::isWaterMob()
{
// prevent drowning
return true;
}
void WaterAnimal::addAdditonalSaveData( CompoundTag* entityTag )
{
super::addAdditonalSaveData(entityTag);
}
void WaterAnimal::readAdditionalSaveData( CompoundTag* tag )
{
super::readAdditionalSaveData(tag);
}
bool WaterAnimal::canSpawn()
{
return level->isUnobstructed(bb);
}
int WaterAnimal::getAmbientSoundInterval()
{
return SharedConstants::TicksPerSecond * 6;
}
bool WaterAnimal::removeWhenFarAway()
{
return true;
}
int WaterAnimal::getExperienceReward( Player* killedBy )
{
return 1 + level->random.nextInt(3);
}
int WaterAnimal::getCreatureBaseType() const {
return MobTypes::BaseWaterCreature;
}

View File

@@ -0,0 +1,46 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ANIMAL__WaterAnimal_H__
#define NET_MINECRAFT_WORLD_ENTITY_ANIMAL__WaterAnimal_H__
//package net.minecraft.world.entity.animal;
#include "../PathfinderMob.h"
#include "../Creature.h"
class Level;
class Player;
class CompoundTag;
/**
* The purpose of this class is to offer a new base class for MobCategory.
* {@link MobCategory}
*
* Note: Can't extend Animal because then water animals would prevent normal
* animals to spawn, due to MobCategory.getMaxInstancesPerChunk(). This class is
* otherwise similar to Animal
*
*/
class WaterAnimal: public PathfinderMob, public Creature
{
typedef PathfinderMob super;
public:
WaterAnimal(Level* level);
/*@Override*/
bool isWaterMob();
void addAdditonalSaveData(CompoundTag* entityTag);
void readAdditionalSaveData(CompoundTag* tag);
bool canSpawn();
int getAmbientSoundInterval();
int getCreatureBaseType() const;
protected:
bool removeWhenFarAway();
/*@Override*/
int getExperienceReward(Player* killedBy);
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ANIMAL__WaterAnimal_H__*/

View File

@@ -0,0 +1,117 @@
#include "FallingTile.h"
#include "../../level/Level.h"
#include "../../../util/Random.h"
#include "../../../nbt/CompoundTag.h"
FallingTile::FallingTile( Level* level )
: Entity(level),
tile(0),
data(0)
{
init();
}
FallingTile::FallingTile( Level* level, float x, float y, float z, int tile, int data)
: Entity(level),
tile(tile),
data(data)
{
init();
setPos(x, y, z);
xo = xOld = x;
yo = yOld = y;
zo = zOld = z;
}
void FallingTile::init() {
entityRendererId = ER_FALLINGTILE_RENDERER;
time = 0;
blocksBuilding = true;
setSize(0.98f, 0.98f);
heightOffset = bbHeight / 2.0f;
makeStepSound = false;
xd = 0;
yd = 0;
zd = 0;
}
bool FallingTile::isPickable() {
return !removed;
}
void FallingTile::tick() {
if (tile == 0) {
remove();
return;
}
xo = x;
yo = y;
zo = z;
time++;
yd -= 0.04f;
move(xd, yd, zd);
xd *= 0.98f;
yd *= 0.98f;
zd *= 0.98f;
if (!level->isClientSide) {
int xt = Mth::floor(x);
int yt = Mth::floor(y);
int zt = Mth::floor(z);
if (time == 1) {
if (level->getTile(xt, yt, zt) == tile) {
level->setTile(xt, yt, zt, 0);
} else {
remove();
return;
}
}
if (onGround) {
xd *= 0.7f;
zd *= 0.7f;
yd *= -0.5f;
remove();
if (level->mayPlace(tile, xt, yt, zt, true, 1) && level->setTileAndData(xt, yt, zt, tile, data)) {
} else if (!level->isClientSide) {
//spawnAtLocation(tile, 1);
}
} else if (time > SharedConstants::TicksPerSecond * 5 && !level->isClientSide) {
//spawnAtLocation(tile, 1);
remove();
}
}
}
float FallingTile::getShadowHeightOffs() {
return 0;
}
Level* FallingTile::getLevel() {
return level;
}
void FallingTile::addAdditonalSaveData( CompoundTag* tag ) {
tag->putByte("Tile", (char) tile);
tag->putByte("Data", (char) data);
tag->putByte("Time", (char) time);
}
void FallingTile::readAdditionalSaveData( CompoundTag* tag ) {
tile = tag->getByte("Tile");
data = tag->getByte("Data");
time = tag->getByte("Time");
}
int FallingTile::getEntityTypeId() const {
return EntityTypes::IdFallingTile;
}

View File

@@ -0,0 +1,40 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ITEM__FallingTile_H__
#define NET_MINECRAFT_WORLD_ENTITY_ITEM__FallingTile_H__
//package net.minecraft.world.entity.item;
#include "../Entity.h"
class Level;
class CompoundTag;
class FallingTile: public Entity
{
public:
FallingTile(Level* level);
FallingTile(Level* level, float x, float y, float z, int tile, int data = 0);
void init();
bool isPickable();
void tick();
int getEntityTypeId() const;
float getShadowHeightOffs();
Level* getLevel();
protected:
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
public:
int tile;
int data;
int time;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ITEM__FallingTile_H__*/

View File

@@ -0,0 +1,211 @@
#include "ItemEntity.h"
#include "../../entity/player/Player.h"
#include "../../item/ItemInstance.h"
#include "../../level/Level.h"
#include "../../level/material/Material.h"
#include "../../level/tile/Tile.h"
#include "../../../util/Mth.h"
#include "../../../nbt/CompoundTag.h"
const int ItemEntity::LIFETIME = 5 * 60 * SharedConstants::TicksPerSecond; // Five minutes, changed in 0.3.3!
ItemEntity::ItemEntity( Level* level, float x, float y, float z, const ItemInstance& item )
: super(level),
item(item),
health(5),
age(0),
tickCount(0),
throwTime(0),
lifeTime(LIFETIME),
bobOffs((float) (Mth::random() * Mth::PI * 2))
{
entityRendererId = ER_ITEM_RENDERER;
setSize(0.25f, 0.25f);
heightOffset = bbHeight / 2.0f;
setPos(x, y, z);
yRot = (float) (Mth::random() * 360);
xd = (float) (Mth::random() * 0.2f - 0.1f);
yd = +0.2f;
zd = (float) (Mth::random() * 0.2f - 0.1f);
makeStepSound = false;
}
ItemEntity::ItemEntity( Level* level )
: super(level),
health(5),
age(0),
tickCount(0),
throwTime(0),
lifeTime(LIFETIME),
bobOffs((float) (Mth::random() * Mth::PI * 2))
{
entityRendererId = ER_ITEM_RENDERER;
setSize(0.25f, 0.25f);
heightOffset = bbHeight / 2.0f;
}
ItemEntity::~ItemEntity()
{
}
void ItemEntity::tick()
{
super::tick();
if (throwTime > 0) throwTime--;
xo = x;
yo = y;
zo = z;
yd -= 0.04f;
if (level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)) == Material::lava) {
yd = 0.2f;
xd = (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.2f;
zd = (sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.2f;
level->playSound(this, "random.fizz", 0.4f, 2.0f + sharedRandom.nextFloat() * 0.4f);
}
checkInTile(x, y, z);
move(xd, yd, zd);
float friction = 0.98f;
if (onGround) {
friction = 0.6f * 0.98f;
int t = level->getTile(Mth::floor(x), Mth::floor(bb.y0) - 1, Mth::floor(z));
if (t > 0) {
friction = Tile::tiles[t]->friction * 0.98f;
}
}
xd *= friction;
yd *= 0.98f;
zd *= friction;
if (onGround) {
yd *= -0.5f;
}
tickCount++;
age++;
if (age >= lifeTime) {
remove();
}
}
bool ItemEntity::isInWater()
{
return level->checkAndHandleWater(bb, Material::water, this);
}
bool ItemEntity::hurt( Entity* source, int damage )
{
markHurt();
health -= damage;
if (health <= 0) {
remove();
}
return false;
}
void ItemEntity::playerTouch( Player* player )
{
if (level->isClientSide) return;
int orgCount = item.count;
if (throwTime == 0 && player->isAlive() && player->inventory->add(&item)) {
level->playSound(this, "random.pop", 0.3f, ((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f + 1.0f) * 2.f);
player->take(this, orgCount);
//if (item.count <= 0) remove(); //@todo
remove();
}
}
void ItemEntity::burn( int dmg )
{
hurt(NULL, dmg);
}
bool ItemEntity::checkInTile( float x, float y, float z )
{
int xTile = Mth::floor(x);
int yTile = Mth::floor(y);
int zTile = Mth::floor(z);
float xd = x - xTile;
float yd = y - yTile;
float zd = z - zTile;
if (Tile::solid[level->getTile(xTile, yTile, zTile)]) {
bool west = !Tile::solid[level->getTile(xTile - 1, yTile, zTile)];
bool east = !Tile::solid[level->getTile(xTile + 1, yTile, zTile)];
bool up = !Tile::solid[level->getTile(xTile, yTile - 1, zTile)];
bool down = !Tile::solid[level->getTile(xTile, yTile + 1, zTile)];
bool north = !Tile::solid[level->getTile(xTile, yTile, zTile - 1)];
bool south = !Tile::solid[level->getTile(xTile, yTile, zTile + 1)];
int dir = -1;
float closest = 9999;
if (west && xd < closest) {
closest = xd;
dir = 0;
}
if (east && 1 - xd < closest) {
closest = 1 - xd;
dir = 1;
}
if (up && yd < closest) {
closest = yd;
dir = 2;
}
if (down && 1 - yd < closest) {
closest = 1 - yd;
dir = 3;
}
if (north && zd < closest) {
closest = zd;
dir = 4;
}
if (south && 1 - zd < closest) {
// closest = 1 - zd; //@note: not read
dir = 5;
}
float speed = sharedRandom.nextFloat() * 0.2f + 0.1f;
if (dir == 0) this->xd = -speed;
if (dir == 1) this->xd = +speed;
if (dir == 2) this->yd = -speed;
if (dir == 3) this->yd = +speed;
if (dir == 4) this->zd = -speed;
if (dir == 5) this->zd = +speed;
}
return false;
}
void ItemEntity::addAdditonalSaveData(CompoundTag* entityTag) {
entityTag->putShort("Health", (char) health);
entityTag->putShort("Age", (short) age);
entityTag->putCompound("Item", item.save(new CompoundTag()));
}
void ItemEntity::readAdditionalSaveData(CompoundTag* tag) {
health = tag->getShort("Health") & 0xff;
age = tag->getShort("Age");
CompoundTag* itemTag = tag->getCompound("Item");
item.load(itemTag);
}
bool ItemEntity::isItemEntity() {
return true;
}
int ItemEntity::getEntityTypeId() const {
return EntityTypes::IdItemEntity;
}
int ItemEntity::getLifeTime() const {
return lifeTime;
}

View File

@@ -0,0 +1,49 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ITEM__ItemEntity_H__
#define NET_MINECRAFT_WORLD_ENTITY_ITEM__ItemEntity_H__
//package net.minecraft.world.entity.item;
#include "../../item/ItemInstance.h"
#include "../../entity/Entity.h"
#include "../../../SharedConstants.h"
class Level;
class ItemInstance;
class ItemEntity: public Entity
{
typedef Entity super;
static const int LIFETIME;
public:
ItemEntity(Level* level);
ItemEntity(Level* level, float x, float y, float z, const ItemInstance& item);
~ItemEntity();
void tick();
bool isInWater();
bool hurt(Entity* source, int damage);
void playerTouch(Player* player);
bool isItemEntity();
int getEntityTypeId() const;
int getLifeTime() const;
protected:
void burn(int dmg);
void addAdditonalSaveData(CompoundTag* entityTag);
void readAdditionalSaveData(CompoundTag* tag);
private:
bool checkInTile(float x, float y, float z);
public:
ItemInstance item;
int age;
int throwTime;
float bobOffs;
private:
int tickCount;
int health;
int lifeTime;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ITEM__ItemEntity_H__*/

View File

@@ -0,0 +1,91 @@
#include "PrimedTnt.h"
#include "../../../nbt/CompoundTag.h"
PrimedTnt::PrimedTnt( Level* level )
: super(level),
life(80)
{
entityRendererId = ER_TNT_RENDERER ;
blocksBuilding = true;
setSize(0.98f, 0.98f);
heightOffset = bbHeight / 2.0f;
}
PrimedTnt::PrimedTnt( Level* level, float x, float y, float z )
: super(level),
life(80)
{
entityRendererId = ER_TNT_RENDERER ;
blocksBuilding = true;
setSize(0.98f, 0.98f);
heightOffset = bbHeight / 2.0f;
setPos(x, y, z);
float rot = Mth::random() * Mth::PI * 2.0f;
xd = Mth::sin(rot * Mth::DEGRAD) * -0.02f;
yd = +0.2f;
zd = Mth::cos(rot * Mth::DEGRAD) * -0.02f;
makeStepSound = false;
xo = x;
yo = y;
zo = z;
}
bool PrimedTnt::isPickable()
{
return !removed;
}
void PrimedTnt::tick()
{
xo = x;
yo = y;
zo = z;
yd -= 0.04f;
move(xd, yd, zd);
xd *= 0.98f;
yd *= 0.98f;
zd *= 0.98f;
if (onGround) {
xd *= 0.7f;
zd *= 0.7f;
yd *= -0.5f;
}
life--;
if (!level->isClientSide && life <= 0) {
remove();
explode();
} else {
level->addParticle(PARTICLETYPE(smoke), x, y + 0.5f, z, 0, 0, 0);
}
}
float PrimedTnt::getShadowHeightOffs()
{
return 0;
}
void PrimedTnt::explode()
{
float r = 3.1f;
level->explode(NULL, x, y, z, r);
}
void PrimedTnt::addAdditonalSaveData(CompoundTag* entityTag) {
entityTag->putByte("Fuse", life);
}
void PrimedTnt::readAdditionalSaveData(CompoundTag* tag) {
life = tag->getByte("Fuse");
}
int PrimedTnt::getEntityTypeId() const {
return EntityTypes::IdPrimedTnt;
}

View File

@@ -0,0 +1,36 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ITEM__PrimedTnt_H__
#define NET_MINECRAFT_WORLD_ENTITY_ITEM__PrimedTnt_H__
//package net.minecraft.world.entity.item;
#include "../Entity.h"
#include "../../level/Level.h"
class CompoundTag;
class PrimedTnt: public Entity
{
typedef Entity super;
public:
PrimedTnt(Level* level);
PrimedTnt(Level* level, float x, float y, float z);
void tick();
bool isPickable();
int getEntityTypeId() const;
float getShadowHeightOffs();
protected:
void addAdditonalSaveData(CompoundTag* entityTag);
void readAdditionalSaveData(CompoundTag* tag);
private:
void explode();
public:
int life;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ITEM__PrimedTnt_H__*/

View File

@@ -0,0 +1,88 @@
#include "TripodCamera.h"
#include "../player/Player.h"
#include "../../level/Level.h"
TripodCamera::TripodCamera( Level* level, Player* owner, float x, float y, float z )
: super(level),
owner(owner),
life(80),
activated(false)
{
entityRendererId = ER_TRIPODCAMERA_RENDERER;
// Copy rotation from the entity placing the camera
xRot = xRotO = owner->xRot;
yRot = yRotO = owner->yRot;
blocksBuilding = true;
setSize(1.0f, 1.5f);
heightOffset = bbHeight / 2.0f - 0.25f;
setPos(x, y, z);
xo = x;
yo = y;
zo = z;
}
bool TripodCamera::isPickable()
{
return !removed;
}
bool TripodCamera::interactPreventDefault()
{
return true;
}
bool TripodCamera::interact( Player* player )
{
activated = true;
return true;
}
void TripodCamera::tick()
{
xo = x;
yo = y;
zo = z;
yd -= 0.04f;
move(xd, yd, zd);
xd *= 0.98f;
yd *= 0.98f;
zd *= 0.98f;
if (onGround) {
xd *= 0.7f;
zd *= 0.7f;
yd *= -0.5f;
}
if (activated) {
--life;
if (life == 0) {
remove();
} else if (life == 8) {
level->takePicture(this, owner);
level->addParticle(PARTICLETYPE(explode), x, y + 0.6f, z, 0, 0, 0);
level->addParticle(PARTICLETYPE(explode), x, y + 0.8f, z, 0, 0, 0);
level->addParticle(PARTICLETYPE(explode), x, y + 1.0f, z, 0, 0, 0);
} else if (life > 8) {
level->addParticle(PARTICLETYPE(smoke), x, y + 1.0f, z, 0, 0, 0);
}
}
}
float TripodCamera::getShadowHeightOffs()
{
return 0;
}
bool TripodCamera::isPushable()
{
return false;
}

View File

@@ -0,0 +1,32 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_ITEM__TripodCamera_H__
#define NET_MINECRAFT_WORLD_ENTITY_ITEM__TripodCamera_H__
#include "../Mob.h"
class TripodCamera: public Mob
{
typedef Mob super;
public:
TripodCamera(Level* level, Player* owner_, float x, float y, float z);
void tick();
bool isPickable();
bool isPushable();
// id == 0 -> not possible to create via serialization (yet)
int getEntityTypeId() const { return 0; }
bool interact(Player* player);
bool interactPreventDefault();
float getShadowHeightOffs();
public:
int life;
protected:
Player* owner;
bool activated;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_ITEM__TripodCamera_H__*/

View File

@@ -0,0 +1,114 @@
#include "Creeper.h"
#include "../Entity.h"
#include "../../item/Item.h"
#include "../../level/Level.h"
#include "../../../nbt/CompoundTag.h"
Creeper::Creeper( Level* level )
: super(level),
swell(0),
oldSwell(0),
swellDir(-1)
{
entityRendererId = ER_CREEPER_RENDERER;
this->textureName = "mob/creeper.png";
entityData.define(DATA_SWELL_DIR, (SynchedEntityData::TypeChar) -1);
}
int Creeper::getMaxHealth() {
return 16;
}
void Creeper::tick() {
oldSwell = swell;
if (level->isClientSide) {
int swellDir = getSwellDir();
if (swellDir > 0 && swell == 0) {
level->playSound(this, "random.fuse", 1, 0.5f);
}
swell += swellDir;
if (swell < 0) swell = 0;
if (swell >= MAX_SWELL) swell = MAX_SWELL;
}
super::tick();
if (!level->isClientSide && attackTargetId == 0) {
if (swell > 0) {
setSwellDir(-1);
swell--;
if (swell < 0) {
swell = 0;
}
}
}
}
float Creeper::getSwelling( float a ) {
return (oldSwell + (swell - oldSwell) * a) / (MAX_SWELL - 2);
}
int Creeper::getEntityTypeId() const {
return MobTypes::Creeper;
}
int Creeper::getDeathLoot() {
return Item::sulphur->id;
}
void Creeper::checkCantSeeTarget( Entity* target, float d ) {
if (level->isClientSide) return;
if (swell > 0) {
setSwellDir(-1);
swell--;
if (swell < 0) {
swell = 0;
}
}
}
std::string Creeper::getHurtSound() {
return "mob.creeper";
}
std::string Creeper::getDeathSound() {
return "mob.creeperdeath";
}
void Creeper::checkHurtTarget( Entity* target, float d ) {
if (level->isClientSide) return;
const int swellDir = getSwellDir();
if ((swellDir <= 0 && d < 3) || (swellDir > 0 && d < 7)) {
if (swell == 0) {
level->playSound(this, "random.fuse", 1, 0.5f);
}
setSwellDir(1);
if (++swell >= MAX_SWELL) {
level->explode(this, x, y, z, 2.4f);
remove();
}
holdGround = true;
} else {
setSwellDir(-1);
if (--swell < 0)
swell = 0;
}
}
int Creeper::getSwellDir() {
return (int) entityData.getByte(DATA_SWELL_DIR);
}
void Creeper::setSwellDir( int dir ) {
entityData.set(DATA_SWELL_DIR, (SynchedEntityData::TypeChar) dir);
}
//@todo
//void die(DamageSource* source) {
// super::die(source);
// if (source.getEntity() instanceof Skeleton) {
// spawnAtLocation(Item::record_01->id + random.nextInt(2), 1);
// }
//}

View File

@@ -0,0 +1,46 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__Creeper_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__Creeper_H__
//package net.minecraft.world.entity.monster;
#include "Monster.h"
#include <string>
class Entity;
class Creeper: public Monster
{
typedef Monster super;
public:
Creeper(Level* level);
/*@Override*/
int getMaxHealth();
void tick();
float getSwelling(float a);
virtual int getEntityTypeId() const;
protected:
int getDeathLoot();
void checkCantSeeTarget(Entity* target, float d);
void checkHurtTarget(Entity* target, float d);
std::string getHurtSound();
std::string getDeathSound();
private:
int getSwellDir();
void setSwellDir(int dir);
int swell;
int oldSwell;
int swellDir;
static const int DATA_SWELL_DIR = 16;
static const int MAX_SWELL = 30;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__Creeper_H__*/

View File

@@ -0,0 +1,144 @@
#include "Monster.h"
#include "../player/Player.h"
#include "../../level/Level.h"
#include "../../Difficulty.h"
#include "../../../util/Mth.h"
//#include "../../effect/MobEffect.h"
Monster::Monster(Level* level)
: super(level),
attackDamage(2),
targetId(0),
lastHurtByMobId(0)
{
entityRendererId = ER_HUMANOID_RENDERER;
//xpReward = Enemy.XP_REWARD_MEDIUM;
}
void Monster::aiStep()
{
updateAttackAnim();
float br = getBrightness(1);
if (br > 0.5f) {
noActionTime += 2;
}
super::aiStep();
}
void Monster::tick()
{
super::tick();
if (!level->isClientSide && level->difficulty == Difficulty::PEACEFUL) remove();
}
bool Monster::hurt( Entity* sourceEntity, int dmg )
{
if (super::hurt(sourceEntity, dmg)) {
if (sourceEntity != this) {
attackTargetId = 0;
if (sourceEntity != NULL) {
attackTargetId = sourceEntity->entityId;
if (sourceEntity->isMob())
lastHurtByMobId = sourceEntity->entityId;
}
}
return true;
}
return false;
}
bool Monster::canSpawn()
{
return isDarkEnoughToSpawn() && super::canSpawn();
}
Entity* Monster::findAttackTarget()
{
//Player* player = level->getNearestAttackablePlayer(this, 16);
Player* player = level->getNearestPlayer(this, 16);
//LOGI("playuer: %p\n", player);
if (player != NULL && canSee(player)) return player;
return NULL;
}
bool Monster::isDarkEnoughToSpawn()
{
//static int st = getTimeMs();
//return getTimeMs() - st > 10000;
int xt = Mth::floor(x);
int yt = Mth::floor(bb.y0);
int zt = Mth::floor(z);
if (level->getBrightness(LightLayer::Sky, xt, yt, zt) > random.nextInt(32)) return false;
int br = level->getRawBrightness(xt, yt, zt);
//if (level->isThundering()) {
// int tmp = level->skyDarken;
// level->skyDarken = 10;
// br = level->getRawBrightness(xt, yt, zt);
// level->skyDarken = tmp;
//}
//LOGI("br: %d\n", br);
return br <= random.nextInt(8); // was 8
}
bool Monster::doHurtTarget( Entity* target )
{
swing();
//if (target->isMob()) setLastHurtMob(target);
//@todo
int dmg = attackDamage;
//if (hasEffect(MobEffect.damageBoost)) {
// dmg += (3 << getEffect(MobEffect.damageBoost).getAmplifier());
//}
//if (hasEffect(MobEffect.weakness)) {
// dmg -= (2 << getEffect(MobEffect.weakness).getAmplifier());
//}
return target->hurt(this, dmg);
}
void Monster::checkHurtTarget( Entity* target, float distance ) {
if (attackTime <= 0 && distance < 2.0f && target->bb.y1 > bb.y0 && target->bb.y0 < bb.y1) {
attackTime = getAttackTime();
doHurtTarget(target);
}
}
float Monster::getWalkTargetValue( int x, int y, int z )
{
return 0.5f - level->getBrightness(x, y, z);
}
int Monster::getCreatureBaseType() const {
return MobTypes::BaseEnemy;
}
Mob* Monster::getTarget() {
if (targetId == 0) return NULL;
return level->getMob(targetId);
}
void Monster::setTarget(Mob* mob) {
targetId = mob? mob->entityId : 0;
}
Mob* Monster::getLastHurtByMob() {
if (targetId == 0) return NULL;
return level->getMob(lastHurtByMobId);
}
void Monster::setLastHurtByMob(Mob* mob) {
lastHurtByMobId = mob? mob->entityId : 0;
}
int Monster::getAttackDamage( Entity* target ) {
return attackDamage;
}
int Monster::getAttackTime() {
return 20;
}

View File

@@ -0,0 +1,57 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__Monster_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__Monster_H__
//package net.minecraft.world.entity->monster;
#include "../PathfinderMob.h"
class Level;
class Entity;
//class DamageSource;
class CompoundTag;
class Monster: public PathfinderMob//implements Enemy
{
typedef PathfinderMob super;
public:
Monster(Level* level);
void aiStep();
void tick();
//bool hurt(DamageSource* source, int dmg) {
bool hurt(Entity* sourceEntity, int dmg);
/*@Override*/
bool canSpawn();
int getCreatureBaseType() const;
bool doHurtTarget(Entity* target);
void setTarget(Mob* mob);
Mob* getTarget();
Mob* getLastHurtByMob();
void setLastHurtByMob(Mob* mob);
virtual int getAttackDamage(Entity* target);
protected:
Entity* findAttackTarget();
bool isDarkEnoughToSpawn();
/**
* Performs hurt action, returns if successful
*
* @param target
* @return
*/
void checkHurtTarget(Entity* target, float distance);
float getWalkTargetValue(int x, int y, int z);
virtual int getAttackTime();
int attackDamage;
int targetId;
int lastHurtByMobId;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__Monster_H__*/

View File

@@ -0,0 +1,10 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__MonsterInclude_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__MonsterInclude_H__
#include "Creeper.h"
#include "Monster.h"
#include "Skeleton.h"
#include "Spider.h"
#include "Zombie.h"
#include "PigZombie.h"
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__MonsterInclude_H__*/

View File

@@ -0,0 +1,138 @@
#include "PigZombie.h"
#include "../../item/Item.h"
#include "../../level/Level.h"
#include "../../Difficulty.h"
#include "../Entity.h"
#include "../projectile/Arrow.h"
PigZombie::PigZombie( Level* level )
: super(level)
, angerTime(0)
, playAngrySoundIn(0)
, weapon(Item::sword_gold)
, stunedTime(SharedConstants::TicksPerSecond * 3){
textureName = "mob/pigzombie.png";
runSpeed = 0.7f;
fireImmune = true;
attackDamage = 5;
}
bool PigZombie::useNewAi() {
return false;
}
void PigZombie::tick() {
//runSpeed = attackTargetId != 0 ? 0.95f : 0.5f;
if(stunedTime > 0)
stunedTime--;
if(playAngrySoundIn > 0) {
if(--playAngrySoundIn == 0) {
level->playSound(x, y, z, "mob.zombiepig.zpigangry", getSoundVolume() * 2, ((random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f) * 1.8f);
}
}
super::tick();
}
std::string PigZombie::getTexture() {
return "mob/pigzombie.png";
}
bool PigZombie::canSpawn() {
return level->difficulty > Difficulty::PEACEFUL && level->isUnobstructed(bb) && level->getCubes(this, bb).empty();
}
void PigZombie::addAdditonalSaveData( CompoundTag* entityTag ) {
super::addAdditonalSaveData(entityTag);
entityTag->putShort("Anger", (short)angerTime);
}
void PigZombie::readAdditionalSaveData( CompoundTag* tag ) {
super::readAdditionalSaveData(tag);
angerTime = tag->getShort("Anger");
}
Entity* PigZombie::findAttackTarget() {
if(stunedTime != 0)
return NULL;
if(angerTime == 0) {
Entity* potentialTarget = super::findAttackTarget();
if(potentialTarget != NULL && potentialTarget->distanceTo(x, y, z) < 5) {
return potentialTarget;
}
return NULL;
}
return super::findAttackTarget();
}
bool PigZombie::hurt( Entity* sourceEntity, int dmg ) {
Entity* attacker = NULL;
if(sourceEntity != NULL) {
if(sourceEntity->isPlayer()) {
attacker = sourceEntity;
}
else if(sourceEntity->isEntityType(EntityTypes::IdArrow)) {
Arrow* arrow = (Arrow*)sourceEntity;
if(arrow->ownerId != 0) {
attacker = level->getEntity(arrow->ownerId);
if(!attacker->isPlayer())
attacker = NULL;
}
}
}
if(attacker != NULL) {
EntityList nearby = level->getEntities(this, bb.grow(12, 12, 12));
for(EntityList::iterator it = nearby.begin(); it != nearby.end(); ++it) {
if((*it)->isEntityType(MobTypes::PigZombie)) {
PigZombie* pigZombie = (PigZombie*)(*it);
pigZombie->alert(sourceEntity);
}
}
}
return super::hurt(sourceEntity, dmg);
}
void PigZombie::alert( Entity* target ) {
attackTargetId = target->entityId;
angerTime = 20 * 20 * random.nextInt(20 * 20);
playAngrySoundIn = random.nextInt(20 * 2);
}
const char* PigZombie::getAmbientSound() {
return "mob.zombiepig.zpig";
}
std::string PigZombie::getHurtSound() {
return "mob.zombiepig.zpighurt";
}
std::string PigZombie::getDeathSound() {
return "mob.zombiepig.zpigdeath";
}
void PigZombie::dropDeathLoot() {
int count = random.nextInt(2);
for(int i = 0; i < count; ++i) {
// We really should spawn gold nuggets instead of ingots.
spawnAtLocation(Item::goldIngot->id, 1);
}
}
bool PigZombie::interact( Player* player ) {
return false;
}
int PigZombie::getDeathLoot() {
return 0;
}
ItemInstance* PigZombie::getCarriedItem() {
return &weapon;
}
int PigZombie::getEntityTypeId() const {
return MobTypes::PigZombie;
}
int PigZombie::getAttackTime() {
return 40;
}

View File

@@ -0,0 +1,38 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__PigZombie_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__PigZombie_H__
#include "Zombie.h"
class ItemInstance;
class PigZombie : public Zombie {
typedef Zombie super;
public:
PigZombie(Level* level);
bool useNewAi();
void tick();
std::string getTexture();
bool canSpawn();
void addAdditonalSaveData(CompoundTag* entityTag);
void readAdditionalSaveData(CompoundTag* tag);
bool hurt(Entity* sourceEntity, int dmg);
bool interact(Player* player);
int getEntityTypeId() const;
virtual int getAttackTime();
ItemInstance* getCarriedItem();
protected:
Entity* findAttackTarget();
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
void dropDeathLoot();
int getDeathLoot();
private:
void alert(Entity* target);
private:
int angerTime;
int playAngrySoundIn;
int stunedTime;
ItemInstance weapon;
};
#endif /* NET_MINECRAFT_WORLD_ENTITY_MONSTER__PigZombie_H__ */

View File

@@ -0,0 +1,107 @@
#include "Skeleton.h"
#include "../projectile/Arrow.h"
Skeleton::Skeleton( Level* level )
: super(level),
bow(Item::bow, 1),
fireCheckTick(0)
{
entityRendererId = ER_SKELETON_RENDERER;
this->textureName = "mob/skeleton.png";
}
int Skeleton::getMaxHealth() {
return 10; // 15
}
void Skeleton::aiStep() {
if ((++fireCheckTick & 1) && level->isDay() && !level->isClientSide) {
float br = getBrightness(1);
if (br > 0.5f) {
if (level->canSeeSky(Mth::floor(x), Mth::floor(y), Mth::floor(z)) && random.nextFloat() * 3.5f < (br - 0.4f)) {
hurt(NULL, 1);
for (int i = 0; i < 5; ++i) {
float xa = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
float ya = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
float za = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
level->addParticle(PARTICLETYPE(explode), x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
}
//setOnFire(8); //@todo
}
}
}
super::aiStep();
}
int Skeleton::getDeathLoot() {
return Item::arrow->id;
}
ItemInstance* Skeleton::getCarriedItem() {
return &bow;
}
int Skeleton::getEntityTypeId() const {
return MobTypes::Skeleton;
}
const char* Skeleton::getAmbientSound() {
return "mob.skeleton";
}
std::string Skeleton::getHurtSound() {
return "mob.skeletonhurt";
}
std::string Skeleton::getDeathSound() {
return "mob.skeletonhurt";
}
void Skeleton::checkHurtTarget( Entity* target, float d ) {
if (d < 10) {
float xd = target->x - x;
float zd = target->z - z;
if (attackTime == 0) {
Arrow* arrow = new Arrow(level, this, 1);
// arrow.y += 1.4f;
float yd = (target->y + target->getHeadHeight() - 0.7f) - arrow->y;
float yo = Mth::sqrt(xd * xd + zd * zd) * 0.2f;
level->playSound(this, "random.bow", 1.0f, 1 / (random.nextFloat() * 0.4f + 0.8f));
level->addEntity(arrow);
arrow->shoot(xd, yd + yo, zd, 1.60f, 32);
attackTime = SharedConstants::TicksPerSecond * 3;
}
yRot = (float) (std::atan2(zd, xd) * Mth::RADDEG) - 90;
holdGround = true;
}
}
void Skeleton::dropDeathLoot( /*bool wasKilledByPlayer, int playerBonusLevel*/ ) {
// drop some arrows
int count = random.nextInt(3 /*+ playerBonusLevel*/);
for (int i = 0; i < count; i++) {
spawnAtLocation(Item::arrow->id, 1);
}
// and some bones
count = random.nextInt(3 /*+ playerBonusLevel*/);
for (int i = 0; i < count; i++) {
spawnAtLocation(Item::bone->id, 1);
}
}
int Skeleton::getUseDuration() {
return attackTime;
}
/*@Override*/
// MobType getMobType() {
// return MobType.UNDEAD;
// }

View File

@@ -0,0 +1,43 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__Skeleton_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__Skeleton_H__
//package net.minecraft.world.entity->monster;
#include "Monster.h"
#include <string>
#include "../../item/ItemInstance.h"
class Level;
class Entity;
class Skeleton: public Monster
{
typedef Monster super;
public:
Skeleton(Level* level);
/*@Override*/
int getMaxHealth();
void aiStep();
int getDeathLoot();
ItemInstance* getCarriedItem();
virtual int getEntityTypeId() const;
protected:
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
void checkHurtTarget(Entity* target, float d);
/*@Override*/
void dropDeathLoot(/*bool wasKilledByPlayer, int playerBonusLevel*/);
virtual int getUseDuration();
private:
ItemInstance bow;
int fireCheckTick;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__Skeleton_H__*/

View File

@@ -0,0 +1,137 @@
#include "Spider.h"
#include "../../item/Item.h"
#include "../../level/Level.h"
#include "../../../util/Mth.h"
Spider::Spider( Level* level )
: super(level),
fireCheckTick(0)
{
entityRendererId = ER_SPIDER_RENDERER;
this->textureName = "mob/spider.png";
this->setSize(1.4f, 0.9f);
runSpeed = 0.5f;
entityData.define(DATA_FLAGS_ID, (DataFlagIdType) 0);
}
void Spider::aiStep() {
super::aiStep();
}
void Spider::tick() {
super::tick();
if (!level->isClientSide) {
// this is to synchronize the spiders' climb state
// in multiplayer (to stop them from "flashing")
setClimbing(horizontalCollision);
}
}
int Spider::getMaxHealth() {
return 8; // 12
}
bool Spider::onLadder() {
return isClimbing();
}
void Spider::makeStuckInWeb() {
// do nothing - spiders don't get stuck in web
}
float Spider::getModelScale() {
return 1.0f;
}
bool Spider::isClimbing() {
return entityData.getFlag<DataFlagIdType>(DATA_FLAGS_ID, 0);
}
void Spider::setClimbing( bool value ) {
if (value)
return entityData.setFlag<DataFlagIdType>(DATA_FLAGS_ID, 0);
else
return entityData.clearFlag<DataFlagIdType>(DATA_FLAGS_ID, 0);
}
int Spider::getEntityTypeId() const {
return MobTypes::Spider;
}
bool Spider::makeStepSound() {
return false;
}
Entity* Spider::findAttackTarget() {
float br = getBrightness(1);
if (br < 0.5f) {
return level->getNearestPlayer(this, 16);
}
return NULL;
}
const char* Spider::getAmbientSound() {
return "mob.spider";
}
std::string Spider::getHurtSound() {
return "mob.spider";
}
std::string Spider::getDeathSound() {
return "mob.spiderdeath";
}
void Spider::checkHurtTarget( Entity* target, float d ) {
float br = getBrightness(1);
if (br > 0.5f && random.nextInt(100) == 0) {
attackTargetId = 0;
return;
}
if (d > 2 && d < 6 && random.nextInt(10) == 0) {
if (onGround) {
float xdd = target->x - x;
float zdd = target->z - z;
float dd = Mth::sqrt(xdd * xdd + zdd * zdd);
xd = (xdd / dd * 0.5f) * 0.8f + xd * 0.2f;
zd = (zdd / dd * 0.5f) * 0.8f + zd * 0.2f;
yd = 0.4f;
}
} else {
super::checkHurtTarget(target, d);
}
}
int Spider::getDeathLoot() {
return Item::string->id;
}
//void dropDeathLoot(/*bool wasKilledByPlayer, int playerBonusLevel*/) {
// super::dropDeathLoot(/*wasKilledByPlayer, playerBonusLevel*/);
//// if (wasKilledByPlayer && (random.nextInt(3) == 0 || random.nextInt(1 + playerBonusLevel) > 0)) {
//// spawnAtLocation(Item::spiderEye->id, 1);
//// }
//}
//float getRideHeight() {
// return bbHeight * .75f - 0.5f;
//}
/*@Override*/
// MobType getMobType() {
// return MobType.ARTHROPOD;
// }
/*@Override*/ //@todo
// bool canBeAffected(MobEffectInstance newEffect) {
// if (newEffect.getId() == MobEffect.poison.id) {
// return false;
// }
// return super::canBeAffected(newEffect);
// }
//

View File

@@ -0,0 +1,62 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__Spider_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__Spider_H__
//package net.minecraft.world.entity->monster;
#include "Monster.h"
#include <string>
class Level;
class Entity;
class Spider: public Monster
{
typedef Monster super;
typedef SynchedEntityData::TypeChar DataFlagIdType;
static const int DATA_FLAGS_ID = 16;
public:
Spider(Level* level);
/*@Override*/
void aiStep();
/*@Override*/
void tick();
/*@Override*/
int getMaxHealth();
/**
* The the spiders act as if they're always on a ladder, which enables them
* to climb walls.
*/
/*@Override*/ //@todo
bool onLadder();
/*@Override*/
void makeStuckInWeb();
float getModelScale();
bool isClimbing();
void setClimbing(bool value);
virtual int getEntityTypeId() const;
protected:
/*@Override*/
bool makeStepSound();
Entity* findAttackTarget();
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
void checkHurtTarget(Entity* target, float d);
int fireCheckTick;
int getDeathLoot();
/*@Override*/
//void dropDeathLoot();
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__Spider_H__*/

View File

@@ -0,0 +1,129 @@
#include "Zombie.h"
#include "../../item/Item.h"
#include "../../level/Level.h"
#include "../../../util/Mth.h"
//#include "../MobType.h"
#include "../ai/goal/GoalSelector.h"
#include "../ai/control/JumpControl.h"
#include "../ai/goal/RandomStrollGoal.h"
#include "../ai/goal/MeleeAttackGoal.h"
#include "../ai/goal/target/NearestAttackableTargetGoal.h"
#include "../ai/goal/target/HurtByTargetGoal.h"
#include "../ai/goal/BreakDoorGoal.h"
Zombie::Zombie( Level* level )
: super(level),
fireCheckTick(0),
_useNewAi(false)
{
entityRendererId = ER_ZOMBIE_RENDERER;
this->textureName = "mob/zombie.png";
//pathfinderMask |= CAN_OPEN_DOORS;
//navigation->canOpenDoors = true;
runSpeed = 0.5f;
attackDamage = 4;
targetSelector = new GoalSelector();
targetSelector->addGoal(1, new HurtByTargetGoal(this, false));
targetSelector->addGoal(2, new NearestAttackableTargetGoal(this, 1, 16, 0, true));
goalSelector = new GoalSelector();
//goalSelector->addGoal(1, new BreakDoorGoal(this));
goalSelector->addGoal(2, new MeleeAttackGoal(this, runSpeed, false, 0));
goalSelector->addGoal(7, new RandomStrollGoal(this, runSpeed) );
moveControl = new MoveControl(this);
jumpControl = new JumpControl(this);
}
Zombie::~Zombie() {
delete goalSelector;
delete targetSelector;
delete moveControl;
delete jumpControl;
}
int Zombie::getMaxHealth() {
return 12; // 16
}
void Zombie::aiStep() {
if ((++fireCheckTick & 1) && level->isDay() && !level->isClientSide) {
float br = getBrightness(1);
if (br > 0.5f) {
if (level->canSeeSky(Mth::floor(x), Mth::floor(y), Mth::floor(z)) && random.nextFloat() * 3.5f < (br - 0.4f)) {
hurt(NULL, 1);
for (int i = 0; i < 5; ++i) {
float xa = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
float ya = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
float za = (2.0f * random.nextFloat() - 1.0f) * (2.0f * random.nextFloat() - 1.0f) * 0.02f;
level->addParticle(PARTICLETYPE(explode), x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za);
}
//setOnFire(8); //@todo
}
}
}
super::aiStep();
}
int Zombie::getEntityTypeId() const {
return MobTypes::Zombie;
}
void Zombie::setUseNewAi( bool use ) {
_useNewAi = use;
}
int Zombie::getArmorValue() {
int armor = super::getArmorValue() + 2;
if (armor > 20) armor = 20;
return armor;
}
const char* Zombie::getAmbientSound() {
return "mob.zombie";
}
std::string Zombie::getHurtSound() {
return "mob.zombiehurt";
}
std::string Zombie::getDeathSound() {
return "mob.zombiedeath";
}
int Zombie::getDeathLoot() {
return 0; //@todo
//return Item::rotten_flesh->id;
}
bool Zombie::useNewAi() {
return _useNewAi;
}
void Zombie::die( Entity* source ) {
super::die(source);
if(!level->isClientSide) {
if(random.nextInt(4) == 0) {
spawnAtLocation(Item::feather->id, random.nextInt(1) + 1);
}
}
}
int Zombie::getAttackDamage( Entity* target ) {
ItemInstance* weapon = getCarriedItem();
int damage = attackDamage;
if(weapon != NULL) damage += weapon->getAttackDamage(this);
return damage;
}
/*@Override*/ //@todo?
//MobType getMobType() {
// return MobType::UNDEAD;
//}

View File

@@ -0,0 +1,44 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_MONSTER__Zombie_H__
#define NET_MINECRAFT_WORLD_ENTITY_MONSTER__Zombie_H__
//package net.minecraft.world.entity->monster;
#include "Monster.h"
#include <string>
class Level;
class Zombie: public Monster
{
typedef Monster super;
public:
Zombie(Level* level);
~Zombie();
/*@Override*/
int getMaxHealth();
void aiStep();
virtual int getEntityTypeId() const;
void setUseNewAi(bool use);
virtual void die(Entity* source);
virtual int getAttackDamage(Entity* target);
protected:
/*@Override*/
int getArmorValue();
const char* getAmbientSound();
std::string getHurtSound();
std::string getDeathSound();
//@todo
int getDeathLoot();
virtual bool useNewAi();
int fireCheckTick;
bool _useNewAi;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_MONSTER__Zombie_H__*/

View 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__*/

View 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;
}

View 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__*/

View 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__*/

View 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__*/

View 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
View 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__*/

View File

@@ -0,0 +1,346 @@
#include "Arrow.h"
#include "../Mob.h"
#include "../player/Player.h"
#include "../../item/Item.h"
#include "../../item/ItemInstance.h"
#include "../../level/Level.h"
#include "../../level/tile/Tile.h"
#include "../../../util/Mth.h"
#include "../../../nbt/CompoundTag.h"
const float Arrow::ARROW_BASE_DAMAGE = 2.0f;
Arrow::Arrow( Level* level )
: super(level),
playerArrow(false),
ownerId(0)
{
_init();
}
Arrow::Arrow( Level* level, float x, float y, float z )
: super(level),
playerArrow(false),
ownerId(0)
{
_init();
this->setPos(x, y, z);
this->heightOffset = 0;
}
Arrow::Arrow( Level* level, Mob* mob, float power )
: super(level),
ownerId(mob->entityId),
playerArrow(mob->isPlayer())
{
_init();
moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot);
const float dx = Mth::cos(yRot * Mth::DEGRAD);
const float dz = Mth::sin(yRot * Mth::DEGRAD);
x -= dx * 0.16f;
y -= 0.1f;
z -= dz * 0.16f;
this->setPos(x, y, z);
this->heightOffset = 0;
const float dxx = Mth::cos(xRot / 180 * Mth::PI);
xd = -dz * dxx;
zd = dx * dxx;
yd = -Mth::sin(xRot / 180 * Mth::PI);
shoot(xd, yd, zd, power * 1.5f, 1);
}
void Arrow::_init()
{
entityRendererId = ER_ARROW_RENDERER;
setSize(0.5f, 0.5f);
shakeTime = 0;
critArrow = false;
xTile = -1;
yTile = -1;
zTile = -1;
lastTile = 0;
lastData = 0;
inGround = false;
life = 0;
flightTime = 0;
}
void Arrow::shoot( float xd, float yd, float zd, float pow, float uncertainty )
{
float dist = Mth::sqrt(xd * xd + yd * yd + zd * zd);
xd /= dist;
yd /= dist;
zd /= dist;
const float radius = 0.0075f * uncertainty;
xd += sharedRandom.nextGaussian() * radius;
yd += sharedRandom.nextGaussian() * radius;
zd += sharedRandom.nextGaussian() * radius;
xd *= pow;
yd *= pow;
zd *= pow;
this->xd = xd;
this->yd = yd;
this->zd = zd;
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
life = 0;
}
void Arrow::lerpMotion( float xd, float yd, float zd )
{
this->xd = xd;
this->yd = yd;
this->zd = zd;
if (xRotO == 0 && yRotO == 0) {
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
moveTo(x, y, z, yRot, xRot);
life = 0;
}
}
void Arrow::tick()
{
super::tick();
if (xRotO == 0 && yRotO == 0) {
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (std::atan2(xd, zd) * Mth::RADDEG);
xRotO = this->xRot = (float) (std::atan2(yd, sd) * Mth::RADDEG);
}
{
int t = level->getTile(xTile, yTile, zTile);
if (t > 0) {
Tile::tiles[t]->updateShape(level, xTile, yTile, zTile);
AABB* aabb = Tile::tiles[t]->getAABB(level, xTile, yTile, zTile);
if (aabb != NULL && aabb->contains(Vec3(x, y, z))) {
inGround = true;
}
}
}
if (shakeTime > 0) shakeTime--;
if (inGround) {
// xd = 0;
// yd = 0;
// zd = 0;
int tile = level->getTile(xTile, yTile, zTile);
int data = level->getData(xTile, yTile, zTile);
if (tile != lastTile || data != lastData) {
inGround = false;
xd *= sharedRandom.nextFloat() * 0.2f;
yd *= sharedRandom.nextFloat() * 0.2f;
zd *= sharedRandom.nextFloat() * 0.2f;
life = 0;
flightTime = 0;
return;
} else {
if (++life == 60 * SharedConstants::TicksPerSecond) remove();
return;
}
} else {
flightTime++;
}
Vec3 from(x, y, z);
Vec3 to(x + xd, y + yd, z + zd);
HitResult res = level->clip(from, to, false, true);
from.set(x, y, z);
to.set(x + xd, y + yd, z + zd);
if (res.isHit()) {
to.set(res.pos.x, res.pos.y, res.pos.z);
}
Entity* hitEntity = NULL;
EntityList& objects = level->getEntities(this, this->bb.expand(xd, yd, zd).grow(1, 1, 1));
float nearest = 0;
for (unsigned int i = 0; i < objects.size(); i++) {
Entity* e = objects[i];
if (!e->isPickable() || (e->entityId == ownerId && flightTime < 5)) continue;
float rr = 0.3f;
AABB bb = e->bb.grow(rr, rr, rr);
HitResult p = bb.clip(from, to);
if (p.isHit()) {
float dd = from.distanceTo(p.pos);
if (dd < nearest || nearest == 0) {
hitEntity = e;
nearest = dd;
}
}
}
if (hitEntity != NULL) {
res = HitResult(hitEntity);
}
if (res.isHit()) {
if (res.type == ENTITY) {
float pow = Mth::sqrt(xd * xd + yd * yd + zd * zd);
int dmg = (int) std::ceil(pow * ARROW_BASE_DAMAGE);
if (critArrow) dmg += sharedRandom.nextInt(dmg / 2 + 2);
//@todo
//DamageSource damageSource = NULL;
//if (owner == NULL) {
// damageSource = DamageSource.arrow(this, this);
//} else {
// damageSource = DamageSource.arrow(this, owner);
//}
//if (res.entity->hurt(damageSource, dmg)) {
// if (res.entity instanceof Mob) {
if (res.entity->hurt(this, dmg)) {
if (res.entity->isMob()) {
((Mob*) res.entity)->arrowCount++;
}
level->playSound(this, "random.bowhit", 1.0f, 1.2f / (sharedRandom.nextFloat() * 0.2f + 0.9f));
remove();
} else {
xd *= -0.1f;
yd *= -0.1f;
zd *= -0.1f;
yRot += 180;
yRotO += 180;
flightTime = 0;
}
} else {
xTile = res.x;
yTile = res.y;
zTile = res.z;
lastTile = level->getTile(xTile, yTile, zTile);
lastData = level->getData(xTile, yTile, zTile);
xd = (float) (res.pos.x - x);
yd = (float) (res.pos.y - y);
zd = (float) (res.pos.z - z);
float dd = Mth::sqrt(xd * xd + yd * yd + zd * zd);
x -= (xd / dd) * 0.05f;
y -= (yd / dd) * 0.05f;
z -= (zd / dd) * 0.05f;
level->playSound(this, "random.bowhit", 1.0f, 1.2f / (sharedRandom.nextFloat() * 0.2f + 0.9f));
inGround = true;
shakeTime = 7;
critArrow = false;
}
}
if (critArrow) {
for (int i = 0; i < 4; i++) {
level->addParticle(PARTICLETYPE(crit), x + xd * i / 4.0f, y + yd * i / 4.0f, z + zd * i / 4.0f, -xd, -yd + 0.2f, -zd);
}
}
x += xd;
y += yd;
z += zd;
float sd = Mth::sqrt(xd * xd + zd * zd);
yRot = std::atan2(xd, zd) * Mth::RADDEG;
xRot = std::atan2(yd, sd) * Mth::RADDEG;
while (xRot - xRotO < -180)
xRotO -= 360;
while (xRot - xRotO >= 180)
xRotO += 360;
while (yRot - yRotO < -180)
yRotO -= 360;
while (yRot - yRotO >= 180)
yRotO += 360;
xRot = xRotO + (xRot - xRotO) * 0.2f;
yRot = yRotO + (yRot - yRotO) * 0.2f;
float inertia = 0.99f;
float gravity = 0.05f;
if (isInWater()) {
for (int i = 0; i < 4; i++) {
float s = 1 / 4.0f;
level->addParticle(PARTICLETYPE(bubble), x - xd * s, y - yd * s, z - zd * s, xd, yd, zd);
}
inertia = 0.80f;
}
xd *= inertia;
yd *= inertia;
zd *= inertia;
yd -= gravity;
setPos(x, y, z);
}
void Arrow::addAdditonalSaveData( CompoundTag* tag )
{
tag->putShort("xTile", (short) xTile);
tag->putShort("yTile", (short) yTile);
tag->putShort("zTile", (short) zTile);
tag->putByte("inTile", (char) lastTile);
tag->putByte("inData", (char) lastData);
tag->putByte("shake", (char) shakeTime);
tag->putByte("inGround", (char) (inGround ? 1 : 0));
tag->putBoolean("player", playerArrow);
}
void Arrow::readAdditionalSaveData( CompoundTag* tag )
{
xTile = tag->getShort("xTile");
yTile = tag->getShort("yTile");
zTile = tag->getShort("zTile");
lastTile = tag->getByte("inTile") & 0xff;
lastData = tag->getByte("inData") & 0xff;
shakeTime = tag->getByte("shake") & 0xff;
inGround = tag->getByte("inGround") == 1;
playerArrow = tag->getBoolean("player");
}
void Arrow::playerTouch( Player* player )
{
if (level->isClientSide) return;
if (inGround && playerArrow && shakeTime <= 0) {
ItemInstance item(Item::arrow, 1);
if (player->inventory->add(&item)) {
level->playSound(this, "random.pop", 0.2f, ((sharedRandom.nextFloat() - sharedRandom.nextFloat()) * 0.7f + 1.0f) * 2.f);
player->take(this, 1);
remove();
}
}
}
float Arrow::getShadowHeightOffs() {
return 0;
}
int Arrow::getEntityTypeId() const {
return EntityTypes::IdArrow;
}
int Arrow::getAuxData() {
return ownerId;
}

View File

@@ -0,0 +1,57 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Arrow_H__
#define NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Arrow_H__
//package net.minecraft.world.entity.projectile;
#include "../Entity.h"
class Level;
class Mob;
class CompoundTag;
class Arrow: public Entity
{
typedef Entity super;
// base damage, multiplied with velocity
static const float ARROW_BASE_DAMAGE;
public:
Arrow(Level* level);
Arrow(Level* level, float x, float y, float z);
Arrow(Level* level, Mob* mob, float power);
void _init();
void shoot(float xd, float yd, float zd, float pow, float uncertainty);
void lerpMotion(float xd, float yd, float zd);
void tick();
int getEntityTypeId() const;
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
void playerTouch(Player* player);
float getShadowHeightOffs();
int getAuxData();
public:
bool playerArrow;
int shakeTime;
int ownerId;
bool critArrow;
private:
int xTile;
int yTile;
int zTile;
int lastTile;
int lastData;
bool inGround;
int life;
int flightTime;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Arrow_H__*/

View File

@@ -0,0 +1,50 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Snowball_H__
#define NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Snowball_H__
//package net.minecraft.world.entity->projectile;
#include "Throwable.h"
#include "../Mob.h"
#include "../../level/Level.h"
#include "../../phys/HitResult.h"
class Snowball: public Throwable
{
typedef Throwable super;
public:
Snowball(Level* level)
: super(level)
{
entityRendererId = ER_SNOWBALL_RENDERER;
}
Snowball(Level* level, Mob* mob)
: super(level, mob)
{
entityRendererId = ER_SNOWBALL_RENDERER;
}
Snowball(Level* level, float x, float y, float z)
: super(level, x, y, z)
{
entityRendererId = ER_SNOWBALL_RENDERER;
}
virtual int getEntityTypeId() const {
return EntityTypes::IdSnowball;
}
/*@Override*/
void onHit(const HitResult& res) {
if (res.type == ENTITY)
res.entity->hurt(this, 0);
for (int i = 0; i < 6; i++)
level->addParticle(PARTICLETYPE(snowballpoof), x, y, z, 0, 0, 0);
if (!level->isClientSide)
remove();
}
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Snowball_H__*/

View File

@@ -0,0 +1,260 @@
#include "Throwable.h"
#include "../player/Player.h"
#include "../../level/Level.h"
#include "../../../util/Mth.h"
#include "../../phys/HitResult.h"
Throwable::Throwable( Level* level )
: super(level)
{
_init();
setSize(0.25f, 0.25f);
}
Throwable::Throwable( Level* level, Mob* mob )
: super(level)
{
_init();
setSize(0.25f, 0.25f);
ownerId = mob->entityId;
this->moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot);
const float rcos = Mth::cos(yRot / 180 * Mth::PI);
const float rsin = Mth::sin(yRot / 180 * Mth::PI);
x -= rcos * 0.16f;
y -= 0.1f;
z -= rsin * 0.16f;
this->setPos(x, y, z);
this->heightOffset = 0;
if (mob->isPlayer()) {
shoot(mob->aimDirection, getThrowPower(), 1);
} else {
float xd = (-rsin * Mth::cos(xRot / 180 * Mth::PI));
float zd = ( rcos * Mth::cos(xRot / 180 * Mth::PI));
float yd = (-Mth::sin((xRot + getThrowUpAngleOffset()) / 180 * Mth::PI));
shoot(xd, yd, zd, getThrowPower(), 1);
}
}
Throwable::Throwable( Level* level, float x, float y, float z )
: super(level)
{
_init();
setSize(0.25f, 0.25f);
this->setPos(x, y, z);
this->heightOffset = 0;
}
void Throwable::_init() {
ownerId = 0;
shakeTime = 0;
inGround = false;
life = 0;
flightTime = 0;
xTile = -1;
yTile = -1;
zTile = -1;
lastTile = 0;
}
bool Throwable::shouldRenderAtSqrDistance( float distance ) {
float size = bb.getSize() * 4;
size *= 64.0f;
return distance < size * size;
}
float Throwable::getThrowPower() {
return 1.5f;
}
float Throwable::getThrowUpAngleOffset() {
return 0;
}
float Throwable::getGravity() {
return 0.03f;
}
void Throwable::shoot(const Vec3& v, float pow, float uncertainty) {
shoot(v.x, v.y, v.z, pow, uncertainty);
}
void Throwable::shoot( float xd, float yd, float zd, float pow, float uncertainty ) {
float dist = Mth::sqrt(xd * xd + yd * yd + zd * zd);
if (dist >= 0.001f) {
xd /= dist;
yd /= dist;
zd /= dist;
} else {
xd = yd = zd = 0;
}
xd += (sharedRandom.nextGaussian()) * 0.0075f * uncertainty;
yd += (sharedRandom.nextGaussian()) * 0.0075f * uncertainty;
zd += (sharedRandom.nextGaussian()) * 0.0075f * uncertainty;
xd *= pow;
yd *= pow;
zd *= pow;
this->xd = xd;
this->yd = yd;
this->zd = zd;
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (Mth::atan2(xd, zd) * 180 / Mth::PI);
xRotO = this->xRot = (float) (Mth::atan2(yd, sd) * 180 / Mth::PI);
life = 0;
}
void Throwable::lerpMotion( float xd, float yd, float zd ) {
this->xd = xd;
this->yd = yd;
this->zd = zd;
if (xRotO == 0 && yRotO == 0) {
float sd = (float) Mth::sqrt(xd * xd + zd * zd);
yRotO = this->yRot = (float) (Mth::atan2(xd, zd) * 180 / Mth::PI);
xRotO = this->xRot = (float) (Mth::atan2(yd, sd) * 180 / Mth::PI);
}
}
void Throwable::tick() {
xOld = x;
yOld = y;
zOld = z;
super::tick();
if (shakeTime > 0) shakeTime--;
if (inGround) {
// xd = 0;
// yd = 0;
// zd = 0;
int tile = level->getTile(xTile, yTile, zTile);
if (tile != lastTile) {
inGround = false;
xd *= sharedRandom.nextFloat() * 0.2f;
yd *= sharedRandom.nextFloat() * 0.2f;
zd *= sharedRandom.nextFloat() * 0.2f;
life = 0;
flightTime = 0;
} else {
life++;
if (life == 20 * 60)
remove();
return;
}
} else {
flightTime++;
}
Vec3 from(x, y, z);
Vec3 to(x + xd, y + yd, z + zd);
HitResult res = level->clip(from, to);
from.set(x, y, z);
to.set(x + xd, y + yd, z + zd);
if (res.isHit())
to.set(res.pos.x, res.pos.y, res.pos.z);
if (!level->isClientSide) {
Entity* hitEntity = NULL;
EntityList& objects = level->getEntities(this, this->bb.expand(xd, yd, zd).grow(1, 1, 1));
float nearest = 0;
for (unsigned int i = 0; i < objects.size(); i++) {
Entity* e = objects[i];
if (!e->isPickable() || (e->entityId == ownerId && flightTime < 5)) continue;
float rr = 0.3f;
AABB bb = e->bb.grow(rr, rr, rr);
HitResult p = bb.clip(from, to);
if (p.isHit()) {
float dd = from.distanceTo(p.pos);
if (dd < nearest || nearest == 0) {
hitEntity = e;
nearest = dd;
}
}
}
if (hitEntity != NULL)
res = HitResult(hitEntity);
}
if (res.isHit())
onHit(res);
x += xd;
y += yd;
z += zd;
float sd = Mth::sqrt(xd * xd + zd * zd);
yRot = Mth::atan2(xd, zd) * 180 / Mth::PI;
xRot = Mth::atan2(yd, sd) * 180 / Mth::PI;
while (xRot - xRotO < -180)
xRotO -= 360;
while (xRot - xRotO >= 180)
xRotO += 360;
while (yRot - yRotO < -180)
yRotO -= 360;
while (yRot - yRotO >= 180)
yRotO += 360;
xRot = xRotO + (xRot - xRotO) * 0.2f;
yRot = yRotO + (yRot - yRotO) * 0.2f;
float inertia = 0.99f;
float gravity = getGravity();
if (isInWater()) {
for (int i = 0; i < 4; i++) {
float s = 1 / 4.0f;
level->addParticle("bubble", x - xd * s, y - yd * s, z - zd * s, xd, yd, zd);
}
inertia = 0.80f;
}
xd *= inertia;
yd *= inertia;
zd *= inertia;
yd -= gravity;
setPos(x, y, z);
}
float Throwable::getShadowHeightOffs() {
return 0;
}
void Throwable::addAdditonalSaveData( CompoundTag* tag ) {
tag->putShort("xTile", (short) xTile);
tag->putShort("yTile", (short) yTile);
tag->putShort("zTile", (short) zTile);
tag->putByte("inTile", (char) lastTile);
tag->putByte("shake", (char) shakeTime);
tag->putByte("inGround", (char) (inGround ? 1 : 0));
}
void Throwable::readAdditionalSaveData( CompoundTag* tag ) {
xTile = tag->getShort("xTile");
yTile = tag->getShort("yTile");
zTile = tag->getShort("zTile");
lastTile = tag->getByte("inTile") & 0xff;
shakeTime = tag->getByte("shake") & 0xff;
inGround = tag->getByte("inGround") == 1;
}
int Throwable::getAuxData() {
return ownerId;
}

View File

@@ -0,0 +1,63 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Throwable_H__
#define NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Throwable_H__
//package net.minecraft.world.entity->projectile;
class Level;
class Mob;
class Player;
class CompoundTag;
#include "../Entity.h"
class HitResult;
/*abstract*/
class Throwable: public Entity
{
typedef Entity super;
public:
Throwable(Level* level);
Throwable(Level* level, Mob* mob);
Throwable(Level* level, float x, float y, float z);
void shoot(const Vec3& v, float pow, float uncertainty);
void shoot(float xd, float yd, float zd, float pow, float uncertainty);
void tick();
void lerpMotion(float xd, float yd, float zd);
void addAdditonalSaveData(CompoundTag* tag);
void readAdditionalSaveData(CompoundTag* tag);
bool shouldRenderAtSqrDistance(float distance);
float getShadowHeightOffs();
int getAuxData();
protected:
virtual float getThrowPower();
virtual float getThrowUpAngleOffset();
virtual float getGravity();
virtual void onHit(const HitResult& res) = 0;
private:
void _init();
public:
int shakeTime;
protected:
bool inGround;
int ownerId;
private:
int life;
int flightTime;
int xTile;
int yTile;
int zTile;
int lastTile;
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__Throwable_H__*/

View File

@@ -0,0 +1,63 @@
#ifndef NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__ThrownEgg_H__
#define NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__ThrownEgg_H__
//package net.minecraft.world.entity->projectile;
#include "Throwable.h"
#include "../Mob.h"
#include "../animal/Chicken.h"
#include "../../level/Level.h"
#include "../../phys/HitResult.h"
class ThrownEgg: public Throwable
{
typedef Throwable super;
public:
ThrownEgg(Level* level)
: super(level)
{
entityRendererId = ER_THROWNEGG_RENDERER;
}
ThrownEgg(Level* level, Mob* mob)
: super(level, mob)
{
entityRendererId = ER_THROWNEGG_RENDERER;
}
ThrownEgg(Level* level, float x, float y, float z)
: super(level, x, y, z)
{
entityRendererId = ER_THROWNEGG_RENDERER;
}
virtual int getEntityTypeId() const {
return EntityTypes::IdThrownEgg;
}
/*@Override*/
protected:
void onHit(const HitResult& res) {
if (res.type == ENTITY)
res.entity->hurt(this, 0);
if (!level->isClientSide && sharedRandom.nextInt(8) == 0) {
int count = 1;
if (sharedRandom.nextInt(32) == 0) count = 4;
for (int i = 0; i < count; i++) {
Chicken* chicken = new Chicken(level);
//chicken.setAge(-20 * 60 * 20);
chicken->moveTo(x, y, z, yRot, 0);
level->addEntity(chicken);
}
}
for (int i = 0; i < 6; i++)
level->addParticle(PARTICLETYPE(snowballpoof), x, y, z, 0, 0, 0);
if (!level->isClientSide)
remove();
}
};
#endif /*NET_MINECRAFT_WORLD_ENTITY_PROJECTILE__ThrownEgg_H__*/

16
src/world/food/FoodConstants.h Executable file
View File

@@ -0,0 +1,16 @@
#ifndef NET_MINECRAFT_WORLD_FOOD__FoodConstants_H__
#define NET_MINECRAFT_WORLD_FOOD__FoodConstants_H__
//package net.minecraft.world.food;
class FoodConstants
{
public:
static const int MAX_FOOD = 20;
// number of game ticks to change health because of food
static const int HEALTH_TICK_COUNT = 80;
static const int HEALTH_TICK_COUNT_SIMPLE = 5;
static const int HEAL_LEVEL = 18;
};
#endif /*NET_MINECRAFT_WORLD_FOOD__FoodConstants_H__*/

Some files were not shown because too many files have changed in this diff Show More