summaryrefslogtreecommitdiffstats
path: root/source/World.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/World.cpp')
-rw-r--r--source/World.cpp368
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)
+