diff --git a/Minecraft.World/Core/AbstractProjectileDispenseBehavior.cpp b/Minecraft.World/Core/AbstractProjectileDispenseBehavior.cpp new file mode 100644 index 000000000..d81edbc7e --- /dev/null +++ b/Minecraft.World/Core/AbstractProjectileDispenseBehavior.cpp @@ -0,0 +1,45 @@ +#include "../Platform/stdafx.h" + +#include "AbstractProjectileDispenseBehavior.h" +#include "../Blocks/DispenserTile.h" +#include "../Entities/Projectile.h" +#include "../Level/Level.h" +#include "../Level/Events/LevelEvent.h" +#include "../Items/ItemInstance.h" + +std::shared_ptr AbstractProjectileDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + Level* world = source->getWorld(); + if (world->countInstanceOf(eTYPE_PROJECTILE, false) >= + Level::MAX_DISPENSABLE_PROJECTILES) { + return DefaultDispenseItemBehavior::execute(source, dispensed, outcome); + } + + Position* position = DispenserTile::getDispensePosition(source); + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + + std::shared_ptr projectile = getProjectile(world, position); + + delete position; + + projectile->shoot(facing->getStepX(), facing->getStepY() + .1f, + facing->getStepZ(), getPower(), getUncertainty()); + world->addEntity(std::dynamic_pointer_cast(projectile)); + + dispensed->remove(1); + return dispensed; +} + +void AbstractProjectileDispenseBehavior::playSound(BlockSource* source, + eOUTCOME outcome) { + if (outcome != LEFT_ITEM) { + source->getWorld()->levelEvent(LevelEvent::SOUND_LAUNCH, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } +} + +float AbstractProjectileDispenseBehavior::getUncertainty() { return 6.0f; } + +float AbstractProjectileDispenseBehavior::getPower() { return 1.1f; } diff --git a/Minecraft.World/Core/AbstractProjectileDispenseBehavior.h b/Minecraft.World/Core/AbstractProjectileDispenseBehavior.h new file mode 100644 index 000000000..d8f72e940 --- /dev/null +++ b/Minecraft.World/Core/AbstractProjectileDispenseBehavior.h @@ -0,0 +1,19 @@ +#pragma once +#include "DefaultDispenseItemBehavior.h" + +class Projectile; +class Position; + +class AbstractProjectileDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); + virtual float getUncertainty(); + virtual float getPower(); + virtual std::shared_ptr getProjectile(Level* world, + Position* position) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/Core/Behavior.h b/Minecraft.World/Core/Behavior.h new file mode 100644 index 000000000..d27ebf951 --- /dev/null +++ b/Minecraft.World/Core/Behavior.h @@ -0,0 +1,3 @@ +#pragma once + +class Behavior {}; \ No newline at end of file diff --git a/Minecraft.World/Core/BehaviorRegistry.cpp b/Minecraft.World/Core/BehaviorRegistry.cpp new file mode 100644 index 000000000..1666a7053 --- /dev/null +++ b/Minecraft.World/Core/BehaviorRegistry.cpp @@ -0,0 +1,25 @@ +#include "../Platform/stdafx.h" + +#include "BehaviorRegistry.h" + +BehaviorRegistry::BehaviorRegistry(DispenseItemBehavior* defaultValue) { + defaultBehavior = defaultValue; +} + +BehaviorRegistry::~BehaviorRegistry() { + for (AUTO_VAR(it, storage.begin()); it != storage.end(); ++it) { + delete it->second; + } + + delete defaultBehavior; +} + +DispenseItemBehavior* BehaviorRegistry::get(Item* key) { + AUTO_VAR(it, storage.find(key)); + + return (it == storage.end()) ? defaultBehavior : it->second; +} + +void BehaviorRegistry::add(Item* key, DispenseItemBehavior* value) { + storage.insert(std::make_pair(key, value)); +} \ No newline at end of file diff --git a/Minecraft.World/Core/BehaviorRegistry.h b/Minecraft.World/Core/BehaviorRegistry.h new file mode 100644 index 000000000..f49d924f1 --- /dev/null +++ b/Minecraft.World/Core/BehaviorRegistry.h @@ -0,0 +1,16 @@ +#pragma once + +class DispenseItemBehavior; + +class BehaviorRegistry { +private: + std::unordered_map storage; + DispenseItemBehavior* defaultBehavior; + +public: + BehaviorRegistry(DispenseItemBehavior* defaultValue); + ~BehaviorRegistry(); + + DispenseItemBehavior* get(Item* key); + void add(Item* key, DispenseItemBehavior* value); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/BlockSource.h b/Minecraft.World/Core/BlockSource.h new file mode 100644 index 000000000..c29279ad9 --- /dev/null +++ b/Minecraft.World/Core/BlockSource.h @@ -0,0 +1,35 @@ +#pragma once + +#include "LocatableSource.h" + +class Tile; +class Material; +class TileEntity; + +class BlockSource : public LocatableSource { +public: + /** + * @return The X coordinate for the middle of the block + */ + virtual double getX() = 0; + + /** + * @return The Y coordinate for the middle of the block + */ + virtual double getY() = 0; + + /** + * @return The Z coordinate for the middle of the block + */ + virtual double getZ() = 0; + + virtual int getBlockX() = 0; + virtual int getBlockY() = 0; + virtual int getBlockZ() = 0; + + virtual Tile* getType() = 0; + virtual int getData() = 0; + virtual Material* getMaterial() = 0; + + virtual std::shared_ptr getEntity() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/Core/BlockSourceImpl.cpp b/Minecraft.World/Core/BlockSourceImpl.cpp new file mode 100644 index 000000000..65ff22f95 --- /dev/null +++ b/Minecraft.World/Core/BlockSourceImpl.cpp @@ -0,0 +1,38 @@ +#include "../Platform/stdafx.h" +#include "BlockSourceImpl.h" +#include "../Headers/net.minecraft.world.level.h" +#include "../Headers/net.minecraft.world.level.tile.h" +#include "../Headers/net.minecraft.world.level.tile.entity.h" + +BlockSourceImpl::BlockSourceImpl(Level* world, int x, int y, int z) { + this->world = world; + this->x = x; + this->y = y; + this->z = z; +} + +Level* BlockSourceImpl::getWorld() { return world; } + +double BlockSourceImpl::getX() { return x + 0.5; } + +double BlockSourceImpl::getY() { return y + 0.5; } + +double BlockSourceImpl::getZ() { return z + 0.5; } + +int BlockSourceImpl::getBlockX() { return x; } + +int BlockSourceImpl::getBlockY() { return y; } + +int BlockSourceImpl::getBlockZ() { return z; } + +Tile* BlockSourceImpl::getType() { + return Tile::tiles[world->getTile(x, y, z)]; +} + +int BlockSourceImpl::getData() { return world->getData(x, y, z); } + +Material* BlockSourceImpl::getMaterial() { return world->getMaterial(x, y, z); } + +std::shared_ptr BlockSourceImpl::getEntity() { + return world->getTileEntity(x, y, z); +} \ No newline at end of file diff --git a/Minecraft.World/Core/BlockSourceImpl.h b/Minecraft.World/Core/BlockSourceImpl.h new file mode 100644 index 000000000..41e10141c --- /dev/null +++ b/Minecraft.World/Core/BlockSourceImpl.h @@ -0,0 +1,28 @@ +#pragma once + +#include "BlockSource.h" + +class Level; + +class BlockSourceImpl : public BlockSource { +private: + Level* world; + int x; + int y; + int z; + +public: + BlockSourceImpl(Level* world, int x, int y, int z); + + Level* getWorld(); + double getX(); + double getY(); + double getZ(); + int getBlockX(); + int getBlockY(); + int getBlockZ(); + Tile* getType(); + int getData(); + Material* getMaterial(); + std::shared_ptr getEntity(); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/DefaultDispenseItemBehavior.cpp b/Minecraft.World/Core/DefaultDispenseItemBehavior.cpp new file mode 100644 index 000000000..a01ce291e --- /dev/null +++ b/Minecraft.World/Core/DefaultDispenseItemBehavior.cpp @@ -0,0 +1,87 @@ +#include "../Platform/stdafx.h" +#include "../Headers/net.minecraft.h" +#include "../Headers/net.minecraft.core.h" +#include "../Headers/net.minecraft.world.level.tile.h" +#include "../Headers/net.minecraft.world.level.h" +#include "../Headers/net.minecraft.world.entity.item.h" +#include "DefaultDispenseItemBehavior.h" + +std::shared_ptr DefaultDispenseItemBehavior::dispense( + BlockSource* source, std::shared_ptr dispensed) { + eOUTCOME outcome = DISPENCED_ITEM; + std::shared_ptr result = execute(source, dispensed, outcome); + + playSound(source, outcome); + playAnimation(source, DispenserTile::getFacing(source->getData()), outcome); + + return result; +} + +std::shared_ptr DefaultDispenseItemBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Position* position = DispenserTile::getDispensePosition(source); + + std::shared_ptr itemInstance = dispensed->remove(1); + + spawnItem(source->getWorld(), itemInstance, 6, facing, position); + + delete position; + + outcome = DISPENCED_ITEM; + return dispensed; +} + +void DefaultDispenseItemBehavior::spawnItem(Level* world, + std::shared_ptr item, + int accuracy, FacingEnum* facing, + Position* position) { + double spawnX = position->getX(); + double spawnY = position->getY(); + double spawnZ = position->getZ(); + + std::shared_ptr itemEntity = std::shared_ptr( + new ItemEntity(world, spawnX, spawnY - 0.3, spawnZ, item)); + + double pow = world->random->nextDouble() * 0.1 + 0.2; + itemEntity->xd = facing->getStepX() * pow; + itemEntity->yd = .2f; + itemEntity->zd = facing->getStepZ() * pow; + + itemEntity->xd += world->random->nextGaussian() * 0.0075f * accuracy; + itemEntity->yd += world->random->nextGaussian() * 0.0075f * accuracy; + itemEntity->zd += world->random->nextGaussian() * 0.0075f * accuracy; + + world->addEntity(itemEntity); +} + +void DefaultDispenseItemBehavior::playSound(BlockSource* source, + eOUTCOME outcome) { + if (outcome != LEFT_ITEM) { + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } else { + // some negative sound effect? + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK_FAIL, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } +} + +void DefaultDispenseItemBehavior::playAnimation(BlockSource* source, + FacingEnum* facing, + eOUTCOME outcome) { + if (outcome != LEFT_ITEM) { + source->getWorld()->levelEvent(LevelEvent::PARTICLES_SHOOT, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), + getLevelEventDataFrom(facing)); + } else { + } +} + +int DefaultDispenseItemBehavior::getLevelEventDataFrom(FacingEnum* facing) { + return facing->getStepX() + 1 + (facing->getStepZ() + 1) * 3; +} \ No newline at end of file diff --git a/Minecraft.World/Core/DefaultDispenseItemBehavior.h b/Minecraft.World/Core/DefaultDispenseItemBehavior.h new file mode 100644 index 000000000..aaafd5607 --- /dev/null +++ b/Minecraft.World/Core/DefaultDispenseItemBehavior.h @@ -0,0 +1,44 @@ +#pragma once +#include "DispenseItemBehavior.h" + +class FacingEnum; +class Position; + +class DefaultDispenseItemBehavior : public DispenseItemBehavior { +protected: + enum eOUTCOME { + // Item has special behaviour that was executed successfully. + ACTIVATED_ITEM = 0, + + // Item was dispenced onto the ground as a pickup. + DISPENCED_ITEM = 1, + + // Execution failed, the item was left unaffected. + LEFT_ITEM = 2, + }; + +public: + DefaultDispenseItemBehavior() {}; + virtual ~DefaultDispenseItemBehavior() {}; + virtual std::shared_ptr dispense( + BlockSource* source, std::shared_ptr dispensed); + +protected: + // 4J-JEV: Added value used to play FAILED sound effect upon reaching spawn + // limits. + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +public: + static void spawnItem(Level* world, std::shared_ptr item, + int accuracy, FacingEnum* facing, Position* position); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); + virtual void playAnimation(BlockSource* source, FacingEnum* facing, + eOUTCOME outcome); + +private: + virtual int getLevelEventDataFrom(FacingEnum* facing); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/DispenseItemBehavior.cpp b/Minecraft.World/Core/DispenseItemBehavior.cpp new file mode 100644 index 000000000..a8946af17 --- /dev/null +++ b/Minecraft.World/Core/DispenseItemBehavior.cpp @@ -0,0 +1,11 @@ +#include "../Platform/stdafx.h" + +#include "DispenseItemBehavior.h" + +DispenseItemBehavior* DispenseItemBehavior::NOOP = + new NoOpDispenseItemBehavior(); + +std::shared_ptr NoOpDispenseItemBehavior::dispense( + BlockSource* source, std::shared_ptr dispensed) { + return dispensed; +} \ No newline at end of file diff --git a/Minecraft.World/Core/DispenseItemBehavior.h b/Minecraft.World/Core/DispenseItemBehavior.h new file mode 100644 index 000000000..0f33e3e65 --- /dev/null +++ b/Minecraft.World/Core/DispenseItemBehavior.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Behavior.h" + +class ItemInstance; +class BlockSource; + +class DispenseItemBehavior : public Behavior { +public: + /** + * The 'do nothing' behavior. + */ + static DispenseItemBehavior* NOOP; + + /** + * + * @param source The source of this call (the dispenser that calls it) + * @param dispensed The ItemInstance which is being dispensed + * @return The ItemInstance that should is 'left over' + */ + virtual std::shared_ptr dispense( + BlockSource* source, std::shared_ptr dispensed) = 0; +}; + +class NoOpDispenseItemBehavior : public DispenseItemBehavior { +public: + std::shared_ptr dispense( + BlockSource* source, std::shared_ptr dispensed); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/FacingEnum.cpp b/Minecraft.World/Core/FacingEnum.cpp new file mode 100644 index 000000000..0f8c58f4e --- /dev/null +++ b/Minecraft.World/Core/FacingEnum.cpp @@ -0,0 +1,34 @@ +#include "../Platform/stdafx.h" + +#include "FacingEnum.h" + +FacingEnum* FacingEnum::DOWN = new FacingEnum(0, 1, 0, -1, 0); +FacingEnum* FacingEnum::UP = new FacingEnum(1, 0, 0, 1, 0); +FacingEnum* FacingEnum::NORTH = new FacingEnum(2, 3, 0, 0, -1); +FacingEnum* FacingEnum::SOUTH = new FacingEnum(3, 2, 0, 0, 1); +FacingEnum* FacingEnum::EAST = new FacingEnum(4, 5, -1, 0, 0); +FacingEnum* FacingEnum::WEST = new FacingEnum(5, 4, 1, 0, 0); + +FacingEnum* FacingEnum::BY_DATA[6] = {FacingEnum::DOWN, FacingEnum::UP, + FacingEnum::NORTH, FacingEnum::SOUTH, + FacingEnum::EAST, FacingEnum::WEST}; + +FacingEnum::FacingEnum(int dataValue, int oppositeIndex, int stepX, int stepY, + int stepZ) + : dataValue(dataValue), + oppositeIndex(oppositeIndex), + stepX(stepX), + stepY(stepY), + stepZ(stepZ) {} + +int FacingEnum::getDataValue() { return dataValue; } + +FacingEnum* FacingEnum::getOpposite() { return BY_DATA[oppositeIndex]; } + +int FacingEnum::getStepX() { return stepX; } + +int FacingEnum::getStepY() { return stepY; } + +int FacingEnum::getStepZ() { return stepZ; } + +FacingEnum* FacingEnum::fromData(int data) { return BY_DATA[data % 6]; } \ No newline at end of file diff --git a/Minecraft.World/Core/FacingEnum.h b/Minecraft.World/Core/FacingEnum.h new file mode 100644 index 000000000..913171db6 --- /dev/null +++ b/Minecraft.World/Core/FacingEnum.h @@ -0,0 +1,31 @@ +#pragma once + +class FacingEnum { +public: + static FacingEnum* DOWN; + static FacingEnum* UP; + static FacingEnum* NORTH; + static FacingEnum* SOUTH; + static FacingEnum* EAST; + static FacingEnum* WEST; + +private: + const int dataValue; + const int oppositeIndex; + const int stepX; + const int stepY; + const int stepZ; + + static FacingEnum* BY_DATA[6]; + + FacingEnum(int dataValue, int oppositeIndex, int stepX, int stepY, + int stepZ); + +public: + int getDataValue(); + FacingEnum* getOpposite(); + int getStepX(); + int getStepY(); + int getStepZ(); + static FacingEnum* fromData(int data); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/ItemDispenseBehaviors.cpp b/Minecraft.World/Core/ItemDispenseBehaviors.cpp new file mode 100644 index 000000000..20d3cca88 --- /dev/null +++ b/Minecraft.World/Core/ItemDispenseBehaviors.cpp @@ -0,0 +1,440 @@ +#include "../Platform/stdafx.h" +#include "../Headers/net.minecraft.world.entity.item.h" +#include "../Headers/net.minecraft.world.entity.projectile.h" +#include "../Headers/net.minecraft.world.level.h" +#include "../Headers/net.minecraft.world.level.tile.h" +#include "../Headers/net.minecraft.world.level.tile.entity.h" +#include "../Headers/net.minecraft.world.item.h" +#include "ItemDispenseBehaviors.h" + +/* Arrow */ + +std::shared_ptr ArrowDispenseBehavior::getProjectile( + Level* world, Position* position) { + std::shared_ptr arrow = std::shared_ptr( + new Arrow(world, position->getX(), position->getY(), position->getZ())); + arrow->pickup = Arrow::PICKUP_ALLOWED; + + return arrow; +} + +/* ThrownEgg */ + +std::shared_ptr EggDispenseBehavior::getProjectile( + Level* world, Position* position) { + return std::shared_ptr(new ThrownEgg( + world, position->getX(), position->getY(), position->getZ())); +} + +/* Snowball */ + +std::shared_ptr SnowballDispenseBehavior::getProjectile( + Level* world, Position* position) { + return std::shared_ptr(new Snowball( + world, position->getX(), position->getY(), position->getZ())); +} + +/* Exp Bottle */ + +std::shared_ptr ExpBottleDispenseBehavior::getProjectile( + Level* world, Position* position) { + return std::shared_ptr(new ThrownExpBottle( + world, position->getX(), position->getY(), position->getZ())); +} + +float ExpBottleDispenseBehavior::getUncertainty() { + return AbstractProjectileDispenseBehavior::getUncertainty() * .5f; +} + +float ExpBottleDispenseBehavior::getPower() { + return AbstractProjectileDispenseBehavior::getPower() * 1.25f; +} + +/* Thrown Potion */ + +ThrownPotionDispenseBehavior::ThrownPotionDispenseBehavior(int potionValue) { + m_potionValue = potionValue; +} + +std::shared_ptr ThrownPotionDispenseBehavior::getProjectile( + Level* world, Position* position) { + return std::shared_ptr( + new ThrownPotion(world, position->getX(), position->getY(), + position->getZ(), m_potionValue)); +} + +float ThrownPotionDispenseBehavior::getUncertainty() { + return AbstractProjectileDispenseBehavior::getUncertainty() * .5f; +} + +float ThrownPotionDispenseBehavior::getPower() { + return AbstractProjectileDispenseBehavior::getPower() * 1.25f; +} + +/* Potion */ + +std::shared_ptr PotionDispenseBehavior::dispense( + BlockSource* source, std::shared_ptr dispensed) { + if (PotionItem::isThrowable(dispensed->getAuxValue())) { + return ThrownPotionDispenseBehavior(dispensed->getAuxValue()) + .dispense(source, dispensed); + } else { + return DefaultDispenseItemBehavior::dispense(source, dispensed); + } +} + +/* SpawnEggItem */ + +std::shared_ptr SpawnEggDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + + // Spawn entity in the middle of the block in front of the dispenser + double spawnX = source->getX() + facing->getStepX(); + double spawnY = source->getBlockY() + .2f; // Above pressure plates + double spawnZ = source->getZ() + facing->getStepZ(); + + int iResult = 0; + std::shared_ptr entity = + SpawnEggItem::spawnMobAt(source->getWorld(), dispensed->getAuxValue(), + spawnX, spawnY, spawnZ, &iResult); + + // 4J-JEV: Added in-case spawn limit is encountered. + if (entity == NULL) { + outcome = LEFT_ITEM; + return dispensed; + } + + if (entity->instanceof(eTYPE_MOB) && dispensed->hasCustomHoverName()) { + std::dynamic_pointer_cast(entity)->setCustomName( + dispensed->getHoverName()); + } + + outcome = ACTIVATED_ITEM; + + dispensed->remove(1); + return dispensed; +} + +/* Fireworks*/ + +std::shared_ptr FireworksDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + Level* world = source->getWorld(); + if (world->countInstanceOf(eTYPE_PROJECTILE, false) >= + Level::MAX_DISPENSABLE_PROJECTILES) { + outcome = LEFT_ITEM; + return dispensed; + } + + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + + double spawnX = source->getX() + facing->getStepX(); + double spawnY = source->getBlockY() + .2f; + double spawnZ = source->getZ() + facing->getStepZ(); + + std::shared_ptr firework = + std::shared_ptr(new FireworksRocketEntity( + world, spawnX, spawnY, spawnZ, dispensed)); + source->getWorld()->addEntity(firework); + + outcome = ACTIVATED_ITEM; + + dispensed->remove(1); + return dispensed; +} + +void FireworksDispenseBehavior::playSound(BlockSource* source, + eOUTCOME outcome) { + // 4J-JEV: This is exactly the same as the default at the moment. + // source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK, + // source->getBlockX(), source->getBlockY(), source->getBlockZ(), 0); + + DefaultDispenseItemBehavior::playSound(source, outcome); +} + +/* Fireballs */ + +std::shared_ptr FireballDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + Level* world = source->getWorld(); + if (world->countInstanceOf(eTYPE_SMALL_FIREBALL, true) >= + Level::MAX_DISPENSABLE_FIREBALLS) { + outcome = LEFT_ITEM; + return dispensed; + } + + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + + Position* position = DispenserTile::getDispensePosition(source); + double spawnX = position->getX() + facing->getStepX() * .3f; + double spawnY = position->getY() + facing->getStepX() * .3f; + double spawnZ = position->getZ() + facing->getStepZ() * .3f; + + delete position; + + Random* random = world->random; + + double dirX = random->nextGaussian() * .05 + facing->getStepX(); + double dirY = random->nextGaussian() * .05 + facing->getStepY(); + double dirZ = random->nextGaussian() * .05 + facing->getStepZ(); + + world->addEntity(std::shared_ptr( + new SmallFireball(world, spawnX, spawnY, spawnZ, dirX, dirY, dirZ))); + + outcome = ACTIVATED_ITEM; + + dispensed->remove(1); + return dispensed; +} + +void FireballDispenseBehavior::playSound(BlockSource* source, + eOUTCOME outcome) { + if (outcome == ACTIVATED_ITEM) { + source->getWorld()->levelEvent(LevelEvent::SOUND_BLAZE_FIREBALL, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } else { + DefaultDispenseItemBehavior::playSound(source, outcome); + } +} + +/* Boats */ + +BoatDispenseBehavior::BoatDispenseBehavior() : DefaultDispenseItemBehavior() { + defaultDispenseItemBehavior = new DefaultDispenseItemBehavior(); +} + +BoatDispenseBehavior::~BoatDispenseBehavior() { + delete defaultDispenseItemBehavior; +} + +std::shared_ptr BoatDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Level* world = source->getWorld(); + + // Spawn the boat 'just' outside the dispenser, it overlaps 2 'pixels' now. + double spawnX = source->getX() + facing->getStepX() * (1 + 2.0f / 16); + double spawnY = source->getY() + facing->getStepY() * (1 + 2.0f / 16); + double spawnZ = source->getZ() + facing->getStepZ() * (1 + 2.0f / 16); + + int frontX = source->getBlockX() + facing->getStepX(); + int frontY = source->getBlockY() + facing->getStepY(); + int frontZ = source->getBlockZ() + facing->getStepZ(); + Material* inFront = world->getMaterial(frontX, frontY, frontZ); + + double yOffset; + + // 4J: If we're at limit, just dispense item (instead of adding boat) + if (world->countInstanceOf(eTYPE_BOAT, true) >= Level::MAX_XBOX_BOATS) { + return defaultDispenseItemBehavior->dispense(source, dispensed); + } + + if (Material::water == inFront) { + yOffset = 1; + } else if (Material::air == inFront && + Material::water == + world->getMaterial(frontX, frontY - 1, frontZ)) { + yOffset = 0; + } else { + return defaultDispenseItemBehavior->dispense(source, dispensed); + } + + outcome = ACTIVATED_ITEM; + + std::shared_ptr boat = std::shared_ptr( + new Boat(world, spawnX, spawnY + yOffset, spawnZ)); + world->addEntity(boat); + + dispensed->remove(1); + return dispensed; +} + +void BoatDispenseBehavior::playSound(BlockSource* source, eOUTCOME outcome) { + // 4J-JEV: This is exactly the same as the default at the moment. + // source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK, + // source->getBlockX(), source->getBlockY(), source->getBlockZ(), 0); + DefaultDispenseItemBehavior::playSound(source, outcome); +} + +/* FilledBucket */ + +std::shared_ptr FilledBucketDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + BucketItem* bucket = (BucketItem*)dispensed->getItem(); + int sourceX = source->getBlockX(); + int sourceY = source->getBlockY(); + int sourceZ = source->getBlockZ(); + + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + if (bucket->emptyBucket(source->getWorld(), sourceX + facing->getStepX(), + sourceY + facing->getStepY(), + sourceZ + facing->getStepZ())) { + dispensed->id = Item::bucket_empty->id; + dispensed->count = 1; + + outcome = ACTIVATED_ITEM; + return dispensed; + } + + return DefaultDispenseItemBehavior::dispense(source, dispensed); +} + +/* EmptyBucket */ + +std::shared_ptr EmptyBucketDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Level* world = source->getWorld(); + + int targetX = source->getBlockX() + facing->getStepX(); + int targetY = source->getBlockY() + facing->getStepY(); + int targetZ = source->getBlockZ() + facing->getStepZ(); + + Material* material = world->getMaterial(targetX, targetY, targetZ); + int dataValue = world->getData(targetX, targetY, targetZ); + + Item* targetType; + if (Material::water == material && dataValue == 0) { + targetType = Item::bucket_water; + } else if (Material::lava == material && dataValue == 0) { + targetType = Item::bucket_lava; + } else { + return DefaultDispenseItemBehavior::execute(source, dispensed, outcome); + } + + world->removeTile(targetX, targetY, targetZ); + if (--dispensed->count == 0) { + dispensed->id = targetType->id; + dispensed->count = 1; + } else if (std::dynamic_pointer_cast( + source->getEntity()) + ->addItem(std::shared_ptr( + new ItemInstance(targetType))) < 0) { + DefaultDispenseItemBehavior::dispense( + source, + std::shared_ptr(new ItemInstance(targetType))); + } + + outcome = ACTIVATED_ITEM; + return dispensed; +} + +/* Flint and Steel */ + +std::shared_ptr FlintAndSteelDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + outcome = ACTIVATED_ITEM; + + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Level* world = source->getWorld(); + + int targetX = source->getBlockX() + facing->getStepX(); + int targetY = source->getBlockY() + facing->getStepY(); + int targetZ = source->getBlockZ() + facing->getStepZ(); + + if (world->isEmptyTile(targetX, targetY, targetZ)) { + world->setTileAndUpdate(targetX, targetY, targetZ, Tile::fire_Id); + + if (dispensed->hurt(1, world->random)) { + dispensed->count = 0; + } + } else if (world->getTile(targetX, targetY, targetZ) == Tile::tnt_Id) { + Tile::tnt->destroy(world, targetX, targetY, targetZ, 1); + world->removeTile(targetX, targetY, targetZ); + } else { + outcome = LEFT_ITEM; + } + + return dispensed; +} + +void FlintAndSteelDispenseBehavior::playSound(BlockSource* source, + eOUTCOME outcome) { + if (outcome == ACTIVATED_ITEM) { + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } else { + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK_FAIL, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } +} + +/* Dye */ + +std::shared_ptr DyeDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + if (dispensed->getAuxValue() == DyePowderItem::WHITE) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Level* world = source->getWorld(); + + int targetX = source->getBlockX() + facing->getStepX(); + int targetY = source->getBlockY() + facing->getStepY(); + int targetZ = source->getBlockZ() + facing->getStepZ(); + + if (DyePowderItem::growCrop(dispensed, world, targetX, targetY, targetZ, + false)) { + if (!world->isClientSide) + world->levelEvent(LevelEvent::PARTICLES_PLANT_GROWTH, targetX, + targetY, targetZ, 0); + outcome = ACTIVATED_ITEM; + } else { + outcome = LEFT_ITEM; + } + + return dispensed; + } else { + return DefaultDispenseItemBehavior::execute(source, dispensed, outcome); + } +} + +void DyeDispenseBehavior::playSound(BlockSource* source, eOUTCOME outcome) { + if (outcome == ACTIVATED_ITEM) { + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } else { + source->getWorld()->levelEvent(LevelEvent::SOUND_CLICK_FAIL, + source->getBlockX(), source->getBlockY(), + source->getBlockZ(), 0); + } +} + +/* TNT */ + +std::shared_ptr TntDispenseBehavior::execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome) { + FacingEnum* facing = DispenserTile::getFacing(source->getData()); + Level* world = source->getWorld(); + + if (world->newPrimedTntAllowed() && + app.GetGameHostOption(eGameHostOption_TNT)) { + int targetX = source->getBlockX() + facing->getStepX(); + int targetY = source->getBlockY() + facing->getStepY(); + int targetZ = source->getBlockZ() + facing->getStepZ(); + + std::shared_ptr tnt = std::shared_ptr( + new PrimedTnt(world, targetX + 0.5f, targetY + 0.5f, targetZ + 0.5f, + nullptr)); + world->addEntity(tnt); + + outcome = ACTIVATED_ITEM; + + dispensed->count--; + } else { + outcome = LEFT_ITEM; + } + return dispensed; +} \ No newline at end of file diff --git a/Minecraft.World/Core/ItemDispenseBehaviors.h b/Minecraft.World/Core/ItemDispenseBehaviors.h new file mode 100644 index 000000000..d0e8f4199 --- /dev/null +++ b/Minecraft.World/Core/ItemDispenseBehaviors.h @@ -0,0 +1,136 @@ +#pragma once +#include "DefaultDispenseItemBehavior.h" +#include "AbstractProjectileDispenseBehavior.h" + +class ArrowDispenseBehavior : public AbstractProjectileDispenseBehavior { +protected: + virtual std::shared_ptr getProjectile(Level* world, + Position* position); +}; + +class EggDispenseBehavior : public AbstractProjectileDispenseBehavior { +protected: + virtual std::shared_ptr getProjectile(Level* world, + Position* position); +}; + +class SnowballDispenseBehavior : public AbstractProjectileDispenseBehavior { +protected: + virtual std::shared_ptr getProjectile(Level* world, + Position* position); +}; + +class ExpBottleDispenseBehavior : public AbstractProjectileDispenseBehavior { +protected: + virtual std::shared_ptr getProjectile(Level* world, + Position* position); + virtual float getUncertainty(); + virtual float getPower(); +}; + +class ThrownPotionDispenseBehavior : public AbstractProjectileDispenseBehavior { +private: + int m_potionValue; + +public: + ThrownPotionDispenseBehavior(int potionValue); + +protected: + virtual std::shared_ptr getProjectile(Level* world, + Position* position); + virtual float getUncertainty(); + virtual float getPower(); +}; + +class PotionDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr dispense( + BlockSource* source, std::shared_ptr dispensed); +}; + +class SpawnEggDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); +}; + +class FireworksDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); +}; + +class FireballDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); +}; + +class BoatDispenseBehavior : public DefaultDispenseItemBehavior { +public: + BoatDispenseBehavior(); + virtual ~BoatDispenseBehavior(); + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); + +private: + DefaultDispenseItemBehavior* defaultDispenseItemBehavior; +}; + +class FilledBucketDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); +}; + +class EmptyBucketDispenseBehavior : public DefaultDispenseItemBehavior { +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); +}; + +class FlintAndSteelDispenseBehavior : public DefaultDispenseItemBehavior { + // bool success; // 4J-JEV: Removed because we have something cleaner for + // this now. +public: + std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); +}; + +class DyeDispenseBehavior : public DefaultDispenseItemBehavior { + // bool success; // 4J-JEV: Removed because we have something cleaner for + // this now. +public: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); + +protected: + virtual void playSound(BlockSource* source, eOUTCOME outcome); +}; + +class TntDispenseBehavior : public DefaultDispenseItemBehavior { +protected: + virtual std::shared_ptr execute( + BlockSource* source, std::shared_ptr dispensed, + eOUTCOME& outcome); +}; \ No newline at end of file diff --git a/Minecraft.World/Core/LocatableSource.h b/Minecraft.World/Core/LocatableSource.h new file mode 100644 index 000000000..ae717be6d --- /dev/null +++ b/Minecraft.World/Core/LocatableSource.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Source.h" +#include "Location.h" + +class LocatableSource : public Source, public Location {}; \ No newline at end of file diff --git a/Minecraft.World/Core/Location.h b/Minecraft.World/Core/Location.h new file mode 100644 index 000000000..82491742f --- /dev/null +++ b/Minecraft.World/Core/Location.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Position.h" + +class Level; + +class Location : public Position { +public: + virtual Level* getWorld() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/Core/Position.h b/Minecraft.World/Core/Position.h new file mode 100644 index 000000000..d38e99487 --- /dev/null +++ b/Minecraft.World/Core/Position.h @@ -0,0 +1,8 @@ +#pragma once + +class Position { +public: + virtual double getX() = 0; + virtual double getY() = 0; + virtual double getZ() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/Core/PositionImpl.h b/Minecraft.World/Core/PositionImpl.h new file mode 100644 index 000000000..0a3b84b31 --- /dev/null +++ b/Minecraft.World/Core/PositionImpl.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Position.h" + +class PositionImpl : public Position { +protected: + double x; + double y; + double z; + +public: + PositionImpl(double x, double y, double z) { + this->x = x; + this->y = y; + this->z = z; + } + + double getX() { return x; } + + double getY() { return y; } + + double getZ() { return z; } +}; \ No newline at end of file diff --git a/Minecraft.World/Core/Source.h b/Minecraft.World/Core/Source.h new file mode 100644 index 000000000..909c6041c --- /dev/null +++ b/Minecraft.World/Core/Source.h @@ -0,0 +1,3 @@ +#pragma once + +class Source {}; \ No newline at end of file