Nameplate and chat bubble improvements
They should stack much better now. I also hooked up the true mouth position to the name-tag, but it's too shaky on chat bubbles.
This commit is contained in:
parent
94c84ad013
commit
63b68b3a55
@ -3,7 +3,8 @@
|
|||||||
"showMasterNames" : true,
|
"showMasterNames" : true,
|
||||||
"fontDirectives" : "?border=1;222;2220",
|
"fontDirectives" : "?border=1;222;2220",
|
||||||
"inspectOpacityRate" : 0.15,
|
"inspectOpacityRate" : 0.15,
|
||||||
"movementThreshold" : 0.5
|
"movementThreshold" : 0.5,
|
||||||
|
"offset" : [0, 13]
|
||||||
},
|
},
|
||||||
"font" : {
|
"font" : {
|
||||||
"defaultDirectives" : "",
|
"defaultDirectives" : "",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"movementThreshold" : 0.5
|
"movementThreshold" : 0.5,
|
||||||
|
"bubbleOffset" : [0, 1.875]
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#include "StarChatBubbleSeparation.hpp"
|
#include "StarChatBubbleSeparation.hpp"
|
||||||
|
#include "StarLogging.hpp"
|
||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ bool compareOverlapRight(RectF const& newBox, RectF const& fixedBox) {
|
|||||||
template <typename Compare>
|
template <typename Compare>
|
||||||
void appendHorizontalOverlaps(List<RectF>& overlaps,
|
void appendHorizontalOverlaps(List<RectF>& overlaps,
|
||||||
List<RectF> const& boxes,
|
List<RectF> const& boxes,
|
||||||
List<RectF>::iterator leftBound,
|
List<RectF>::const_iterator leftBound,
|
||||||
Compare compare,
|
Compare compare,
|
||||||
RectF const& box) {
|
RectF const& box) {
|
||||||
auto i = leftBound;
|
auto i = leftBound;
|
||||||
@ -36,7 +37,7 @@ void appendHorizontalOverlaps(List<RectF>& overlaps,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RectF separateBubble(List<RectF>& sortedLeftEdges, List<RectF>& sortedRightEdges, RectF box) {
|
RectF separateBubble(List<RectF> const& sortedLeftEdges, List<RectF> const& sortedRightEdges, List<RectF>& outLeftEdges, List<RectF>& outRightEdges, RectF box) {
|
||||||
// We have to maintain two lists of boxes: one sorted by the left edges and
|
// We have to maintain two lists of boxes: one sorted by the left edges and
|
||||||
// one
|
// one
|
||||||
// by the right edges. This is because boxes can be different sizes, and
|
// by the right edges. This is because boxes can be different sizes, and
|
||||||
@ -54,19 +55,33 @@ RectF separateBubble(List<RectF>& sortedLeftEdges, List<RectF>& sortedRightEdges
|
|||||||
// overlap with 'box'.
|
// overlap with 'box'.
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// While box is overlapping any other boxes, raise its Y value.
|
// While box is overlapping any other boxes, move it halfway away.
|
||||||
List<RectF> overlappingBoxes =
|
List<RectF> overlappingBoxes = horizontalOverlaps.filtered([&box](RectF const& overlapper) {
|
||||||
horizontalOverlaps.filtered([&box](RectF const& overlapper) { return box.intersects(overlapper, false); });
|
if (overlapper.intersects(box, false)) {
|
||||||
|
Vec2F oSize = overlapper.size(), bSize = box.size();
|
||||||
|
if (oSize[0] == bSize[0]) {
|
||||||
|
if (oSize[1] == bSize[1])
|
||||||
|
return overlapper.center()[1] <= box.center()[1];
|
||||||
|
else
|
||||||
|
return oSize[1] > bSize[1];
|
||||||
|
}
|
||||||
|
else if (oSize[0] > bSize[0])
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
if (overlappingBoxes.empty())
|
if (overlappingBoxes.empty())
|
||||||
break;
|
break;
|
||||||
RectF overlapBoundBox = fold(overlappingBoxes, box, [](RectF const& a, RectF const& b) { return a.combined(b); });
|
RectF overlapBoundBox = fold(overlappingBoxes, box, [](RectF const& a, RectF const& b) { return a.combined(b); });
|
||||||
|
SpatialLogger::logPoly("screen", PolyF(box), { 255, 0, 0, 255 });
|
||||||
|
SpatialLogger::logPoly("screen", PolyF(overlapBoundBox), { 0, 0, 255, 255 });
|
||||||
auto height = box.height();
|
auto height = box.height();
|
||||||
box.setYMin(overlapBoundBox.yMax());
|
box.setYMin(overlapBoundBox.yMax());
|
||||||
box.setYMax(box.yMin() + height);
|
box.setYMax(box.yMin() + height);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedLeftEdges.insertSorted(box, compareLeft);
|
outLeftEdges.append(box);
|
||||||
sortedRightEdges.insertSorted(box, compareRight);
|
outRightEdges.append(box);
|
||||||
|
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,16 @@ struct BubbleState {
|
|||||||
Vec2F currentDestination;
|
Vec2F currentDestination;
|
||||||
// The bound box of the nametag if it was at the destination.
|
// The bound box of the nametag if it was at the destination.
|
||||||
RectF boundBox;
|
RectF boundBox;
|
||||||
// The position for the bubble chosen by the algorithm (which it may not
|
// The separation offset for the bubble chosen by the algorithm (which it may not
|
||||||
// have fully moved to yet).
|
// have fully moved to yet).
|
||||||
Vec2F separatedPosition;
|
Vec2F seperatedOffset;
|
||||||
// The bound box of the bubble around the separatedPosition.
|
// The bound box of the bubble around the separatedOffset.
|
||||||
RectF separatedBox;
|
RectF separatedBox;
|
||||||
// Where the bubble is now, which could be anywhere en route to the
|
// Where the bubble offset is now, which could be anywhere en route to the
|
||||||
// separatedPosition.
|
// seperatedOffset.
|
||||||
|
Vec2F currentOffset;
|
||||||
|
|
||||||
|
// The final position.
|
||||||
Vec2F currentPosition;
|
Vec2F currentPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,6 +66,11 @@ private:
|
|||||||
List<RectF> m_sortedRightEdges;
|
List<RectF> m_sortedRightEdges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool compareLeft(RectF const& a, RectF const& b);
|
||||||
|
bool compareRight(RectF const& a, RectF const& b);
|
||||||
|
bool compareOverlapLeft(RectF const& newBox, RectF const& fixedBox);
|
||||||
|
bool compareOverlapRight(RectF const& newBox, RectF const& fixedBox);
|
||||||
|
|
||||||
// Shifts box upwards until it is not overlapping any of the boxes in
|
// Shifts box upwards until it is not overlapping any of the boxes in
|
||||||
// sortedLeftEdges
|
// sortedLeftEdges
|
||||||
// and sortedRightEdges.
|
// and sortedRightEdges.
|
||||||
@ -71,7 +79,7 @@ private:
|
|||||||
// The two lists contain all the chat bubbles that have been separated, sorted
|
// The two lists contain all the chat bubbles that have been separated, sorted
|
||||||
// by
|
// by
|
||||||
// the X positions of their left and right edges respectively.
|
// the X positions of their left and right edges respectively.
|
||||||
RectF separateBubble(List<RectF>& sortedLeftEdges, List<RectF>& sortedRightEdges, RectF box);
|
RectF separateBubble(List<RectF> const& sortedLeftEdges, List<RectF> const& sortedRightEdges, List<RectF>& outLeftEdges, List<RectF>& outRightEdges, RectF box);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
BubbleSeparator<T>::BubbleSeparator(float tweenFactor, float movementThreshold)
|
BubbleSeparator<T>::BubbleSeparator(float tweenFactor, float movementThreshold)
|
||||||
@ -100,9 +108,10 @@ void BubbleSeparator<T>::setMovementThreshold(float movementThreshold) {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void BubbleSeparator<T>::addBubble(Vec2F position, RectF boundBox, T contents, unsigned margin) {
|
void BubbleSeparator<T>::addBubble(Vec2F position, RectF boundBox, T contents, unsigned margin) {
|
||||||
boundBox.setYMax(boundBox.yMax() + margin);
|
boundBox.setYMax(boundBox.yMax() + margin);
|
||||||
RectF separated = separateBubble(m_sortedLeftEdges, m_sortedRightEdges, boundBox);
|
RectF separated = separateBubble(m_sortedLeftEdges, m_sortedRightEdges, m_sortedLeftEdges, m_sortedRightEdges, boundBox);
|
||||||
Vec2F separatedPosition = position + separated.min() - boundBox.min();
|
Vec2F separatedOffset = separated.min() - boundBox.min();
|
||||||
Bubble bubble = Bubble{contents, position, position, boundBox, separatedPosition, separated, separatedPosition};
|
Vec2F separatedPosition = position + separatedOffset;
|
||||||
|
Bubble bubble = Bubble{ contents, position, position, boundBox, separatedOffset, separated, separatedOffset, separatedPosition };
|
||||||
m_bubbles.insertSorted(move(bubble), &BubbleSeparator<T>::compareBubbleY);
|
m_bubbles.insertSorted(move(bubble), &BubbleSeparator<T>::compareBubbleY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,26 +135,36 @@ List<BubbleState<T>> BubbleSeparator<T>::filtered(function<bool(Bubble const&, T
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void BubbleSeparator<T>::forEach(function<void(Bubble&, T&)> func) {
|
void BubbleSeparator<T>::forEach(function<void(Bubble&, T&)> func) {
|
||||||
bool anyMoved = false;
|
bool anyMoved = false;
|
||||||
m_bubbles.exec([this, func, &anyMoved](Bubble& bubble) {
|
|
||||||
|
List<Box<float, 2>> leftEdges = move(m_sortedLeftEdges);
|
||||||
|
List<Box<float, 2>> rightEdges = move(m_sortedRightEdges);
|
||||||
|
|
||||||
|
m_bubbles.exec([this, func, &anyMoved, &leftEdges, &rightEdges](Bubble& bubble) {
|
||||||
RectF oldBoundBox = bubble.boundBox;
|
RectF oldBoundBox = bubble.boundBox;
|
||||||
|
|
||||||
func(bubble, bubble.contents);
|
func(bubble, bubble.contents);
|
||||||
|
|
||||||
|
// Kae: I'm disabling the movement threshold check for now because it also
|
||||||
|
// stops bubble sorting on bubbles that haven't moved, which causes problems.
|
||||||
|
|
||||||
Vec2F sizeDelta = bubble.boundBox.size() - oldBoundBox.size();
|
Vec2F sizeDelta = bubble.boundBox.size() - oldBoundBox.size();
|
||||||
Vec2F positionDelta = bubble.idealDestination - bubble.currentDestination;
|
leftEdges.remove(bubble.separatedBox);
|
||||||
if (sizeDelta.magnitude() > m_movementThreshold || positionDelta.magnitude() > m_movementThreshold) {
|
rightEdges.remove(bubble.separatedBox);
|
||||||
m_sortedLeftEdges.remove(bubble.separatedBox);
|
RectF boundBox = RectF::withCenter(bubble.idealDestination, bubble.boundBox.size());
|
||||||
m_sortedRightEdges.remove(bubble.separatedBox);
|
RectF separated = separateBubble(leftEdges, rightEdges, m_sortedLeftEdges, m_sortedRightEdges, boundBox);
|
||||||
RectF boundBox = bubble.boundBox.translated(positionDelta);
|
leftEdges.insertSorted(bubble.separatedBox, compareLeft);
|
||||||
RectF separated = separateBubble(m_sortedLeftEdges, m_sortedRightEdges, boundBox);
|
rightEdges.insertSorted(bubble.separatedBox, compareRight);
|
||||||
anyMoved = true;
|
|
||||||
bubble.currentDestination = bubble.idealDestination;
|
anyMoved = true;
|
||||||
bubble.boundBox = boundBox;
|
bubble.currentDestination = bubble.idealDestination;
|
||||||
bubble.separatedPosition = bubble.idealDestination + separated.min() - boundBox.min();
|
bubble.boundBox = boundBox;
|
||||||
bubble.separatedBox = separated;
|
bubble.seperatedOffset = separated.min() - boundBox.min();
|
||||||
bubble.currentPosition += positionDelta;
|
bubble.separatedBox = separated;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_sortedLeftEdges.sort(compareLeft);
|
||||||
|
m_sortedRightEdges.sort(compareRight);
|
||||||
|
|
||||||
if (anyMoved)
|
if (anyMoved)
|
||||||
m_bubbles.sort(&BubbleSeparator<T>::compareBubbleY);
|
m_bubbles.sort(&BubbleSeparator<T>::compareBubbleY);
|
||||||
}
|
}
|
||||||
@ -153,8 +172,10 @@ void BubbleSeparator<T>::forEach(function<void(Bubble&, T&)> func) {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void BubbleSeparator<T>::update() {
|
void BubbleSeparator<T>::update() {
|
||||||
m_bubbles.exec([this](Bubble& bubble) {
|
m_bubbles.exec([this](Bubble& bubble) {
|
||||||
Vec2F delta = bubble.separatedPosition - bubble.currentPosition;
|
Vec2F delta = bubble.seperatedOffset - bubble.currentOffset;
|
||||||
bubble.currentPosition += m_tweenFactor * delta;
|
bubble.currentOffset += m_tweenFactor * delta;
|
||||||
|
|
||||||
|
bubble.currentPosition = bubble.currentDestination + bubble.currentOffset;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ void NameplatePainter::update(WorldClientPtr const& world, WorldCamera const& ca
|
|||||||
|
|
||||||
m_nametags.forEach([&world, &camera, this, inspectionMode](BubbleState<Nametag>& bubbleState, Nametag& nametag) {
|
m_nametags.forEach([&world, &camera, this, inspectionMode](BubbleState<Nametag>& bubbleState, Nametag& nametag) {
|
||||||
if (auto entity = as<NametagEntity>(world->entity(nametag.entityId))) {
|
if (auto entity = as<NametagEntity>(world->entity(nametag.entityId))) {
|
||||||
bubbleState.idealDestination = camera.worldToScreen(entity->position()) + m_offset * camera.pixelRatio();
|
bubbleState.idealDestination = camera.worldToScreen(entity->nametagOrigin()) + m_offset * camera.pixelRatio();
|
||||||
bubbleState.boundBox = determineBoundBox(bubbleState.idealDestination, nametag);
|
bubbleState.boundBox = determineBoundBox(bubbleState.idealDestination, nametag);
|
||||||
|
|
||||||
nametag.statusText = entity->statusText();
|
nametag.statusText = entity->statusText();
|
||||||
|
@ -810,6 +810,10 @@ Vec3B Monster::nametagColor() const {
|
|||||||
return m_monsterVariant.nametagColor;
|
return m_monsterVariant.nametagColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec2F Monster::nametagOrigin() const {
|
||||||
|
return mouthPosition(false);
|
||||||
|
}
|
||||||
|
|
||||||
bool Monster::aggressive() const {
|
bool Monster::aggressive() const {
|
||||||
return m_aggressive;
|
return m_aggressive;
|
||||||
}
|
}
|
||||||
@ -826,6 +830,10 @@ Vec2F Monster::mouthPosition() const {
|
|||||||
return mouthOffset() + position();
|
return mouthOffset() + position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec2F Monster::mouthPosition(bool) const {
|
||||||
|
return mouthPosition();
|
||||||
|
}
|
||||||
|
|
||||||
List<ChatAction> Monster::pullPendingChatActions() {
|
List<ChatAction> Monster::pullPendingChatActions() {
|
||||||
return std::move(m_pendingChatActions);
|
return std::move(m_pendingChatActions);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ public:
|
|||||||
Maybe<String> statusText() const override;
|
Maybe<String> statusText() const override;
|
||||||
bool displayNametag() const override;
|
bool displayNametag() const override;
|
||||||
Vec3B nametagColor() const override;
|
Vec3B nametagColor() const override;
|
||||||
|
Vec2F nametagOrigin() const override;
|
||||||
|
|
||||||
bool aggressive() const override;
|
bool aggressive() const override;
|
||||||
|
|
||||||
@ -118,6 +119,7 @@ public:
|
|||||||
Maybe<LuaValue> evalScript(String const& code) override;
|
Maybe<LuaValue> evalScript(String const& code) override;
|
||||||
|
|
||||||
virtual Vec2F mouthPosition() const override;
|
virtual Vec2F mouthPosition() const override;
|
||||||
|
virtual Vec2F mouthPosition(bool ignoreAdjustments) const override;
|
||||||
virtual List<ChatAction> pullPendingChatActions() override;
|
virtual List<ChatAction> pullPendingChatActions() override;
|
||||||
|
|
||||||
List<PhysicsForceRegion> forceRegions() const override;
|
List<PhysicsForceRegion> forceRegions() const override;
|
||||||
|
@ -216,9 +216,9 @@ RectF Npc::metaBoundBox() const {
|
|||||||
return RectF(-4, -4, 4, 4);
|
return RectF(-4, -4, 4, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Npc::mouthOffset() const {
|
Vec2F Npc::mouthOffset(bool ignoreAdjustments) const {
|
||||||
return Vec2F{m_humanoid.mouthOffset(true)[0] * numericalDirection(m_humanoid.facingDirection()),
|
return Vec2F{m_humanoid.mouthOffset(ignoreAdjustments)[0] * numericalDirection(m_humanoid.facingDirection()),
|
||||||
m_humanoid.mouthOffset(true)[1]};
|
m_humanoid.mouthOffset(ignoreAdjustments)[1]};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Npc::feetOffset() const {
|
Vec2F Npc::feetOffset() const {
|
||||||
@ -508,6 +508,10 @@ Vec3B Npc::nametagColor() const {
|
|||||||
return m_npcVariant.nametagColor;
|
return m_npcVariant.nametagColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec2F Npc::nametagOrigin() const {
|
||||||
|
return mouthPosition(false);
|
||||||
|
}
|
||||||
|
|
||||||
bool Npc::aggressive() const {
|
bool Npc::aggressive() const {
|
||||||
return m_aggressive.get();
|
return m_aggressive.get();
|
||||||
}
|
}
|
||||||
@ -804,7 +808,11 @@ void Npc::getNetStates(bool initial) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Npc::mouthPosition() const {
|
Vec2F Npc::mouthPosition() const {
|
||||||
return mouthOffset() + position();
|
return mouthOffset(true) + position();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2F Npc::mouthPosition(bool ignoreAdjustments) const {
|
||||||
|
return mouthOffset(ignoreAdjustments) + position();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ChatAction> Npc::pullPendingChatActions() {
|
List<ChatAction> Npc::pullPendingChatActions() {
|
||||||
|
@ -57,7 +57,7 @@ public:
|
|||||||
Vec2F position() const override;
|
Vec2F position() const override;
|
||||||
RectF metaBoundBox() const override;
|
RectF metaBoundBox() const override;
|
||||||
|
|
||||||
Vec2F mouthOffset() const;
|
Vec2F mouthOffset(bool ignoreAdjustments = true) const;
|
||||||
Vec2F feetOffset() const;
|
Vec2F feetOffset() const;
|
||||||
Vec2F headArmorOffset() const;
|
Vec2F headArmorOffset() const;
|
||||||
Vec2F chestArmorOffset() const;
|
Vec2F chestArmorOffset() const;
|
||||||
@ -105,6 +105,7 @@ public:
|
|||||||
Maybe<String> statusText() const override;
|
Maybe<String> statusText() const override;
|
||||||
bool displayNametag() const override;
|
bool displayNametag() const override;
|
||||||
Vec3B nametagColor() const override;
|
Vec3B nametagColor() const override;
|
||||||
|
Vec2F nametagOrigin() const override;
|
||||||
|
|
||||||
bool aggressive() const;
|
bool aggressive() const;
|
||||||
|
|
||||||
@ -112,6 +113,7 @@ public:
|
|||||||
Maybe<LuaValue> evalScript(String const& code) override;
|
Maybe<LuaValue> evalScript(String const& code) override;
|
||||||
|
|
||||||
Vec2F mouthPosition() const override;
|
Vec2F mouthPosition() const override;
|
||||||
|
Vec2F mouthPosition(bool ignoreAdjustments) const override;
|
||||||
List<ChatAction> pullPendingChatActions() override;
|
List<ChatAction> pullPendingChatActions() override;
|
||||||
|
|
||||||
bool isInteractive() const override;
|
bool isInteractive() const override;
|
||||||
|
@ -1193,6 +1193,10 @@ Vec2F Object::mouthPosition() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec2F Object::mouthPosition(bool) const {
|
||||||
|
return mouthPosition();
|
||||||
|
}
|
||||||
|
|
||||||
List<ChatAction> Object::pullPendingChatActions() {
|
List<ChatAction> Object::pullPendingChatActions() {
|
||||||
return std::move(m_pendingChatActions);
|
return std::move(m_pendingChatActions);
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ public:
|
|||||||
Maybe<LuaValue> evalScript(String const& code) override;
|
Maybe<LuaValue> evalScript(String const& code) override;
|
||||||
|
|
||||||
virtual Vec2F mouthPosition() const override;
|
virtual Vec2F mouthPosition() const override;
|
||||||
|
virtual Vec2F mouthPosition(bool ignoreAdjustments) const override;
|
||||||
virtual List<ChatAction> pullPendingChatActions() override;
|
virtual List<ChatAction> pullPendingChatActions() override;
|
||||||
|
|
||||||
void breakObject(bool smash = true);
|
void breakObject(bool smash = true);
|
||||||
|
@ -580,9 +580,9 @@ Vec2F Player::velocity() const {
|
|||||||
return m_movementController->velocity();
|
return m_movementController->velocity();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Player::mouthOffset() const {
|
Vec2F Player::mouthOffset(bool ignoreAdjustments) const {
|
||||||
return Vec2F(
|
return Vec2F(
|
||||||
m_humanoid->mouthOffset(true)[0] * numericalDirection(facingDirection()), m_humanoid->mouthOffset(true)[1]);
|
m_humanoid->mouthOffset(ignoreAdjustments)[0] * numericalDirection(facingDirection()), m_humanoid->mouthOffset(ignoreAdjustments)[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Player::feetOffset() const {
|
Vec2F Player::feetOffset() const {
|
||||||
@ -610,7 +610,11 @@ Vec2F Player::legsArmorOffset() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec2F Player::mouthPosition() const {
|
Vec2F Player::mouthPosition() const {
|
||||||
return position() + mouthOffset();
|
return position() + mouthOffset(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2F Player::mouthPosition(bool ignoreAdjustments) const {
|
||||||
|
return position() + mouthOffset(ignoreAdjustments);
|
||||||
}
|
}
|
||||||
|
|
||||||
RectF Player::collisionArea() const {
|
RectF Player::collisionArea() const {
|
||||||
@ -1933,6 +1937,10 @@ Vec3B Player::nametagColor() const {
|
|||||||
return jsonToVec3B(assets->json("/player.config:nametagColor"));
|
return jsonToVec3B(assets->json("/player.config:nametagColor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec2F Player::nametagOrigin() const {
|
||||||
|
return mouthPosition(false);
|
||||||
|
}
|
||||||
|
|
||||||
void Player::setBodyDirectives(String const& directives) {
|
void Player::setBodyDirectives(String const& directives) {
|
||||||
m_identity.bodyDirectives = directives;
|
m_identity.bodyDirectives = directives;
|
||||||
m_identityUpdated = true;
|
m_identityUpdated = true;
|
||||||
|
@ -86,7 +86,8 @@ public:
|
|||||||
Vec2F velocity() const override;
|
Vec2F velocity() const override;
|
||||||
|
|
||||||
Vec2F mouthPosition() const override;
|
Vec2F mouthPosition() const override;
|
||||||
Vec2F mouthOffset() const;
|
Vec2F mouthPosition(bool ignoreAdjustments) const override;
|
||||||
|
Vec2F mouthOffset(bool ignoreAdjustments = true) const;
|
||||||
Vec2F feetOffset() const;
|
Vec2F feetOffset() const;
|
||||||
Vec2F headArmorOffset() const;
|
Vec2F headArmorOffset() const;
|
||||||
Vec2F chestArmorOffset() const;
|
Vec2F chestArmorOffset() const;
|
||||||
@ -284,6 +285,7 @@ public:
|
|||||||
Maybe<String> statusText() const override;
|
Maybe<String> statusText() const override;
|
||||||
bool displayNametag() const override;
|
bool displayNametag() const override;
|
||||||
Vec3B nametagColor() const override;
|
Vec3B nametagColor() const override;
|
||||||
|
Vec2F nametagOrigin() const override;
|
||||||
|
|
||||||
void setBodyDirectives(String const& directives);
|
void setBodyDirectives(String const& directives);
|
||||||
void setHairType(String const& group, String const& type);
|
void setHairType(String const& group, String const& type);
|
||||||
|
@ -10,7 +10,8 @@ STAR_CLASS(ChattyEntity);
|
|||||||
|
|
||||||
class ChattyEntity : public virtual Entity {
|
class ChattyEntity : public virtual Entity {
|
||||||
public:
|
public:
|
||||||
virtual Vec2F mouthPosition() const = 0;
|
virtual Vec2F mouthPosition() const { return mouthPosition(true); };
|
||||||
|
virtual Vec2F mouthPosition(bool) const = 0;
|
||||||
virtual List<ChatAction> pullPendingChatActions() = 0;
|
virtual List<ChatAction> pullPendingChatActions() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ public:
|
|||||||
virtual Maybe<String> statusText() const = 0;
|
virtual Maybe<String> statusText() const = 0;
|
||||||
virtual bool displayNametag() const = 0;
|
virtual bool displayNametag() const = 0;
|
||||||
virtual Vec3B nametagColor() const = 0;
|
virtual Vec3B nametagColor() const = 0;
|
||||||
|
virtual Vec2F nametagOrigin() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user