4jcraft/Minecraft.World/Items/SpawnEggItem.cpp
2026-03-21 16:39:12 -05:00

348 lines
12 KiB
C++

#include "../Platform/stdafx.h"
#include "../../Minecraft.Client/Minecraft.h"
#include "../Headers/net.minecraft.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.entity.h"
#include "../Headers/net.minecraft.world.entity.npc.h"
#include "../Headers/net.minecraft.world.h"
#include "../Util/HitResult.h"
#include "SpawnEggItem.h"
#include "../Util/Difficulty.h"
SpawnEggItem::SpawnEggItem(int id) : Item(id) {
setMaxStackSize(16); // 4J-PB brought forward. It is 64 on PC, but we'll
// never be able to place that many
setStackedByData(true);
overlay = NULL;
}
std::wstring SpawnEggItem::getHoverName(
std::shared_ptr<ItemInstance> itemInstance) {
std::wstring elementName = getDescription();
int nameId = EntityIO::getNameId(itemInstance->getAuxValue());
if (nameId >= 0) {
elementName =
replaceAll(elementName, L"{*CREATURE*}", app.GetString(nameId));
// elementName += " " + I18n.get("entity." + encodeId + ".name");
} else {
elementName = replaceAll(elementName, L"{*CREATURE*}", L"");
}
return elementName;
}
int SpawnEggItem::getColor(std::shared_ptr<ItemInstance> item,
int spriteLayer) {
AUTO_VAR(it, EntityIO::idsSpawnableInCreative.find(item->getAuxValue()));
if (it != EntityIO::idsSpawnableInCreative.end()) {
EntityIO::SpawnableMobInfo* spawnableMobInfo = it->second;
if (spriteLayer == 0) {
return Minecraft::GetInstance()->getColourTable()->getColor(
spawnableMobInfo->eggColor1);
}
return Minecraft::GetInstance()->getColourTable()->getColor(
spawnableMobInfo->eggColor2);
}
return 0xffffff;
}
bool SpawnEggItem::hasMultipleSpriteLayers() { return true; }
Icon* SpawnEggItem::getLayerIcon(int auxValue, int spriteLayer) {
if (spriteLayer > 0) {
return overlay;
}
return Item::getLayerIcon(auxValue, spriteLayer);
}
// 4J-PB - added for dispenser
std::shared_ptr<Entity> SpawnEggItem::canSpawn(int iAuxVal, Level* level,
int* piResult) {
std::shared_ptr<Entity> newEntity = EntityIO::newById(iAuxVal, level);
if (newEntity != NULL) {
bool canSpawn = false;
switch (newEntity->GetType()) {
case eTYPE_CHICKEN:
if (level->canCreateMore(eTYPE_CHICKEN,
Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyChickens;
}
break;
case eTYPE_WOLF:
if (level->canCreateMore(eTYPE_WOLF, Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyWolves;
}
break;
case eTYPE_VILLAGER:
if (level->canCreateMore(eTYPE_VILLAGER,
Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyVillagers;
}
break;
case eTYPE_MUSHROOMCOW:
if (level->canCreateMore(eTYPE_MUSHROOMCOW,
Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyMooshrooms;
}
break;
case eTYPE_SQUID:
if (level->canCreateMore(eTYPE_SQUID, Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManySquid;
}
break;
case eTYPE_BAT:
if (level->canCreateMore(eTYPE_BAT, Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyBats;
}
break;
default:
if (eTYPE_FLAGSET(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK,
newEntity->GetType())) {
if (level->canCreateMore(newEntity->GetType(),
Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
// different message for each animal
*piResult = eSpawnResult_FailTooManyPigsCowsSheepCats;
}
}
// 4J: Use eTYPE_ENEMY instead of monster (slimes and ghasts
// aren't monsters)
else if (newEntity->instanceof(eTYPE_ENEMY)) {
// 4J-PB - check if the player is trying to spawn an enemy
// in peaceful mode
if (level->difficulty == Difficulty::PEACEFUL) {
*piResult = eSpawnResult_FailCantSpawnInPeaceful;
} else if (level->canCreateMore(newEntity->GetType(),
Level::eSpawnType_Egg)) {
canSpawn = true;
} else {
*piResult = eSpawnResult_FailTooManyMonsters;
}
}
#ifndef _CONTENT_PACKAGE
else if (app.DebugArtToolsOn()) {
canSpawn = true;
}
#endif
break;
}
if (canSpawn) {
return newEntity;
}
}
return nullptr;
}
bool SpawnEggItem::useOn(std::shared_ptr<ItemInstance> itemInstance,
std::shared_ptr<Player> player, Level* level, int x,
int y, int z, int face, float clickX, float clickY,
float clickZ, bool bTestUseOnOnly) {
if (level->isClientSide) {
return true;
}
int tile = level->getTile(x, y, z);
#ifndef _CONTENT_PACKAGE
if (app.DebugArtToolsOn() && tile == Tile::mobSpawner_Id) {
// 4J Stu - Force adding this as a tile update
level->removeTile(x, y, z);
level->setTileAndData(x, y, z, Tile::mobSpawner_Id, 0,
Tile::UPDATE_ALL);
std::shared_ptr<MobSpawnerTileEntity> mste =
std::dynamic_pointer_cast<MobSpawnerTileEntity>(
level->getTileEntity(x, y, z));
if (mste != NULL) {
mste->setEntityId(
EntityIO::getEncodeId(itemInstance->getAuxValue()));
return true;
}
}
#endif
x += Facing::STEP_X[face];
y += Facing::STEP_Y[face];
z += Facing::STEP_Z[face];
double yOff = 0;
if (face == Facing::UP &&
(Tile::tiles[tile] != NULL &&
Tile::tiles[tile]->getRenderShape() == Tile::SHAPE_FENCE)) {
// special case
yOff = .5;
}
int iResult = 0;
std::shared_ptr<Entity> result = spawnMobAt(
level, itemInstance->getAuxValue(), x + .5, y + yOff, z + .5, &iResult);
if (bTestUseOnOnly) {
return result != NULL;
}
if (result != NULL) {
// 4J-JEV: SetCustomName is a method for Mob not LivingEntity; so change
// instanceof to check for Mobs.
if (result->instanceof(eTYPE_MOB) &&
itemInstance->hasCustomHoverName()) {
std::dynamic_pointer_cast<Mob>(result)->setCustomName(
itemInstance->getHoverName());
}
if (!player->abilities.instabuild) {
itemInstance->count--;
}
} else {
DisplaySpawnError(player, iResult);
}
return true;
}
std::shared_ptr<ItemInstance> SpawnEggItem::use(
std::shared_ptr<ItemInstance> itemInstance, Level* level,
std::shared_ptr<Player> player) {
if (level->isClientSide) return itemInstance;
HitResult* hr = getPlayerPOVHitResult(level, player, true);
if (hr == NULL) {
delete hr;
return itemInstance;
}
if (hr->type == HitResult::TILE) {
int xt = hr->x;
int yt = hr->y;
int zt = hr->z;
if (!level->mayInteract(player, xt, yt, zt, 0)) {
delete hr;
return itemInstance;
}
if (!player->mayUseItemAt(xt, yt, zt, hr->f, itemInstance))
return itemInstance;
if (level->getMaterial(xt, yt, zt) == Material::water) {
int iResult = 0;
std::shared_ptr<Entity> result = spawnMobAt(
level, itemInstance->getAuxValue(), xt, yt, zt, &iResult);
if (result != NULL) {
// 4J-JEV: SetCustomName is a method for Mob not LivingEntity;
// so change instanceof to check for Mobs.
if (result->instanceof(eTYPE_MOB) &&
itemInstance->hasCustomHoverName()) {
std::dynamic_pointer_cast<Mob>(result)->setCustomName(
itemInstance->getHoverName());
}
if (!player->abilities.instabuild) {
itemInstance->count--;
}
} else {
SpawnEggItem::DisplaySpawnError(player, iResult);
}
}
}
return itemInstance;
}
std::shared_ptr<Entity> SpawnEggItem::spawnMobAt(Level* level, int auxVal,
double x, double y, double z,
int* piResult) {
int mobId = auxVal;
int extraData = 0;
// 4J Stu - Enable spawning specific entity sub-types
mobId = auxVal & 0xFFF;
extraData = auxVal >> 12;
if (EntityIO::idsSpawnableInCreative.find(mobId) ==
EntityIO::idsSpawnableInCreative.end()) {
return nullptr;
}
std::shared_ptr<Entity> newEntity = nullptr;
for (int i = 0; i < SPAWN_COUNT; i++) {
newEntity = canSpawn(mobId, level, piResult);
// 4J-JEV: DynCasting to Mob not LivingEntity; so change instanceof to
// check for Mobs.
if (newEntity != NULL && newEntity->instanceof(eTYPE_MOB)) {
std::shared_ptr<Mob> mob =
std::dynamic_pointer_cast<Mob>(newEntity);
newEntity->moveTo(
x, y, z, Mth::wrapDegrees(level->random->nextFloat() * 360), 0);
newEntity->setDespawnProtected(); // 4J added, default to being
// protected against despawning
// (has to be done after initial
// position is set)
mob->yHeadRot = mob->yRot;
mob->yBodyRot = mob->yRot;
mob->finalizeMobSpawn(NULL, extraData);
level->addEntity(newEntity);
mob->playAmbientSound();
}
}
return newEntity;
}
void SpawnEggItem::registerIcons(IconRegister* iconRegister) {
Item::registerIcons(iconRegister);
overlay = iconRegister->registerIcon(getIconName() + L"_overlay");
}
void SpawnEggItem::DisplaySpawnError(std::shared_ptr<Player> player,
int result) {
// some negative sound effect?
// level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0);
switch (result) {
case eSpawnResult_FailTooManyPigsCowsSheepCats:
player->displayClientMessage(IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED);
break;
case eSpawnResult_FailTooManyChickens:
player->displayClientMessage(IDS_MAX_CHICKENS_SPAWNED);
break;
case eSpawnResult_FailTooManySquid:
player->displayClientMessage(IDS_MAX_SQUID_SPAWNED);
break;
case eSpawnResult_FailTooManyBats:
player->displayClientMessage(IDS_MAX_BATS_SPAWNED);
break;
case eSpawnResult_FailTooManyWolves:
player->displayClientMessage(IDS_MAX_WOLVES_SPAWNED);
break;
case eSpawnResult_FailTooManyMooshrooms:
player->displayClientMessage(IDS_MAX_MOOSHROOMS_SPAWNED);
break;
case eSpawnResult_FailTooManyMonsters:
player->displayClientMessage(IDS_MAX_ENEMIES_SPAWNED);
break;
case eSpawnResult_FailTooManyVillagers:
player->displayClientMessage(IDS_MAX_VILLAGERS_SPAWNED);
break;
case eSpawnResult_FailCantSpawnInPeaceful:
player->displayClientMessage(IDS_CANT_SPAWN_IN_PEACEFUL);
break;
}
}