Experience orb (#4259)
* Replace cWorld::FindClosesPlayer with cWorld::DoWithClosestPlayer * Implement experience reward splitting into the orb sizes used in vanilla * Modified speed calculation in cExpOrb::Tick to make the orbs fly towards the player Fixes #4216
This commit is contained in:
parent
1e014a54dc
commit
57690b81a2
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
} ;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
SetSpeedX((a_PlayerPos.x - GetPosition().x) * 2.0);
|
||||
SetSpeedY((a_PlayerPos.y - GetPosition().y) * 2.0);
|
||||
SetSpeedZ((a_PlayerPos.z - GetPosition().z) * 2.0);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
12
src/World.h
12
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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user