summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchangyong guo <guo1487@163.com>2018-08-02 16:59:10 +0200
committerpeterbell10 <peterbell10@live.co.uk>2018-08-02 16:59:10 +0200
commit57690b81a24a29d70cb6f4196a6e0f521a3cb61b (patch)
treeac40e460dd92c9fe8a9554b0815684b67ecf2e11
parentOcelots no longer multiply exponentially (#4272) (diff)
downloadcuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar.gz
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar.bz2
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar.lz
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar.xz
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.tar.zst
cuberite-57690b81a24a29d70cb6f4196a6e0f521a3cb61b.zip
-rw-r--r--Server/Plugins/APIDump/Classes/World.lua55
-rw-r--r--src/Bindings/ManualBindings_World.cpp80
-rw-r--r--src/Blocks/BlockMobSpawner.h2
-rw-r--r--src/Blocks/BlockOre.h2
-rw-r--r--src/Blocks/WorldInterface.h4
-rw-r--r--src/Entities/ExpOrb.cpp94
-rw-r--r--src/Entities/ExpOrb.h3
-rw-r--r--src/MobSpawner.cpp10
-rw-r--r--src/Mobs/Monster.cpp12
-rw-r--r--src/Mobs/Ocelot.cpp10
-rw-r--r--src/Mobs/PassiveMonster.cpp11
-rw-r--r--src/Mobs/Wolf.cpp13
-rw-r--r--src/UI/SlotArea.cpp2
-rw-r--r--src/World.cpp63
-rw-r--r--src/World.h12
15 files changed, 317 insertions, 56 deletions
diff --git a/Server/Plugins/APIDump/Classes/World.lua b/Server/Plugins/APIDump/Classes/World.lua
index bb991d9e6..8c19a44ef 100644
--- a/Server/Plugins/APIDump/Classes/World.lua
+++ b/Server/Plugins/APIDump/Classes/World.lua
@@ -968,6 +968,39 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
},
Notes = "If there is a mob head at the specified coords, calls the CallbackFunction with the {{cMobHeadEntity}} parameter representing the furnace. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cMobHeadEntity|MobHeadEntity}})</pre> The function returns false if there is no mob head, or if there is, it returns the bool value that the callback has returned.",
},
+ DoWithNearestPlayer =
+ {
+ Params =
+ {
+ {
+ Name = "Position",
+ Type = "Vector3d",
+ },
+ {
+ Name = "RangeLimit",
+ Type = "number",
+ },
+ {
+ Name = "CallbackFunction",
+ Type = "function",
+ },
+ {
+ Name = "CheckLineOfSight",
+ Type = "boolean",
+ },
+ {
+ Name = "IgnoreSpectator",
+ Type = "boolean",
+ },
+ },
+ Returns =
+ {
+ {
+ Type = "boolean",
+ },
+ },
+ Notes = "Calls the specified callback function with the {{cPlayer|player}} nearest to the specified position as its parameter, if they are still within the range limit. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found.",
+ },
DoWithNoteBlockAt =
{
Params =
@@ -3473,6 +3506,28 @@ function OnAllChunksAvailable()</pre> All return values from the callbacks are i
Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse ticks. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value. Returns the EntityID of the new spawned primed tnt, or {{cEntity#INVALID_ID|cEntity#INVALID_ID}} if no primed tnt was created. (DEPRECATED, use vector-parametered version)",
},
},
+ SpawnSplitExperienceOrbs =
+ {
+ Params =
+ {
+ {
+ Name = "Position",
+ Type = "Vector3d",
+ },
+ {
+ Name = "Reward",
+ Type = "number",
+ },
+ },
+ Returns =
+ {
+ {
+ Name = "EntityID",
+ Type = "table",
+ },
+ },
+ Notes = "Spawns experience orbs of the specified total value at the given location. The orbs' values are split according to regular Minecraft rules. Returns an array-table of UniqueID of all the orbs.",
+ },
TryGetHeight =
{
Params =
diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp
index 9b51ab926..c6716cf91 100644
--- a/src/Bindings/ManualBindings_World.cpp
+++ b/src/Bindings/ManualBindings_World.cpp
@@ -463,6 +463,52 @@ static int tolua_cWorld_DoWithPlayerByUUID(lua_State * tolua_S)
+static int tolua_cWorld_DoWithNearestPlayer(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cWorld") ||
+ !L.CheckParamUserType(2, "Vector3<double>") ||
+ !L.CheckParamNumber(3) ||
+ !L.CheckParamFunction(4) ||
+ // Params 5 and 6 are optional bools, no check for those
+ !L.CheckParamEnd(7)
+ )
+ {
+ return 0;
+ }
+
+ // Get parameters:
+ cWorld * Self;
+ Vector3d * Position;
+ double RangeLimit;
+ cLuaState::cRef FnRef;
+ bool CheckLineOfSight = true, IgnoreSpectators = true; // Defaults for the optional params
+ L.GetStackValues(1, Self, Position, RangeLimit, FnRef, CheckLineOfSight, IgnoreSpectators);
+
+ if (!FnRef.IsValid())
+ {
+ return L.ApiParamError("Expected a valid callback function for parameter #3");
+ }
+
+ // Call the function:
+ bool res = Self->DoWithNearestPlayer(*Position, RangeLimit, [&](cPlayer & a_Player)
+ {
+ bool ret = false;
+ L.Call(FnRef, &a_Player, cLuaState::Return, ret);
+ return ret;
+ }, CheckLineOfSight, IgnoreSpectators);
+
+ // Push the result as the return value:
+ L.Push(res);
+ return 1;
+}
+
+
+
+
+
static int tolua_cWorld_ForEachLoadedChunk(lua_State * tolua_S)
{
// Exported manually, because tolua doesn't support converting functions to functor types.
@@ -830,6 +876,38 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
+static int tolua_cWorld_SpawnSplitExperienceOrbs(lua_State* tolua_S)
+{
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamSelf("cWorld") ||
+ !L.CheckParamUserType(2, "Vector3<double>") ||
+ !L.CheckParamNumber(3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ cWorld * self = nullptr;
+ Vector3d * Position;
+ int Reward;
+ L.GetStackValues(1, self, Position, Reward);
+ if (self == nullptr)
+ {
+ tolua_error(tolua_S, "Invalid 'self' in function 'SpawnSplitExperienceOrbs'", nullptr);
+ return 0;
+ }
+
+ // Execute and push result:
+ L.Push(self->SpawnExperienceOrb(Position->x, Position->y, Position->z, Reward));
+ return 1;
+}
+
+
+
+
+
static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
{
/* Exported manually, because tolua would require the out-only param a_Height to be used when calling
@@ -897,6 +975,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithFlowerPotAt", DoWithXYZ<cWorld, cFlowerPotEntity, &cWorld::DoWithFlowerPotAt>);
tolua_function(tolua_S, "DoWithFurnaceAt", DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
tolua_function(tolua_S, "DoWithMobHeadAt", DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadAt>);
+ tolua_function(tolua_S, "DoWithNearestPlayer", tolua_cWorld_DoWithNearestPlayer);
tolua_function(tolua_S, "DoWithNoteBlockAt", DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
tolua_function(tolua_S, "DoWithPlayer", DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "DoWithPlayerByUUID", tolua_cWorld_DoWithPlayerByUUID);
@@ -917,6 +996,7 @@ void cManualBindings::BindWorld(lua_State * tolua_S)
tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
+ tolua_function(tolua_S, "SpawnSplitExperienceOrbs", tolua_cWorld_SpawnSplitExperienceOrbs);
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
diff --git a/src/Blocks/BlockMobSpawner.h b/src/Blocks/BlockMobSpawner.h
index f36e2b6eb..7911d3398 100644
--- a/src/Blocks/BlockMobSpawner.h
+++ b/src/Blocks/BlockMobSpawner.h
@@ -46,6 +46,6 @@ public:
auto & Random = GetRandomProvider();
int Reward = 15 + Random.RandInt(14) + Random.RandInt(14);
- a_WorldInterface.SpawnExperienceOrb(static_cast<double>(a_BlockX), static_cast<double>(a_BlockY + 1), static_cast<double>(a_BlockZ), Reward);
+ a_WorldInterface.SpawnSplitExperienceOrbs(static_cast<double>(a_BlockX), static_cast<double>(a_BlockY + 1), static_cast<double>(a_BlockZ), Reward);
}
} ;
diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h
index 0c72e39f9..0a52de3d6 100644
--- a/src/Blocks/BlockOre.h
+++ b/src/Blocks/BlockOre.h
@@ -121,7 +121,7 @@ public:
if (Reward != 0)
{
- a_WorldInterface.SpawnExperienceOrb(a_BlockX, a_BlockY, a_BlockZ, Reward);
+ a_WorldInterface.SpawnSplitExperienceOrbs(a_BlockX, a_BlockY, a_BlockZ, Reward);
}
}
} ;
diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h
index 344eb9ca3..e49283402 100644
--- a/src/Blocks/WorldInterface.h
+++ b/src/Blocks/WorldInterface.h
@@ -52,6 +52,10 @@ public:
Returns the UniqueID of the spawned experience orb, or cEntity::INVALID_ID on failure. */
virtual UInt32 SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) = 0;
+ /** Spawns experience orbs of the specified total value at the given location. The orbs' values are split according to regular Minecraft rules.
+ Returns an vector of UniqueID of all the orbs. */
+ virtual std::vector<UInt32> SpawnSplitExperienceOrbs(double a_X, double a_Y, double a_Z, int a_Reward) = 0;
+
/** Sends the block on those coords to the player */
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player) = 0;
diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index ecc2860b0..eda96bbc5 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -12,7 +12,8 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward)
{
SetMaxHealth(5);
SetHealth(5);
- SetGravity(0);
+ SetGravity(-16);
+ SetAirDrag(0.02f);
}
@@ -26,7 +27,8 @@ cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward)
{
SetMaxHealth(5);
SetHealth(5);
- SetGravity(0);
+ SetGravity(-16);
+ SetAirDrag(0.02f);
}
@@ -47,33 +49,52 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client)
void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
DetectCacti();
+ m_TicksAlive++;
- // Check player proximity no more than twice per second
- if ((m_TicksAlive % 10) == 0)
+ // Find closest player within 6.5 meter (slightly increase detect range to have same effect in client)
+ bool FoundPlayer = m_World->DoWithNearestPlayer(GetPosition(), 6.5, [&](cPlayer & a_Player) -> bool
{
- cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5, false));
- if ((a_ClosestPlayer != nullptr) && (!a_ClosestPlayer->IsGameModeSpectator()))
+ Vector3f a_PlayerPos(a_Player.GetPosition());
+ a_PlayerPos.y += 0.8f;
+ Vector3f a_Distance = a_PlayerPos - GetPosition();
+ double Distance = a_Distance.Length();
+
+ if (Distance < 0.7f)
+ {
+ a_Player.DeltaExperience(m_Reward);
+
+ m_World->BroadcastSoundEffect("entity.experience_orb.pickup", GetPosition(), 0.5f, (0.75f + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
+ Destroy(true);
+ return true;
+ }
+
+ // Experience orb will "float" or glide toward the player up to a distance of 6 blocks.
+ // speeding up as they get nearer to the player, Speed range 6 - 12 m per second, accelerate 60 m per second^2
+ Vector3d SpeedDelta(a_Distance);
+ SpeedDelta.Normalize();
+ SpeedDelta *= 3;
+
+ Vector3d CurrentSpeed = GetSpeed();
+ CurrentSpeed += SpeedDelta;
+ if (CurrentSpeed.Length() > 12)
{
- Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition());
- a_PlayerPos.y++;
- Vector3f a_Distance(a_PlayerPos - GetPosition());
- double Distance(a_Distance.Length());
- if (Distance < 0.5f)
- {
- LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward);
- a_ClosestPlayer->DeltaExperience(m_Reward);
-
- m_World->BroadcastSoundEffect("entity.experience_orb.pickup", GetPosition(), 0.5f, (0.75f + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
-
- Destroy(true);
- return;
- }
- SetSpeedX((a_PlayerPos.x - GetPosition().x) * 2.0);
- SetSpeedY((a_PlayerPos.y - GetPosition().y) * 2.0);
- SetSpeedZ((a_PlayerPos.z - GetPosition().z) * 2.0);
+ CurrentSpeed.Normalize();
+ CurrentSpeed *= 12;
}
+
+ SetSpeed(CurrentSpeed);
+ m_Gravity = 0;
+
+ return true;
+ }, false, true); // Don't check line of sight, ignore spectator mode player
+
+ if (!FoundPlayer)
+ {
+ m_Gravity = -16;
}
+
HandlePhysics(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
m_Timer += a_Dt;
if (m_Timer >= std::chrono::minutes(5))
@@ -96,3 +117,30 @@ bool cExpOrb::DoTakeDamage(TakeDamageInfo & a_TDI)
return super::DoTakeDamage(a_TDI);
}
+
+
+
+
+
+std::vector<int> cExpOrb::Split(int a_Reward)
+{
+ const static std::array<int, 11> BaseValue = {{1, 3, 7, 17, 37, 73, 149, 307, 617, 1237, 2477}};
+
+ std::vector<int> Rewards;
+ size_t Index = BaseValue.size() - 1; // Last one
+
+ while (a_Reward > 0)
+ {
+ while (a_Reward < BaseValue[Index])
+ {
+ Index--;
+ }
+
+ a_Reward -= BaseValue[Index];
+ Rewards.push_back(BaseValue[Index]);
+ }
+
+ return Rewards;
+}
+
+
diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h
index 20ac6e304..c4519e963 100644
--- a/src/Entities/ExpOrb.h
+++ b/src/Entities/ExpOrb.h
@@ -44,6 +44,9 @@ public:
// tolua_end
+ /** Split reward into small values according to regular Minecraft rules */
+ static std::vector<int> Split(int a_Reward);
+
protected:
int m_Reward;
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index f04cfa178..0e41e72ec 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -86,8 +86,14 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
auto & Random = GetRandomProvider();
BLOCKTYPE TargetBlock = a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ);
- cPlayer * a_Closest_Player = a_Chunk->GetWorld()->FindClosestPlayer(a_Chunk->PositionToWorldPosition(a_RelX, a_RelY, a_RelZ), 24);
- if (a_Closest_Player != nullptr) // Too close to a player, bail out
+ // If too close to any player, don't spawn anything
+ auto WorldPos = a_Chunk->PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
+ static const double RangeLimit = 24;
+ if (a_Chunk->GetWorld()->DoWithNearestPlayer(WorldPos, RangeLimit, [](cPlayer & a_Player) -> bool
+ {
+ return true;
+ })
+ )
{
return false;
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 3c202d693..5327da832 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -660,7 +660,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
}
if ((a_TDI.Attacker != nullptr) && (!IsBaby()))
{
- m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward);
+ m_World->SpawnSplitExperienceOrbs(GetPosX(), GetPosY(), GetPosZ(), Reward);
}
m_DestroyTimer = std::chrono::milliseconds(0);
}
@@ -712,13 +712,11 @@ void cMonster::OnRightClicked(cPlayer & a_Player)
// monster sez: Do I see the player
void cMonster::CheckEventSeePlayer(cChunk & a_Chunk)
{
- // TODO: Rewrite this to use cWorld's DoWithPlayers()
- cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false);
-
- if (Closest != nullptr)
+ m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
{
- EventSeePlayer(Closest, a_Chunk);
- }
+ EventSeePlayer(&a_Player, a_Chunk);
+ return true;
+ }, false);
}
diff --git a/src/Mobs/Ocelot.cpp b/src/Mobs/Ocelot.cpp
index 02af45a7d..855a11627 100644
--- a/src/Mobs/Ocelot.cpp
+++ b/src/Mobs/Ocelot.cpp
@@ -47,12 +47,11 @@ void cOcelot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_CheckPlayerTickCount == 23)
{
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), 10, true);
- if (a_Closest_Player != nullptr)
+ m_World->DoWithNearestPlayer(GetPosition(), 10, [&](cPlayer & a_Player) -> bool
{
cItems Items;
GetBreedingItems(Items);
- if (Items.ContainsType(a_Closest_Player->GetEquippedItem().m_ItemType))
+ if (Items.ContainsType(a_Player.GetEquippedItem().m_ItemType))
{
if (!IsBegging())
{
@@ -60,7 +59,7 @@ void cOcelot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_World->BroadcastEntityMetadata(*this);
}
- MoveToPosition(a_Closest_Player->GetPosition());
+ MoveToPosition(a_Player.GetPosition());
}
else
{
@@ -70,8 +69,9 @@ void cOcelot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_World->BroadcastEntityMetadata(*this);
}
}
- }
+ return true;
+ }, true);
m_CheckPlayerTickCount = 0;
}
else
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index c9345662d..cd0f59153 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -136,16 +136,17 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
GetFollowedItems(FollowedItems);
if (FollowedItems.Size() > 0)
{
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
- if (a_Closest_Player != nullptr)
+ m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
{
- cItem EquippedItem = a_Closest_Player->GetEquippedItem();
+ cItem EquippedItem = a_Player.GetEquippedItem();
if (FollowedItems.ContainsType(EquippedItem))
{
- Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ Vector3d PlayerPos = a_Player.GetPosition();
MoveToPosition(PlayerPos);
}
- }
+
+ return true;
+ });
}
}
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index 401175bf0..74924ab11 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -271,10 +271,9 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (GetTarget() == nullptr)
{
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
- if (a_Closest_Player != nullptr)
+ m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
{
- switch (a_Closest_Player->GetEquippedItem().m_ItemType)
+ switch (a_Player.GetEquippedItem().m_ItemType)
{
case E_ITEM_BONE:
case E_ITEM_RAW_BEEF:
@@ -291,12 +290,12 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_World->BroadcastEntityMetadata(*this);
}
- m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food
+ m_FinalDestination = a_Player.GetPosition(); // So that we will look at a player holding food
// Don't move to the player if the wolf is sitting.
if (!IsSitting())
{
- MoveToPosition(a_Closest_Player->GetPosition());
+ MoveToPosition(a_Player.GetPosition());
}
break;
@@ -310,7 +309,9 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
}
}
- }
+
+ return true;
+ });
}
else
{
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 19c75d196..0c69e33b0 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -1937,7 +1937,7 @@ void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Playe
int Reward = m_Furnace->GetAndResetReward();
if (Reward > 0)
{
- a_Player.GetWorld()->SpawnExperienceOrb(a_Player.GetPosX(), a_Player.GetPosY(), a_Player.GetPosZ(), Reward);
+ a_Player.GetWorld()->SpawnSplitExperienceOrbs(a_Player.GetPosX(), a_Player.GetPosY(), a_Player.GetPosZ(), Reward);
}
/** TODO 2014-05-12 xdot: Figure out when to call this method. */
diff --git a/src/World.cpp b/src/World.cpp
index 0c4618606..c832f7386 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -2298,6 +2298,49 @@ UInt32 cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Rewa
+std::vector<UInt32> cWorld::SpawnSplitExperienceOrbs(double a_X, double a_Y, double a_Z, int a_Reward)
+{
+ std::vector<UInt32> OrbsID;
+
+ if (a_Reward < 1)
+ {
+ LOGWARNING("%s: Attempting to create an experience orb with non-positive reward!", __FUNCTION__);
+ return OrbsID;
+ }
+
+ std::vector<int> Rewards = cExpOrb::Split(a_Reward);
+
+ // Check generate number to decide speed limit (distribute range)
+ float SpeedLimit = (Rewards.size() / 2) + 5;
+ if (SpeedLimit > 10)
+ {
+ SpeedLimit = 10;
+ }
+
+ auto & Random = GetRandomProvider();
+ for (auto Reward : Rewards)
+ {
+ auto ExpOrb = cpp14::make_unique<cExpOrb>(a_X, a_Y, a_Z, Reward);
+ auto ExpOrbPtr = ExpOrb.get();
+ double SpeedX = Random.RandReal(-SpeedLimit, SpeedLimit);
+ double SpeedY = Random.RandReal(0.5);
+ double SpeedZ = Random.RandReal(-SpeedLimit, SpeedLimit);
+ ExpOrbPtr->SetSpeed(SpeedX, SpeedY, SpeedZ);
+
+ UInt32 Id = ExpOrbPtr->GetUniqueID();
+ if (ExpOrbPtr->Initialize(std::move(ExpOrb), *this))
+ {
+ OrbsID.push_back(Id);
+ }
+ }
+
+ return OrbsID;
+}
+
+
+
+
+
UInt32 cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content, int a_BlockHeight)
{
std::unique_ptr<cMinecart> Minecart;
@@ -2817,9 +2860,9 @@ bool cWorld::DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback
-cPlayer * cWorld::FindClosestPlayer(Vector3d a_Pos, float a_SightLimit, bool a_CheckLineOfSight)
+bool cWorld::DoWithNearestPlayer(Vector3d a_Pos, double a_RangeLimit, cPlayerListCallback a_Callback, bool a_CheckLineOfSight, bool a_IgnoreSpectator)
{
- double ClosestDistance = a_SightLimit;
+ double ClosestDistance = a_RangeLimit;
cPlayer * ClosestPlayer = nullptr;
cCSLock Lock(m_CSPlayers);
@@ -2829,6 +2872,12 @@ cPlayer * cWorld::FindClosestPlayer(Vector3d a_Pos, float a_SightLimit, bool a_C
{
continue;
}
+
+ if (a_IgnoreSpectator && (*itr)->IsGameModeSpectator())
+ {
+ continue;
+ }
+
Vector3f Pos = (*itr)->GetPosition();
double Distance = (Pos - a_Pos).Length();
@@ -2850,7 +2899,15 @@ cPlayer * cWorld::FindClosestPlayer(Vector3d a_Pos, float a_SightLimit, bool a_C
ClosestDistance = Distance;
ClosestPlayer = *itr;
}
- return ClosestPlayer;
+
+ if (ClosestPlayer)
+ {
+ return a_Callback(*ClosestPlayer);
+ }
+ else
+ {
+ return false;
+ }
}
diff --git a/src/World.h b/src/World.h
index 1deae2b74..faccb01f0 100644
--- a/src/World.h
+++ b/src/World.h
@@ -279,8 +279,8 @@ public:
/** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
- cPlayer * FindClosestPlayer(Vector3d a_Pos, float a_SightLimit, bool a_CheckLineOfSight = true);
+ /** Calls the callback for nearest player for given position, Returns false if player not found, otherwise returns the same value as the callback */
+ bool DoWithNearestPlayer(Vector3d a_Pos, double a_RangeLimit, cPlayerListCallback a_Callback, bool a_CheckLineOfSight = true, bool a_IgnoreSpectator = true);
/** Finds the player over his uuid and calls the callback */
bool DoWithPlayerByUUID(const cUUID & a_PlayerUUID, cPlayerListCallback a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
@@ -465,6 +465,14 @@ public:
Returns the UniqueID of the spawned experience orb, or cEntity::INVALID_ID on failure. */
virtual UInt32 SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward) override;
+ // tolua_end
+
+ /** Spawns experience orbs of the specified total value at the given location. The orbs' values are split according to regular Minecraft rules.
+ Returns an vector of UniqueID of all the orbs. */
+ virtual std::vector<UInt32> SpawnSplitExperienceOrbs(double a_X, double a_Y, double a_Z, int a_Reward) override; // Exported in ManualBindings_World.cpp
+
+ // tolua_begin
+
// DEPRECATED, use the vector-parametered version instead.
UInt32 SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTimeInSec = 80, double a_InitialVelocityCoeff = 1)
{