diff options
Diffstat (limited to 'source/World.cpp')
-rw-r--r-- | source/World.cpp | 368 |
1 files changed, 149 insertions, 219 deletions
diff --git a/source/World.cpp b/source/World.cpp index ef56e7fe9..dd3965e3d 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -28,35 +28,9 @@ #include "Simulator/VaporizeFluidSimulator.h" // Mobs: -#include "Mobs/Bat.h" -#include "Mobs/Blaze.h" -#include "Mobs/Cavespider.h" -#include "Mobs/Chicken.h" -#include "Mobs/Cow.h" -#include "Mobs/Creeper.h" -#include "Mobs/Enderman.h" -#include "Mobs/EnderDragon.h" -#include "Mobs/Ghast.h" -#include "Mobs/Giant.h" -#include "Mobs/Horse.h" -#include "Mobs/IronGolem.h" -#include "Mobs/Magmacube.h" -#include "Mobs/Mooshroom.h" -#include "Mobs/Ocelot.h" -#include "Mobs/Pig.h" -#include "Mobs/Sheep.h" -#include "Mobs/Silverfish.h" -#include "Mobs/Skeleton.h" -#include "Mobs/Slime.h" -#include "Mobs/SnowGolem.h" -#include "Mobs/Spider.h" -#include "Mobs/Squid.h" -#include "Mobs/Villager.h" -#include "Mobs/Witch.h" -#include "Mobs/Wither.h" -#include "Mobs/Wolf.h" -#include "Mobs/Zombie.h" -#include "Mobs/Zombiepigman.h" +#include "Mobs/IncludeAllMonsters.h" +#include "MobCensus.h" +#include "MobSpawner.h" #include "MersenneTwister.h" #include "Generating/Trees.h" @@ -81,6 +55,12 @@ /// Up to this many m_SpreadQueue elements are handled each world tick const int MAX_LIGHTING_SPREAD_PER_TICK = 10; +const int TIME_SUNSET = 12000; +const int TIME_NIGHT_START = 13187; +const int TIME_NIGHT_END = 22812; +const int TIME_SUNRISE = 23999; +const int TIME_SPAWN_DIVISOR = 148; + @@ -252,11 +232,11 @@ cWorld::cWorld(const AString & a_WorldName) : m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), - m_LastSpawnMonster(0), m_RSList(0), m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) - m_TickThread(*this) + m_TickThread(*this), + m_SkyDarkness(0) { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); @@ -471,8 +451,8 @@ void cWorld::Start(void) m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500); m_GameMode = eGameMode_Creative; - cIniFile IniFile(m_IniFileName); - if (!IniFile.ReadFile()) + cIniFile IniFile; + if (!IniFile.ReadFile(m_IniFileName)) { LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str()); } @@ -515,14 +495,35 @@ void cWorld::Start(void) m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode); - m_bAnimals = true; - m_SpawnMonsterRate = 200; // 1 mob each 10 seconds - cIniFile IniFile2("settings.ini"); - if (IniFile2.ReadFile()) + // Load allowed mobs: + const char * DefaultMonsters = ""; + switch (m_Dimension) { - m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true); - m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks - + case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break; + case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break; + case dimEnd: DefaultMonsters = "enderman"; break; + default: + { + ASSERT(!"Unhandled world dimension"); + DefaultMonsters = "wither"; + break; + } + } + m_bAnimals = IniFile.GetValueB("Monsters", "AnimalsOn", true); + AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters); + AStringVector SplitList = StringSplitAndTrim(AllMonsters, ","); + for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr) + { + cMonster::eType ToAdd = cMonster::StringToMobType(*itr); + if (ToAdd != cMonster::mtInvalidType) + { + m_AllowedMobs.insert(ToAdd); + LOGD("Allowed mob: %s", itr->c_str()); + } + else + { + LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str()); + } } m_ChunkMap = new cChunkMap(this); @@ -553,8 +554,15 @@ void cWorld::Start(void) m_ChunkSender.Start(this); m_TickThread.Start(); + // Init of the spawn monster time (as they are supposed to have different spawn rate) + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0)); + + // Save any changes that the defaults may have done to the ini file: - if (!IniFile.WriteFile()) + if (!IniFile.WriteFile(m_IniFileName)) { LOGWARNING("Could not write world config to %s", m_IniFileName.c_str()); } @@ -607,6 +615,9 @@ void cWorld::Tick(float a_Dt) m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0); + // Updates the sky darkness based on current time of day + UpdateSkyDarkness(); + // Broadcast time update every 40 ticks (2 seconds) if (m_LastTimeUpdate < m_WorldAge - 40) { @@ -648,7 +659,7 @@ void cWorld::Tick(float a_Dt) UnloadUnusedChunks(); } - TickSpawnMobs(a_Dt); + TickMobs(a_Dt); std::vector<int> m_RSList_copy(m_RSList); @@ -733,125 +744,61 @@ void cWorld::TickWeather(float a_Dt) -void cWorld::TickSpawnMobs(float a_Dt) +void cWorld::TickMobs(float a_Dt) { - if (!m_bAnimals || (m_WorldAge - m_LastSpawnMonster <= m_SpawnMonsterRate)) - { - return; - } - - m_LastSpawnMonster = m_WorldAge; - Vector3d SpawnPos; - { - cCSLock Lock(m_CSPlayers); - if (m_Players.size() <= 0) - { - return; - } - int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); - cPlayerList::iterator itr = m_Players.begin(); - for (int i = 1; i < RandomPlayerIdx; i++) - { - itr++; - } - SpawnPos = (*itr)->GetPosition(); - } + // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs + cWorld::cLock Lock(*this); - int dayRand = (m_TickRand.randInt() / 7) % 6; - int nightRand = (m_TickRand.randInt() / 11) % 10; - - SpawnPos += Vector3d((double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32); - int Height = GetHeight((int)SpawnPos.x, (int)SpawnPos.z); - - int MobType = -1; - int Biome = GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z); - switch (Biome) + // before every Mob action, we have to count them depending on the distance to players, on their family ... + cMobCensus MobCensus; + m_ChunkMap->CollectMobCensus(MobCensus); + if (m_bAnimals) { - case biNether: + // Spawning is enabled, spawn now: + static const cMonster::eFamily AllFamilies[] = { - // Spawn nether mobs - switch (nightRand) - { - case 0: MobType = cMonster::mtBlaze; break; - case 1: MobType = cMonster::mtGhast; break; - case 2: MobType = cMonster::mtGhast; break; - case 3: MobType = cMonster::mtGhast; break; - case 4: MobType = cMonster::mtZombiePigman; break; - case 5: MobType = cMonster::mtZombiePigman; break; - case 6: MobType = cMonster::mtZombiePigman; break; - case 7: MobType = cMonster::mtZombiePigman; break; - case 8: MobType = cMonster::mtZombiePigman; break; - case 9: MobType = cMonster::mtZombiePigman; break; - } - break; - } - - case biEnd: + cMonster::mfHostile, + cMonster::mfPassive, + cMonster::mfAmbient, + cMonster::mfWater, + } ; + for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++) { - // Spawn only The End mobs - switch (nightRand) + cMonster::eFamily Family = AllFamilies[i]; + int SpawnDelay = cMonster::GetSpawnDelay(Family); + if ( + (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round + MobCensus.IsCapped(Family) + ) { - case 0: MobType = cMonster::mtEnderDragon; break; - case 1: MobType = cMonster::mtEnderman; break; - case 2: MobType = cMonster::mtEnderman; break; - case 3: MobType = cMonster::mtEnderman; break; - case 4: MobType = cMonster::mtEnderman; break; - case 5: MobType = cMonster::mtEnderman; break; - case 6: MobType = cMonster::mtEnderman; break; - case 7: MobType = cMonster::mtEnderman; break; - case 8: MobType = cMonster::mtEnderman; break; - case 9: MobType = cMonster::mtEnderman; break; + continue; } - break; - } - - case biMushroomIsland: - case biMushroomShore: - { - // Mushroom land gets only mooshrooms - MobType = cMonster::mtMooshroom; - break; - } - - default: - { - // Overworld biomes depend on whether it's night or day: - if (m_TimeOfDay >= 12000 + 1000) - { - // Night mobs: - switch (nightRand) - { - case 0: MobType = cMonster::mtSpider; break; - case 1: MobType = cMonster::mtZombie; break; - case 2: MobType = cMonster::mtEnderman; break; - case 3: MobType = cMonster::mtCreeper; break; - case 4: MobType = cMonster::mtCaveSpider; break; - case 7: MobType = cMonster::mtSlime; break; - case 8: MobType = cMonster::mtSilverfish; break; - case 9: MobType = cMonster::mtSkeleton; break; - } - } // if (night) - else + m_LastSpawnMonster[Family] = m_WorldAge; + cMobSpawner Spawner(Family, m_AllowedMobs); + if (Spawner.CanSpawnAnything()) { - // During the day: - switch (dayRand) + m_ChunkMap->SpawnMobs(Spawner); + // do the spawn + for (cMobSpawner::tSpawnedContainer::const_iterator itr2 = Spawner.getSpawned().begin(); itr2 != Spawner.getSpawned().end(); itr2++) { - case 0: MobType = cMonster::mtChicken; break; - case 1: MobType = cMonster::mtCow; break; - case 2: MobType = cMonster::mtPig; break; - case 3: MobType = cMonster::mtSheep; break; - case 4: MobType = cMonster::mtSquid; break; - case 5: MobType = cMonster::mtWolf; break; - case 6: MobType = cMonster::mtHorse; break; + SpawnMobFinalize(*itr2); } - } // else (night) - } // case overworld biomes - } // switch (biome) + } + } // for i - AllFamilies[] + } // if (Spawning enabled) - if (MobType >= 0) + // move close mobs + cMobProximityCounter::sIterablePair allCloseEnoughToMoveMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(-1, 64 * 16);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allCloseEnoughToMoveMobs.m_Begin; itr != allCloseEnoughToMoveMobs.m_End; itr++) { - // A proper mob type was selected, now spawn the mob: - SpawnMob(SpawnPos.x, SpawnPos.y, SpawnPos.z, (cMonster::eType)MobType); + itr->second.m_Monster.Tick(a_Dt, itr->second.m_Chunk); + } + + // remove too far mobs + cMobProximityCounter::sIterablePair allTooFarMobs = MobCensus.GetProximityCounter().getMobWithinThosesDistances(128 * 16, -1);// MG TODO : deal with this magic number (the 16 is the size of a block) + for(cMobProximityCounter::tDistanceToMonster::const_iterator itr = allTooFarMobs.m_Begin; itr != allTooFarMobs.m_End; itr++) + { + itr->second.m_Monster.Destroy(true); } } @@ -931,6 +878,31 @@ void cWorld::TickClients(float a_Dt) +void cWorld::UpdateSkyDarkness(void) +{ + int TempTime = (int)m_TimeOfDay; + if (TempTime <= TIME_SUNSET) + { + m_SkyDarkness = 0; + } + else if (TempTime <= TIME_NIGHT_START) + { + m_SkyDarkness = (TIME_NIGHT_START - TempTime) / TIME_SPAWN_DIVISOR; + } + else if (TempTime <= TIME_NIGHT_END) + { + m_SkyDarkness = 8; + } + else + { + m_SkyDarkness = (TIME_SUNRISE - TempTime) / TIME_SPAWN_DIVISOR; + } +} + + + + + void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) { return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); @@ -1533,7 +1505,7 @@ bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed) +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) { MTRand r1; a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop @@ -1545,7 +1517,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double cPickup * Pickup = new cPickup( a_BlockX, a_BlockY, a_BlockZ, - *itr, SpeedX, SpeedY, SpeedZ + *itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ ); Pickup->Initialize(this); } @@ -1555,13 +1527,13 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ) +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated) { for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { cPickup * Pickup = new cPickup( a_BlockX, a_BlockY, a_BlockZ, - *itr, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ + *itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ ); Pickup->Initialize(this); } @@ -2592,82 +2564,39 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp { cMonster * Monster = NULL; - int SlSize = GetTickRandomNumber(2) + 1; // 1 .. 3 - Slime int ShColor = GetTickRandomNumber(15); // 0 .. 15 - Sheep - bool SkType = GetDimension() == biNether; // Skeleton - - int VilType = GetTickRandomNumber(cVillager::vtMax); // 0 .. 6 - Villager - if (VilType == 6) { VilType = 0; } // Give farmers a better chance of spawning + bool SkType = GetDimension() == dimNether ; // Skeleton - int HseType = GetTickRandomNumber(7); // 0 .. 7 - Horse Type (donkey, zombie, etc.) - int HseColor = GetTickRandomNumber(6); // 0 .. 6 - Horse - int HseStyle = GetTickRandomNumber(4); // 0 .. 4 - Horse - int HseTameTimes = GetTickRandomNumber(6) + 1; // 1 .. 7 - Horse tame amount - if ((HseType == 5) || (HseType == 6) || (HseType == 7)) { HseType = 0; } // 5,6,7 = 0 because little chance of getting 0 with TickRand - - switch (a_MonsterType) - { - case cMonster::mtBat: Monster = new cBat(); break; - case cMonster::mtBlaze: Monster = new cBlaze(); break; - case cMonster::mtCaveSpider: Monster = new cCavespider(); break; - case cMonster::mtChicken: Monster = new cChicken(); break; - case cMonster::mtCow: Monster = new cCow(); break; - case cMonster::mtCreeper: Monster = new cCreeper(); break; - case cMonster::mtEnderman: Monster = new cEnderman(); break; - case cMonster::mtEnderDragon: Monster = new cEnderDragon(); break; - case cMonster::mtGhast: Monster = new cGhast(); break; - case cMonster::mtGiant: Monster = new cGiant(); break; - case cMonster::mtHorse: - { - Monster = new cHorse(HseType, HseColor, HseStyle, HseTameTimes); break; - } - case cMonster::mtIronGolem: Monster = new cIronGolem(); break; - case cMonster::mtMagmaCube: Monster = new cMagmaCube(SlSize); break; - case cMonster::mtMooshroom: Monster = new cMooshroom(); break; - case cMonster::mtOcelot: Monster = new cOcelot(); break; - case cMonster::mtPig: Monster = new cPig(); break; - case cMonster::mtSheep: Monster = new cSheep(ShColor); break; - case cMonster::mtSilverfish: Monster = new cSilverfish(); break; - case cMonster::mtSkeleton: Monster = new cSkeleton(SkType); break; - case cMonster::mtSlime: Monster = new cSlime(SlSize); break; - case cMonster::mtSnowGolem: Monster = new cSnowGolem(); break; - case cMonster::mtSpider: Monster = new cSpider(); break; - case cMonster::mtSquid: Monster = new cSquid(); break; - case cMonster::mtVillager: - { - Monster = new cVillager((cVillager::eVillagerType)VilType); break; - } - case cMonster::mtWitch: Monster = new cWitch(); break; - case cMonster::mtWither: Monster = new cWither(); break; - case cMonster::mtWolf: Monster = new cWolf(); break; - case cMonster::mtZombie: Monster = new cZombie(false); break; // TODO: Villager infection - case cMonster::mtZombiePigman: Monster = new cZombiePigman(); break; - - default: - { - LOGWARNING("%s: Unhandled monster type: %d. Not spawning.", __FUNCTION__, a_MonsterType); - return -1; - } + Monster = cMonster::NewMonsterFromType(a_MonsterType); + if (Monster != NULL) + { + Monster->SetPosition(a_PosX, a_PosY, a_PosZ); } - Monster->SetPosition(a_PosX, a_PosY, a_PosZ); - Monster->SetHealth(Monster->GetMaxHealth()); - if (cPluginManager::Get()->CallHookSpawningMonster(*this, *Monster)) + return SpawnMobFinalize(Monster); +} + + + + +int cWorld::SpawnMobFinalize(cMonster * a_Monster) +{ + if (!a_Monster) + return -1; + a_Monster->SetHealth(a_Monster->GetMaxHealth()); + if (cPluginManager::Get()->CallHookSpawningMonster(*this, *a_Monster)) { - delete Monster; + delete a_Monster; return -1; } - if (!Monster->Initialize(this)) + if (!a_Monster->Initialize(this)) { - delete Monster; + delete a_Monster; return -1; } + BroadcastSpawnEntity(*a_Monster); + cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); - BroadcastSpawnEntity(*Monster); - // Because it's logical that ALL mob spawns need spawn effects, not just spawners - BroadcastSoundParticleEffect(2004, (int)(floor(a_PosX) * 8), (int)(floor(a_PosY) * 8), (int)(floor(a_PosZ) * 8), 0); - - cPluginManager::Get()->CallHookSpawnedMonster(*this, *Monster); - return Monster->GetUniqueID(); + return a_Monster->GetUniqueID(); } @@ -2782,3 +2711,4 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) + |