Create initial mob caps option

This commit is contained in:
Alexandra-Myers 2026-03-07 15:33:40 -05:00
parent 70458e410f
commit 069759196d
12 changed files with 168 additions and 35 deletions

View file

@ -45,9 +45,11 @@
#define GAME_HOST_OPTION_BITMASK_DOTILEDROPS 0x08000000
#define GAME_HOST_OPTION_BITMASK_NATURALREGEN 0x10000000
#define GAME_HOST_OPTION_BITMASK_DODAYLIGHTCYCLE 0x20000000
#define GAME_HOST_OPTION_BITMASK_WORLDMOBCAP 0xC0000000 // 2 bits, 4 values (small(0), medium(1), large(2), unlimited(3))
#define GAME_HOST_OPTION_BITMASK_ALL 0xFFFFFFFF
#define GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT 20
#define GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT 20
#define GAME_HOST_OPTION_BITMASK_WORLDMOBCAP_BITSHIFT 28 // Set to this value as there is no other remaining space
enum EGameHostOptionWorldSize
{

View file

@ -636,6 +636,7 @@ enum eGameHostOption
eGameHostOption_BedrockFog,
eGameHostOption_NoHUD,
eGameHostOption_WorldSize,
eGameHostOption_WorldMobCap,
eGameHostOption_All,
eGameHostOption_DisableSaving,

View file

@ -8067,10 +8067,15 @@ void CMinecraftApp::SetGameHostOption(unsigned int &uiHostSettings, eGameHostOpt
}
break;
case eGameHostOption_WorldSize:
// clear the difficulty first
// Clear the original value first
uiHostSettings&=~GAME_HOST_OPTION_BITMASK_WORLDSIZE;
uiHostSettings|=(GAME_HOST_OPTION_BITMASK_WORLDSIZE & (uiVal<<GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT));
break;
case eGameHostOption_WorldMobCap:
// Clear the original value first
uiHostSettings&=~GAME_HOST_OPTION_BITMASK_WORLDMOBCAP;
uiHostSettings|=(GAME_HOST_OPTION_BITMASK_WORLDMOBCAP & (uiVal<<GAME_HOST_OPTION_BITMASK_WORLDMOBCAP_BITSHIFT));
break;
case eGameHostOption_All:
uiHostSettings=uiVal;
break;
@ -8157,6 +8162,8 @@ unsigned int CMinecraftApp::GetGameHostOption(unsigned int uiHostSettings, eGame
return (uiHostSettings&GAME_HOST_OPTION_BITMASK_NOTOWNER);
case eGameHostOption_WorldSize:
return (uiHostSettings&GAME_HOST_OPTION_BITMASK_WORLDSIZE) >> GAME_HOST_OPTION_BITMASK_WORLDSIZE_BITSHIFT;
case eGameHostOption_WorldMobCap:
return (uiHostSettings&GAME_HOST_OPTION_BITMASK_WORLDMOBCAP) >> GAME_HOST_OPTION_BITMASK_WORLDMOBCAP_BITSHIFT;
case eGameHostOption_MobGriefing:
return !(uiHostSettings&GAME_HOST_OPTION_BITMASK_MOBGRIEFING);
case eGameHostOption_KeepInventory:

View file

@ -11,6 +11,7 @@
#include "..\..\..\Minecraft.World\BiomeSource.h"
#include "..\..\..\Minecraft.World\IntCache.h"
#include "..\..\..\Minecraft.World\LevelType.h"
#include "..\..\..\Minecraft.World\MobCategory.h"
#include "..\..\DLCTexturePack.h"
#ifdef __PSVITA__
@ -1213,6 +1214,10 @@ void UIScene_CreateWorldMenu::CreateGame(UIScene_CreateWorldMenu* pClass, DWORD
pClass->m_MoreOptionsParams.currentWorldSize = (EGameHostOptionWorldSize)(pClass->m_MoreOptionsParams.worldSize+1);
pClass->m_MoreOptionsParams.newWorldSize = (EGameHostOptionWorldSize)(pClass->m_MoreOptionsParams.worldSize+1);
#endif
app.SetGameHostOption(eGameHostOption_WorldMobCap, pClass->m_MoreOptionsParams.worldMobCap );
// Use helper to update the caps based on what was set earlier
MobCategory::updateMobCaps(pClass->m_MoreOptionsParams.worldMobCap);
g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0);

View file

@ -15,6 +15,14 @@ int m_iWorldSizeTitleA[4] =
};
#endif
string m_iWorldMobCapTitleA[4] =
{
"Small",
"Medium",
"Large",
"Unlimited"
};
UIScene_LaunchMoreOptionsMenu::UIScene_LaunchMoreOptionsMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
// Setup all the Iggy references we need for this scene
@ -132,6 +140,10 @@ UIScene_LaunchMoreOptionsMenu::UIScene_LaunchMoreOptionsMenu(int iPad, void *ini
m_tabIndex = m_params->bGenerateOptions ? TAB_WORLD_OPTIONS : TAB_GAME_OPTIONS;
// Introduce a mob cap slider for large worlds. If there were a better solution for this it would be nice...
m_sliderWorldMobCap.init(UIString::UIString("Mob Cap Size: " + m_iWorldMobCapTitleA[m_params->worldMobCap]),eControl_WorldMobCap,0,3,m_params->worldMobCap);
// set the default text
#ifdef _LARGE_WORLDS
wstring wsText=L"";
@ -663,6 +675,11 @@ void UIScene_LaunchMoreOptionsMenu::handleSliderMove(F64 sliderId, F64 currentVa
}
#endif
break;
case eControl_WorldMobCap:
m_sliderWorldMobCap.handleSliderMove(value);
m_params->worldMobCap = value;
m_sliderWorldMobCap.setLabel(UIString::UIString("Mob Cap Size: " + m_iWorldMobCapTitleA[m_params->worldMobCap]));
break;
}
}

View file

@ -38,6 +38,7 @@ private:
eControl_EditSeed,
eControl_WorldSize,
eControl_WorldResize,
eControl_WorldMobCap,
eControl_Count
};
@ -61,6 +62,8 @@ private:
UIControl_TextInput m_editSeed;
UIControl_Slider m_sliderWorldSize;
UIControl_Slider m_sliderWorldResize;
UIControl_Label m_labelWorldMobCap; // Added ~ Mob cap label for user control
UIControl_Slider m_sliderWorldMobCap;
IggyName m_funcSetMenuType, m_funcChangeTab, m_funcSetDescription;
UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene)
UI_MAP_ELEMENT( m_labelGameOptions, "LabelGame")
@ -99,6 +102,8 @@ private:
UI_MAP_ELEMENT( m_labelRandomSeed, "RandomSeed")
UI_MAP_ELEMENT( m_labelWorldSize, "WorldSize")
UI_MAP_ELEMENT( m_sliderWorldSize, "WorldSizeSlider")
UI_MAP_ELEMENT( m_labelWorldMobCap, "WorldMobCap")
UI_MAP_ELEMENT( m_sliderWorldMobCap, "WorldMobCapSlider")
UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_Structures], "CheckboxStructures")
UI_MAP_ELEMENT( m_checkboxes[eLaunchCheckbox_BonusChest], "CheckboxBonusChest")

View file

@ -8,6 +8,7 @@
#include "..\..\MinecraftServer.h"
#include "..\..\..\Minecraft.World\LevelSettings.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\..\Minecraft.World\MobCategory.h"
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
#include "Common\Network\Sony\SonyHttp.h"
#endif
@ -479,6 +480,8 @@ void UIScene_LoadMenu::tick()
m_MoreOptionsParams.bDisableSaving = app.GetGameHostOption(uiHostOptions,eGameHostOption_DisableSaving)>0?TRUE:FALSE;
m_MoreOptionsParams.currentWorldSize = (EGameHostOptionWorldSize)app.GetGameHostOption(uiHostOptions,eGameHostOption_WorldSize);
m_MoreOptionsParams.newWorldSize = m_MoreOptionsParams.currentWorldSize;
// Load world mob cap option
m_MoreOptionsParams.worldMobCap = app.GetGameHostOption(uiHostOptions,eGameHostOption_WorldMobCap);
m_MoreOptionsParams.bMobGriefing = app.GetGameHostOption(uiHostOptions, eGameHostOption_MobGriefing);
m_MoreOptionsParams.bKeepInventory = app.GetGameHostOption(uiHostOptions, eGameHostOption_KeepInventory);
@ -1616,6 +1619,10 @@ void UIScene_LoadMenu::StartGameFromSave(UIScene_LoadMenu* pClass, DWORD dwLocal
#ifdef _LARGE_WORLDS
app.SetGameHostOption(eGameHostOption_WorldSize, pClass->m_MoreOptionsParams.worldSize+1 ); // 0 is GAME_HOST_OPTION_WORLDSIZE_UNKNOWN
#endif
app.SetGameHostOption(eGameHostOption_WorldMobCap, pClass->m_MoreOptionsParams.worldMobCap );
// Use helper to update the caps based on what was set earlier
MobCategory::updateMobCaps(pClass->m_MoreOptionsParams.worldMobCap);
// app.SetGameNewWorldSize(64, true );
// app.SetGameNewWorldSize(0, false );

View file

@ -356,6 +356,7 @@ typedef struct _LaunchMoreOptionsMenuInitData
wstring seed;
int worldSize;
int worldMobCap;
bool bDisableSaving;
EGameHostOptionWorldSize currentWorldSize;
@ -372,6 +373,7 @@ typedef struct _LaunchMoreOptionsMenuInitData
bTNT = true;
iPad = -1;
worldSize = 3;
worldMobCap = 0;
seed = L"";
bDisableSaving = false;
newWorldSize = e_worldSize_Unknown;

View file

@ -4692,6 +4692,10 @@ void Level::decrementUnsavedChunkCount()
bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
{
if (app.GetGameHostOption(eGameHostOption_WorldMobCap) == 3)
{
return true;
}
int count = 0;
int max = 0;
if(spawnType == eSpawnType_Egg || spawnType == eSpawnType_Portal)
@ -4700,52 +4704,52 @@ bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
{
case eTYPE_VILLAGER:
count = countInstanceOf( eTYPE_VILLAGER, true);
max = MobCategory::MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG;
max = MobCategory::maxVillagersWithSpawnEgg;
break;
case eTYPE_CHICKEN:
count = countInstanceOf( eTYPE_CHICKEN, true);
max = MobCategory::MAX_XBOX_CHICKENS_WITH_SPAWN_EGG;
max = MobCategory::maxChickensWithSpawnEgg;
break;
case eTYPE_WOLF:
count = countInstanceOf( eTYPE_WOLF, true);
max = MobCategory::MAX_XBOX_WOLVES_WITH_SPAWN_EGG;
max = MobCategory::maxWolvesWithSpawnEgg;
break;
case eTYPE_MUSHROOMCOW:
count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG;
max = MobCategory::maxMushroomCowsWithSpawnEgg;
break;
case eTYPE_SQUID:
count = countInstanceOf( eTYPE_SQUID, true);
max = MobCategory::MAX_XBOX_SQUIDS_WITH_SPAWN_EGG;
max = MobCategory::maxSquidsWithSpawnEgg;
break;
case eTYPE_SNOWMAN:
count = countInstanceOf( eTYPE_SNOWMAN, true);
max = MobCategory::MAX_XBOX_SNOWMEN;
max = MobCategory::maxSnowGolems;
break;
case eTYPE_VILLAGERGOLEM:
count = countInstanceOf( eTYPE_VILLAGERGOLEM, true);
max = MobCategory::MAX_XBOX_IRONGOLEM;
max = MobCategory::maxIronGolems;
break;
case eTYPE_WITHERBOSS:
count = countInstanceOf(eTYPE_WITHERBOSS, true) + countInstanceOf(eTYPE_ENDERDRAGON, true);
max = MobCategory::MAX_CONSOLE_BOSS;
max = MobCategory::maxBosses;
break;
default:
if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
{
count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
max = MobCategory::MAX_XBOX_ANIMALS_WITH_SPAWN_EGG;
max = MobCategory::maxAnimalsWithSpawnEgg;
}
// 4J: Use eTYPE_ENEMY instead of monster (slimes and ghasts aren't monsters)
else if(Entity::instanceof(type, eTYPE_ENEMY))
{
count = countInstanceOf(eTYPE_ENEMY, false);
max = MobCategory::MAX_XBOX_MONSTERS_WITH_SPAWN_EGG;
max = MobCategory::maxMonstersWithSpawnEgg;
}
else if( (type & eTYPE_AMBIENT) == eTYPE_AMBIENT)
{
count = countInstanceOf( eTYPE_AMBIENT, false);
max = MobCategory::MAX_AMBIENT_WITH_SPAWN_EGG;
max = MobCategory::maxAmbientWithSpawnEgg;
}
// 4J: Added minecart and boats
else if (Entity::instanceof(type, eTYPE_MINECART))
@ -4766,25 +4770,25 @@ bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType)
{
case eTYPE_VILLAGER:
count = countInstanceOf( eTYPE_VILLAGER, true);
max = MobCategory::MAX_VILLAGERS_WITH_BREEDING;
max = MobCategory::maxVillagersWithBreeding;
break;
case eTYPE_CHICKEN:
count = countInstanceOf( eTYPE_CHICKEN, true);
max = MobCategory::MAX_XBOX_CHICKENS_WITH_BREEDING;
max = MobCategory::maxChickensWithBreeding;
break;
case eTYPE_WOLF:
count = countInstanceOf( eTYPE_WOLF, true);
max = MobCategory::MAX_XBOX_WOLVES_WITH_BREEDING;
max = MobCategory::maxWolvesWithBreeding;
break;
case eTYPE_MUSHROOMCOW:
count = countInstanceOf( eTYPE_MUSHROOMCOW, true);
max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING;
max = MobCategory::maxMushroomCowsWithBreeding;
break;
default:
if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK)
{
count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
max = MobCategory::MAX_XBOX_ANIMALS_WITH_BREEDING;
max = MobCategory::maxAnimalsWithBreeding;
}
else if( (type & eTYPE_MONSTER) == eTYPE_MONSTER)
{

View file

@ -19,26 +19,28 @@ MobCategoryArray MobCategory::values = MobCategoryArray(7);
void MobCategory::staticCtor()
{
// 4J - adjusted the max levels here for the xbox version, which now represent the max levels in the whole world
monster = new MobCategory(70, Material::air, false, false, eTYPE_MONSTER, false, CONSOLE_MONSTERS_HARD_LIMIT);
creature = new MobCategory(10, Material::air, true, true, eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false, CONSOLE_ANIMALS_HARD_LIMIT);
ambient = new MobCategory(15, Material::air, true, false, eTYPE_AMBIENT, false, CONSOLE_AMBIENT_HARD_LIMIT),
waterCreature = new MobCategory(5, Material::water, true, false, eTYPE_WATERANIMAL, false, CONSOLE_SQUID_HARD_LIMIT);
monster = new MobCategory(70, Material::air, false, false, eTYPE_MONSTER, false);
creature = new MobCategory(10, Material::air, true, true, eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false);
ambient = new MobCategory(15, Material::air, true, false, eTYPE_AMBIENT, false),
waterCreature = new MobCategory(5, Material::water, true, false, eTYPE_WATERANIMAL, false);
values[0] = monster;
values[1] = creature;
values[2] = ambient;
values[3] = waterCreature;
// 4J - added 2 new categories to give us better control over spawning wolves & chickens
creature_wolf = new MobCategory(3, Material::air, true, true, eTYPE_WOLF, true, MAX_XBOX_WOLVES);
creature_chicken = new MobCategory( 2, Material::air, true, true, eTYPE_CHICKEN, true, MAX_XBOX_CHICKENS);
creature_mushroomcow = new MobCategory(2, Material::air, true, true, eTYPE_MUSHROOMCOW, true, MAX_XBOX_MUSHROOMCOWS);
creature_wolf = new MobCategory(3, Material::air, true, true, eTYPE_WOLF, true);
creature_chicken = new MobCategory(2, Material::air, true, true, eTYPE_CHICKEN, true);
creature_mushroomcow = new MobCategory(2, Material::air, true, true, eTYPE_MUSHROOMCOW, true);
values[4] = creature_wolf;
values[5] = creature_chicken;
values[6] = creature_mushroomcow;
updateMobCaps(0); // Set the mob caps to the default limits
}
MobCategory::MobCategory(int maxVar, Material *spawnPositionMaterial, bool isFriendly, bool isPersistent, eINSTANCEOF eBase, bool isSingleType, int maxPerLevel)
: m_max(maxVar), spawnPositionMaterial(spawnPositionMaterial), m_isFriendly(isFriendly), m_isPersistent(isPersistent), m_eBase(eBase), m_isSingleType(isSingleType), m_maxPerLevel(maxPerLevel)
MobCategory::MobCategory(int maxVar, Material *spawnPositionMaterial, bool isFriendly, bool isPersistent, eINSTANCEOF eBase, bool isSingleType)
: m_max(maxVar), spawnPositionMaterial(spawnPositionMaterial), m_isFriendly(isFriendly), m_isPersistent(isPersistent), m_eBase(eBase), m_isSingleType(isSingleType)
{
}
@ -77,3 +79,71 @@ bool MobCategory::isPersistent()
{
return m_isPersistent;
}
void MobCategory::updateMobCaps(int mode)
{
switch (mode)
{
case 0: // Original limits
maxNaturalMonsters = 50;
maxNaturalAnimals = 50;
maxNaturalAmbient = 20;
maxNaturalSquid = 5;
maxNaturalChickens = 8;
maxNaturalWolves = 8;
maxNaturalMushroomCows = 2;
maxSnowGolems = 16;
maxIronGolems = 16;
maxBosses = 1;
maxVillagersWithBreeding = 35;
break;
case 1:
// Match previous definition of limits
maxNaturalMonsters = 70;
maxNaturalAnimals = 70;
maxNaturalAmbient = 20;
maxNaturalSquid = 10;
maxNaturalChickens = 16;
maxNaturalWolves = 16;
maxNaturalMushroomCows = 8;
maxSnowGolems = 16;
maxIronGolems = 16;
maxBosses = 1;
maxVillagersWithBreeding = 50;
break;
case 2: // Large, higher than both original and new limits
maxNaturalMonsters = 100;
maxNaturalAnimals = 100;
maxNaturalAmbient = 40;
maxNaturalSquid = 15;
maxNaturalChickens = 24;
maxNaturalWolves = 24;
maxNaturalMushroomCows = 16;
maxSnowGolems = 32;
maxIronGolems = 32;
maxBosses = 3;
maxVillagersWithBreeding = 100;
break;
case 3:
return; // Keep current limits, as all related checks are bypassed in this case
}
monster->m_maxPerLevel = maxNaturalMonsters;
creature->m_maxPerLevel = maxNaturalAnimals;
ambient->m_maxPerLevel = maxNaturalAmbient;
waterCreature->m_maxPerLevel = maxNaturalSquid;
creature_wolf->m_maxPerLevel = maxNaturalWolves;
creature_chicken->m_maxPerLevel = maxNaturalChickens;
creature_mushroomcow->m_maxPerLevel = maxNaturalMushroomCows;
maxAnimalsWithBreeding = maxNaturalAnimals + 20;
maxChickensWithBreeding = maxNaturalChickens + 8;
maxMushroomCowsWithBreeding = maxNaturalMushroomCows + 20;
maxWolvesWithBreeding = maxNaturalWolves + 8;
maxAnimalsWithSpawnEgg = maxAnimalsWithBreeding + 20;
maxChickensWithSpawnEgg = maxChickensWithBreeding + 10;
maxWolvesWithSpawnEgg = maxWolvesWithBreeding + 10;
maxMonstersWithSpawnEgg = maxNaturalMonsters + 20;
maxVillagersWithSpawnEgg = maxVillagersWithBreeding + 15;
maxMushroomCowsWithSpawnEgg = maxMushroomCowsWithBreeding + 8;
maxSquidsWithSpawnEgg = maxNaturalSquid + 8;
maxAmbientWithSpawnEgg = maxNaturalAmbient + 8;
}

View file

@ -209,10 +209,20 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie
}
}
// Use variable to not repeat checks
int maxInstances = mobCategory->getMaxInstancesPerLevel();
// Check for if the mob cap is "off" (works as Java does, scaled by chunk count)
bool usesChunkLimit = app.GetGameHostOption(eGameHostOption_WorldMobCap) == 3;
if (usesChunkLimit)
{
// Use Java logic for the max count instead, accounting for chunks polled and the size of a chunk.
maxInstances = mobCategory->getMaxInstancesPerChunk() * chunksToPoll.size() / CHUNK_HORIZONTAL_SCALE;
}
// 4J - this is now quite different to the java version. We just have global max counts for the level whereas the original has a max per chunk that
// scales with the number of chunks to be polled.
int categoryCount = level->countInstanceOf( mobCategory->getEnumBaseClass(), mobCategory->isSingleType());
if( categoryCount >= mobCategory->getMaxInstancesPerLevel())
if(categoryCount >= maxInstances)
{
continue;
}
@ -314,6 +324,7 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie
// was added initially to stop flat lands being totally populated with slimes but seems like a generally good rule.
eINSTANCEOF mobType = mob->GetType();
// Keep this check the same despite it not existing in Java as it would otherwise be a much more significant change to behavior.
if( ( mobType & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ) || ( mobType & eTYPE_MONSTER ) )
{
// even more special rule for ghasts, because filling up the nether with 25 of them is a bit unpleasant. In the java version they are
@ -321,26 +332,26 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie
// aren't actually even counted properly themselves
if( mobType == eTYPE_GHAST )
{
if( level->countInstanceOf(mobType, true) >= 4 ) continue;
if( level->countInstanceOf(mobType, true) >= 4 ) continue;
}
else if( mobType == eTYPE_ENDERMAN && level->dimension->id == 1 )
{
// Special rule for the end, as we only have Endermen (plus the dragon). Increase the spawnable counts based on level difficulty
int maxEndermen = mobCategory->getMaxInstancesPerLevel();
int maxEndermen = maxInstances;
if( level->difficulty == Difficulty::NORMAL )
{
maxEndermen -= mobCategory->getMaxInstancesPerLevel()/4;
maxEndermen -= maxInstances/4;
}
else if( level->difficulty <= Difficulty::EASY)
{
maxEndermen -= mobCategory->getMaxInstancesPerLevel()/2;
maxEndermen -= maxInstances/2;
}
if( level->countInstanceOf(mobType, true) >= maxEndermen ) continue;
}
else if( level->countInstanceOf(mobType, true) >= ( mobCategory->getMaxInstancesPerLevel() / 2 ) ) continue;
}
else if( level->countInstanceOf(mobType, true) >= ( maxInstances / 2 ) ) continue;
{ }
mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0);
@ -359,7 +370,7 @@ const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFrie
// 4J - change here so that we can't ever make more than the desired amount of entities in each priority. In the original java version
// depending on the random spawn positions being considered the only limit as to the number of entities created per category is the number
// of chunks to poll.
if (categoryCount >= mobCategory->getMaxInstancesPerLevel() ) goto categoryLoop;
if (categoryCount >= maxInstances ) goto categoryLoop;
if (clusterSize >= mob->getMaxSpawnClusterSize()) goto chunkLoop;
}
}

View file

@ -12,6 +12,8 @@ class Level;
class MobSpawner
{
private:
// For some reason, this constant is used in Java as the dividend for the max count of a mob category in the level, thus this is done to keep parity.
static const int CHUNK_HORIZONTAL_SCALE = 289;
static const int MIN_SPAWN_DISTANCE;
protected: