From 069759196d042e9ae7aa43a8b8fff56ccf56d64a Mon Sep 17 00:00:00 2001 From: Alexandra-Myers Date: Sat, 7 Mar 2026 15:33:40 -0500 Subject: [PATCH] Create initial mob caps option --- Minecraft.Client/Common/App_Defines.h | 4 +- Minecraft.Client/Common/App_enums.h | 1 + Minecraft.Client/Common/Consoles_App.cpp | 9 +- .../Common/UI/UIScene_CreateWorldMenu.cpp | 5 ++ .../UI/UIScene_LaunchMoreOptionsMenu.cpp | 17 ++++ .../Common/UI/UIScene_LaunchMoreOptionsMenu.h | 5 ++ .../Common/UI/UIScene_LoadMenu.cpp | 7 ++ Minecraft.Client/Common/UI/UIStructs.h | 2 + Minecraft.World/Level.cpp | 36 ++++---- Minecraft.World/MobCategory.cpp | 88 +++++++++++++++++-- Minecraft.World/MobSpawner.cpp | 27 ++++-- Minecraft.World/MobSpawner.h | 2 + 12 files changed, 168 insertions(+), 35 deletions(-) diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h index 7e96896c9..97d04b2ce 100644 --- a/Minecraft.Client/Common/App_Defines.h +++ b/Minecraft.Client/Common/App_Defines.h @@ -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 { diff --git a/Minecraft.Client/Common/App_enums.h b/Minecraft.Client/Common/App_enums.h index 15a179787..3c11cc386 100644 --- a/Minecraft.Client/Common/App_enums.h +++ b/Minecraft.Client/Common/App_enums.h @@ -636,6 +636,7 @@ enum eGameHostOption eGameHostOption_BedrockFog, eGameHostOption_NoHUD, eGameHostOption_WorldSize, + eGameHostOption_WorldMobCap, eGameHostOption_All, eGameHostOption_DisableSaving, diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 43cf73e15..11352bdc0 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -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; + 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: diff --git a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp index 67c0fd8c4..86fa684b6 100644 --- a/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_CreateWorldMenu.cpp @@ -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); diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp index 96dd744e9..af731bdc1 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.cpp @@ -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; } } diff --git a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h index caf9a71f0..aa7b97e2e 100644 --- a/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h +++ b/Minecraft.Client/Common/UI/UIScene_LaunchMoreOptionsMenu.h @@ -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") diff --git a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp index ed3c8137e..c087210e6 100644 --- a/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_LoadMenu.cpp @@ -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 ); diff --git a/Minecraft.Client/Common/UI/UIStructs.h b/Minecraft.Client/Common/UI/UIStructs.h index ac83458fc..17530f9e6 100644 --- a/Minecraft.Client/Common/UI/UIStructs.h +++ b/Minecraft.Client/Common/UI/UIStructs.h @@ -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; diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index ef79fb397..127c823ee 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -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) { diff --git a/Minecraft.World/MobCategory.cpp b/Minecraft.World/MobCategory.cpp index 507be6eda..d246e3cf8 100644 --- a/Minecraft.World/MobCategory.cpp +++ b/Minecraft.World/MobCategory.cpp @@ -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; +} \ No newline at end of file diff --git a/Minecraft.World/MobSpawner.cpp b/Minecraft.World/MobSpawner.cpp index e593a59f6..a6659b947 100644 --- a/Minecraft.World/MobSpawner.cpp +++ b/Minecraft.World/MobSpawner.cpp @@ -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; } } diff --git a/Minecraft.World/MobSpawner.h b/Minecraft.World/MobSpawner.h index 452ddcc43..153002537 100644 --- a/Minecraft.World/MobSpawner.h +++ b/Minecraft.World/MobSpawner.h @@ -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: