mirror of
https://github.com/cuberite/cuberite.git
synced 2025-01-07 03:16:55 +08:00
Chorus fruit teleport (#5259)
* Outline function for teleporting. * Created new handler for chorus fruit. * Fixed AttemptTeleport failing. * Better names, working sound effect. * Corrected naming. * Remove stray LOGD. * Offset teleport to middle of block. * Style Fixes Co-authored-by: x12xx12x <44411062+12xx12@users.noreply.github.com> * Style Fixes 2 Co-authored-by: x12xx12x <44411062+12xx12@users.noreply.github.com> * Move FindTeleportDestination to static cPawn method. * cBoundingBox interface. * Cleanup includes. * Maybe exported to API? * Change a_World to reference, add to APIDesc. Co-authored-by: x12xx12x <44411062+12xx12@users.noreply.github.com>
This commit is contained in:
parent
e9265b1d01
commit
1d1fa91401
@ -9587,6 +9587,91 @@ a_Player:OpenWindow(Window);
|
||||
{
|
||||
Notes = "Removes all currently applied entity effects",
|
||||
},
|
||||
FindTeleportDestination =
|
||||
{
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "World",
|
||||
Type = "cWorld",
|
||||
},
|
||||
{
|
||||
Name = "HeightRequired",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "NumTries",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "Destination",
|
||||
Type = "Vector3d",
|
||||
},
|
||||
{
|
||||
Name = "MinBoxCorner",
|
||||
Type = "Vector3i",
|
||||
},
|
||||
{
|
||||
Name = "MaxBoxCorner",
|
||||
Type = "Vector3i",
|
||||
},
|
||||
},
|
||||
Notes = "Function to find suitable teleport destination in or below box. Returns true and places result in Destination if found, otherwise returns false. Details at: https://minecraft.fandom.com/wiki/Enderman#Teleportation.",
|
||||
},
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "World",
|
||||
Type = "cWorld",
|
||||
},
|
||||
{
|
||||
Name = "HeightRequired",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "NumTries",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "Destination",
|
||||
Type = "Vector3d",
|
||||
},
|
||||
{
|
||||
Name = "BoundingBox",
|
||||
Type = "cBoundingBox",
|
||||
},
|
||||
},
|
||||
Notes = "Function to find suitable teleport destination in or below box. Returns true and places result in Destination if found, otherwise returns false. Details at: https://minecraft.fandom.com/wiki/Enderman#Teleportation.",
|
||||
},
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "World",
|
||||
Type = "cWorld",
|
||||
},
|
||||
{
|
||||
Name = "HeightRequired",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "NumTries",
|
||||
Type = "number",
|
||||
},
|
||||
{
|
||||
Name = "Centre",
|
||||
Type = "Vector3i",
|
||||
},
|
||||
{
|
||||
Name = "HalfCubeWidth",
|
||||
Type = "number",
|
||||
},
|
||||
},
|
||||
Notes = "Function to find suitable teleport destination in or below box. Returns true and places result in Destination if found, otherwise returns false. Details at: https://minecraft.fandom.com/wiki/Enderman#Teleportation.",
|
||||
},
|
||||
},
|
||||
HasEntityEffect =
|
||||
{
|
||||
Params =
|
||||
|
@ -1756,7 +1756,8 @@ void cClientHandle::HandleUseItem(bool a_UsedMainHand)
|
||||
if (
|
||||
ItemHandler->IsFood() &&
|
||||
(m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only non-creative or hungry players can eat
|
||||
(HeldItem.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten
|
||||
(HeldItem.m_ItemType != E_ITEM_GOLDEN_APPLE) && // Golden apple is a special case, it is used instead of eaten
|
||||
(HeldItem.m_ItemType != E_ITEM_CHORUS_FRUIT) // Chorus fruit is a special case, it is used instead of eaten
|
||||
)
|
||||
{
|
||||
// The player is satiated or in creative, and trying to eat
|
||||
|
@ -547,3 +547,81 @@ bool cPawn::DeductTotem(const eDamageType a_DamageType)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPawn::FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, const Vector3i a_MinBoxCorner, const Vector3i a_MaxBoxCorner)
|
||||
{
|
||||
/*
|
||||
Algorithm:
|
||||
Choose random destination.
|
||||
Seek downwards, regardless of distance until the block is made of movement-blocking material: https://minecraft.fandom.com/wiki/Materials
|
||||
Succeeds if no liquid or solid blocks prevents from standing at destination.
|
||||
*/
|
||||
auto & Random = GetRandomProvider();
|
||||
|
||||
for (unsigned int i = 0; i < a_NumTries; i++)
|
||||
{
|
||||
const int DestX = Random.RandInt(a_MinBoxCorner.x, a_MaxBoxCorner.x);
|
||||
int DestY = Random.RandInt(a_MinBoxCorner.y, a_MaxBoxCorner.y);
|
||||
const int DestZ = Random.RandInt(a_MinBoxCorner.z, a_MaxBoxCorner.z);
|
||||
|
||||
// Seek downwards from initial destination until we find a solid block or go into the void
|
||||
BLOCKTYPE DestBlock = a_World.GetBlock({DestX, DestY, DestZ});
|
||||
while ((DestY >= 0) && !cBlockInfo::IsSolid(DestBlock))
|
||||
{
|
||||
DestBlock = a_World.GetBlock({DestX, DestY, DestZ});
|
||||
DestY--;
|
||||
}
|
||||
|
||||
// Couldn't find a solid block so move to next attempt
|
||||
if (DestY < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Succeed if blocks above destination are empty
|
||||
bool Success = true;
|
||||
for (int j = 1; j <= a_HeightRequired; j++)
|
||||
{
|
||||
BLOCKTYPE TestBlock = a_World.GetBlock({DestX, DestY + j, DestZ});
|
||||
if (cBlockInfo::IsSolid(TestBlock) || IsBlockLiquid(TestBlock))
|
||||
{
|
||||
Success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Offsets for entity to be centred and standing on solid block
|
||||
a_Destination = Vector3d(DestX + 0.5, DestY + 1, DestZ + 0.5);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPawn::FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, const cBoundingBox a_BoundingBox)
|
||||
{
|
||||
return FindTeleportDestination(a_World, a_HeightRequired, a_NumTries, a_Destination, a_BoundingBox.GetMin(), a_BoundingBox.GetMax());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPawn::FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, Vector3i a_Centre, const int a_HalfCubeWidth)
|
||||
{
|
||||
Vector3i MinCorner(a_Centre.x - a_HalfCubeWidth, a_Centre.y - a_HalfCubeWidth, a_Centre.z - a_HalfCubeWidth);
|
||||
Vector3i MaxCorner(a_Centre.x + a_HalfCubeWidth, a_Centre.y + a_HalfCubeWidth, a_Centre.z + a_HalfCubeWidth);
|
||||
return FindTeleportDestination(a_World, a_HeightRequired, a_NumTries, a_Destination, MinCorner, MaxCorner);
|
||||
}
|
||||
|
@ -70,6 +70,21 @@ public:
|
||||
/** Returns the entity effect, if it is currently applied or nullptr if not. */
|
||||
cEntityEffect * GetEntityEffect(cEntityEffect::eType a_EffectType) const;
|
||||
|
||||
// tolua_begin
|
||||
|
||||
static bool FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, const Vector3i a_MinBoxCorner, const Vector3i a_MaxBoxCorner);
|
||||
|
||||
static bool FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, const cBoundingBox a_BoundingBox);
|
||||
|
||||
/** Used by enderman and chorus fruit.
|
||||
Checks for valid destinations in a cube of length 2 * a_HalfCubeWidth centred at a_Centre.
|
||||
Returns true and places destination in a_Destination if successful.
|
||||
Returns false if destination could be found after a_NumTries attempts.
|
||||
Details at: https://minecraft.fandom.com/wiki/Enderman#Teleportation. */
|
||||
static bool FindTeleportDestination(cWorld & a_World, const int a_HeightRequired, const unsigned int a_NumTries, Vector3d & a_Destination, Vector3i a_Centre, const int a_HalfCubeWidth);
|
||||
|
||||
// tolua_end
|
||||
|
||||
protected:
|
||||
|
||||
typedef std::map<cEntityEffect::eType, std::unique_ptr<cEntityEffect>> tEffectMap;
|
||||
|
@ -15,6 +15,7 @@ target_sources(
|
||||
ItemBucket.h
|
||||
ItemButton.h
|
||||
ItemChest.h
|
||||
ItemChorusFruit.h
|
||||
ItemCloth.h
|
||||
ItemComparator.h
|
||||
ItemCookedFish.h
|
||||
|
44
src/Items/ItemChorusFruit.h
Normal file
44
src/Items/ItemChorusFruit.h
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ItemFood.h"
|
||||
#include "../Entities/Pawn.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cItemChorusFruitHandler:
|
||||
public cItemFoodHandler
|
||||
{
|
||||
using Super = cItemFoodHandler;
|
||||
|
||||
public:
|
||||
|
||||
cItemChorusFruitHandler():
|
||||
Super(E_ITEM_CHORUS_FRUIT, FoodInfo(4, 2.4))
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
|
||||
{
|
||||
cItemHandler::EatItem(a_Player, a_Item);
|
||||
|
||||
if (!a_Player->IsGameModeCreative())
|
||||
{
|
||||
a_Player->GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
|
||||
// Attempt to find a teleport destination
|
||||
Vector3d Destination;
|
||||
cWorld * World = a_Player->GetWorld();
|
||||
if (cPawn::FindTeleportDestination(*World, 2, 16, Destination, a_Player->GetPosition(), 8))
|
||||
{
|
||||
// Broadcast sound effect to _pre-teleport_ location, then teleport player.
|
||||
World->BroadcastSoundEffect("item.chorus_fruit.teleport", a_Player->GetPosition(), 1, 1);
|
||||
a_Player->TeleportToCoords(Destination.x, Destination.y, Destination.z);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
@ -20,6 +20,7 @@
|
||||
#include "ItemBucket.h"
|
||||
#include "ItemButton.h"
|
||||
#include "ItemChest.h"
|
||||
#include "ItemChorusFruit.h"
|
||||
#include "ItemCloth.h"
|
||||
#include "ItemComparator.h"
|
||||
#include "ItemCookedFish.h"
|
||||
@ -354,7 +355,6 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
|
||||
case E_ITEM_BAKED_POTATO: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6));
|
||||
case E_ITEM_BEETROOT: return new cItemFoodHandler(a_ItemType, FoodInfo(1, 1.2));
|
||||
case E_ITEM_BREAD: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6));
|
||||
case E_ITEM_CHORUS_FRUIT: return new cItemFoodHandler(a_ItemType, FoodInfo(4, 2.4));
|
||||
case E_ITEM_COOKED_CHICKEN: return new cItemFoodHandler(a_ItemType, FoodInfo(6, 7.2));
|
||||
case E_ITEM_COOKED_MUTTON: return new cItemFoodHandler(a_ItemType, FoodInfo(6, 9.6));
|
||||
case E_ITEM_COOKED_PORKCHOP: return new cItemFoodHandler(a_ItemType, FoodInfo(8, 12.8));
|
||||
@ -371,6 +371,7 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
|
||||
case E_ITEM_STEAK: return new cItemFoodHandler(a_ItemType, FoodInfo(8, 12.8));
|
||||
|
||||
// Special-case food with their own handler
|
||||
case E_ITEM_CHORUS_FRUIT: return new cItemChorusFruitHandler();
|
||||
case E_ITEM_COOKED_FISH: return new cItemCookedFishHandler();
|
||||
case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler();
|
||||
case E_ITEM_POISONOUS_POTATO: return new cItemPoisonousPotatoHandler();
|
||||
|
Loading…
Reference in New Issue
Block a user