Head Rotation
(way too hardcoded, not ideal but it will do in the meantime as many have been asking for it)
This commit is contained in:
parent
3df5cb78da
commit
0a1a82b18b
@ -54,6 +54,24 @@
|
||||
|
||||
"instrumentSlider" : { "type" : "slider", "position" : [62, 158], "gridImage" : "/interface/optionsmenu/largeselection.png" },
|
||||
"instrumentLabel" : { "type" : "label", "position" : [32, 158], "value" : "Tunes" },
|
||||
"instrumentValueLabel" : { "type" : "label", "position" : [192, 158], "hAnchor" : "mid", "value" : "Replace Me" }
|
||||
"instrumentValueLabel" : { "type" : "label", "position" : [192, 158], "hAnchor" : "mid", "value" : "Replace Me" },
|
||||
|
||||
"headRotationLabel" : {
|
||||
"type" : "label",
|
||||
"position" : [25, 51],
|
||||
"hAnchor" : "left",
|
||||
"value" : "HEAD ROTATION"
|
||||
},
|
||||
"headRotationCheckbox" : {
|
||||
"type" : "button",
|
||||
"pressedOffset" : [0, 0],
|
||||
"position" : [104, 51],
|
||||
"base" : "/interface/optionsmenu/checkboxnocheck.png",
|
||||
"hover" : "/interface/optionsmenu/checkboxnocheckhover.png",
|
||||
"baseImageChecked" : "/interface/optionsmenu/checkboxcheck.png",
|
||||
"hoverImageChecked" : "/interface/optionsmenu/checkboxcheckhover.png",
|
||||
"checkable" : true,
|
||||
"checked" : true
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
#include "StarVoiceSettingsMenu.hpp"
|
||||
#include "StarBindingsMenu.hpp"
|
||||
#include "StarGraphicsMenu.hpp"
|
||||
#include "StarHumanoid.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -47,6 +48,9 @@ OptionsMenu::OptionsMenu(PaneManager* manager, UniverseClientPtr client)
|
||||
reader.registerCallback("allowAssetsMismatchCheckbox", [=](Widget*) {
|
||||
updateAllowAssetsMismatch();
|
||||
});
|
||||
reader.registerCallback("headRotationCheckbox", [=](Widget*) {
|
||||
updateHeadRotation();
|
||||
});
|
||||
reader.registerCallback("backButton", [=](Widget*) {
|
||||
dismiss();
|
||||
});
|
||||
@ -77,6 +81,7 @@ OptionsMenu::OptionsMenu(PaneManager* manager, UniverseClientPtr client)
|
||||
m_clientIPJoinableButton = fetchChild<ButtonWidget>("clientIPJoinableCheckbox");
|
||||
m_clientP2PJoinableButton = fetchChild<ButtonWidget>("clientP2PJoinableCheckbox");
|
||||
m_allowAssetsMismatchButton = fetchChild<ButtonWidget>("allowAssetsMismatchCheckbox");
|
||||
m_headRotationButton = fetchChild<ButtonWidget>("headRotationCheckbox");
|
||||
|
||||
m_instrumentLabel = fetchChild<LabelWidget>("instrumentValueLabel");
|
||||
m_sfxLabel = fetchChild<LabelWidget>("sfxValueLabel");
|
||||
@ -115,7 +120,8 @@ StringList const OptionsMenu::ConfigKeys = {
|
||||
"tutorialMessages",
|
||||
"clientIPJoinable",
|
||||
"clientP2PJoinable",
|
||||
"allowAssetsMismatch"
|
||||
"allowAssetsMismatch",
|
||||
"humanoidHeadRotation"
|
||||
};
|
||||
|
||||
void OptionsMenu::initConfig() {
|
||||
@ -166,6 +172,12 @@ void OptionsMenu::updateAllowAssetsMismatch() {
|
||||
Root::singleton().configuration()->set("allowAssetsMismatch", m_allowAssetsMismatchButton->isChecked());
|
||||
}
|
||||
|
||||
void OptionsMenu::updateHeadRotation() {
|
||||
m_localChanges.set("humanoidHeadRotation", m_headRotationButton->isChecked());
|
||||
Root::singleton().configuration()->set("humanoidHeadRotation", m_headRotationButton->isChecked());
|
||||
Humanoid::globalHeadRotation() = m_headRotationButton->isChecked();
|
||||
}
|
||||
|
||||
void OptionsMenu::syncGuiToConf() {
|
||||
m_instrumentSlider->setVal(m_localChanges.get("instrumentVol").toInt(), false);
|
||||
m_instrumentLabel->setText(toString(m_instrumentSlider->val()));
|
||||
@ -180,6 +192,7 @@ void OptionsMenu::syncGuiToConf() {
|
||||
m_clientIPJoinableButton->setChecked(m_localChanges.get("clientIPJoinable").toBool());
|
||||
m_clientP2PJoinableButton->setChecked(m_localChanges.get("clientP2PJoinable").toBool());
|
||||
m_allowAssetsMismatchButton->setChecked(m_localChanges.get("allowAssetsMismatch").toBool());
|
||||
m_headRotationButton->setChecked(m_localChanges.get("humanoidHeadRotation").optBool().value(true));
|
||||
|
||||
auto appController = GuiContext::singleton().applicationController();
|
||||
if (!appController->p2pNetworkingService()) {
|
||||
|
@ -36,6 +36,7 @@ private:
|
||||
void updateClientIPJoinable();
|
||||
void updateClientP2PJoinable();
|
||||
void updateAllowAssetsMismatch();
|
||||
void updateHeadRotation();
|
||||
|
||||
void syncGuiToConf();
|
||||
|
||||
@ -52,6 +53,7 @@ private:
|
||||
ButtonWidgetPtr m_clientIPJoinableButton;
|
||||
ButtonWidgetPtr m_clientP2PJoinableButton;
|
||||
ButtonWidgetPtr m_allowAssetsMismatchButton;
|
||||
ButtonWidgetPtr m_headRotationButton;
|
||||
|
||||
LabelWidgetPtr m_instrumentLabel;
|
||||
LabelWidgetPtr m_sfxLabel;
|
||||
|
@ -242,6 +242,14 @@ EnumMap<Humanoid::State> const Humanoid::StateNames{
|
||||
{Humanoid::State::Lay, "lay"},
|
||||
};
|
||||
|
||||
// gross, but I don't want to make config calls more than I need to
|
||||
bool& Humanoid::globalHeadRotation(Maybe<bool> default) {
|
||||
static Maybe<bool> s_headRotation;
|
||||
if (!s_headRotation)
|
||||
s_headRotation = Root::singleton().configuration()->get("humanoidHeadRotation").optBool().value(true);
|
||||
return *s_headRotation;
|
||||
};
|
||||
|
||||
Humanoid::Humanoid(Json const& config) {
|
||||
loadConfig(config);
|
||||
|
||||
@ -252,7 +260,7 @@ Humanoid::Humanoid(Json const& config) {
|
||||
m_movingBackwards = false;
|
||||
m_altHand.angle = 0;
|
||||
m_facingDirection = Direction::Left;
|
||||
m_rotation = 0;
|
||||
m_headRotationTarget = m_headRotation = m_rotation = 0;
|
||||
m_scale = Vec2F::filled(1.f);
|
||||
m_drawVaporTrail = false;
|
||||
m_state = State::Idle;
|
||||
@ -412,6 +420,10 @@ void Humanoid::setMovingBackwards(bool movingBackwards) {
|
||||
m_movingBackwards = movingBackwards;
|
||||
}
|
||||
|
||||
void Humanoid::setHeadRotation(float headRotation) {
|
||||
m_headRotationTarget = headRotation;
|
||||
}
|
||||
|
||||
void Humanoid::setRotation(float rotation) {
|
||||
m_rotation = rotation;
|
||||
}
|
||||
@ -476,6 +488,10 @@ void Humanoid::setPrimaryHandNonRotatedDrawables(List<Drawable> drawables) {
|
||||
m_primaryHand.nonRotatedDrawables = std::move(drawables);
|
||||
}
|
||||
|
||||
bool Humanoid::primaryHandHoldingItem() const {
|
||||
return m_primaryHand.holdingItem;
|
||||
}
|
||||
|
||||
void Humanoid::setAltHandParameters(bool holdingItem, float angle, float itemAngle, bool recoil,
|
||||
bool outsideOfHand) {
|
||||
m_altHand.holdingItem = holdingItem;
|
||||
@ -498,16 +514,24 @@ void Humanoid::setAltHandNonRotatedDrawables(List<Drawable> drawables) {
|
||||
m_altHand.nonRotatedDrawables = std::move(drawables);
|
||||
}
|
||||
|
||||
bool Humanoid::altHandHoldingItem() const {
|
||||
return m_altHand.holdingItem;
|
||||
}
|
||||
|
||||
void Humanoid::animate(float dt) {
|
||||
m_animationTimer += dt;
|
||||
m_emoteAnimationTimer += dt;
|
||||
m_danceTimer += dt;
|
||||
float headRotationTarget = globalHeadRotation() ? m_headRotationTarget : 0.f;
|
||||
float diff = angleDiff(m_headRotation, headRotationTarget);
|
||||
m_headRotation = (headRotationTarget - (headRotationTarget - m_headRotation) * powf(.333333f, dt * 60.f));
|
||||
}
|
||||
|
||||
void Humanoid::resetAnimation() {
|
||||
m_animationTimer = 0.0f;
|
||||
m_emoteAnimationTimer = 0.0f;
|
||||
m_danceTimer = 0.0f;
|
||||
m_headRotation = globalHeadRotation() ? 0.f : m_headRotationTarget;
|
||||
}
|
||||
|
||||
List<Drawable> Humanoid::render(bool withItems, bool withRotationAndScale) {
|
||||
@ -641,11 +665,28 @@ List<Drawable> Humanoid::render(bool withItems, bool withRotationAndScale) {
|
||||
else if (m_state == Lay)
|
||||
headPosition += m_headLayOffset;
|
||||
|
||||
auto addHeadDrawable = [&](Drawable drawable, bool forceFullbright = false) {
|
||||
if (m_facingDirection == Direction::Left)
|
||||
drawable.scale(Vec2F(-1, 1));
|
||||
drawable.fullbright |= forceFullbright;
|
||||
if (m_headRotation != 0.f) {
|
||||
float dir = numericalDirection(m_facingDirection);
|
||||
Vec2F rotationPoint = headPosition;
|
||||
rotationPoint[0] *= dir;
|
||||
rotationPoint[1] -= .25f;
|
||||
float headX = (m_headRotation / ((float)Constants::pi * 2.f));
|
||||
drawable.rotate(m_headRotation, rotationPoint);
|
||||
drawable.position[0] -= state() == State::Run ? (fmaxf(headX * dir, 0.f) * 2.f) * dir : headX;
|
||||
drawable.position[1] -= fabsf(m_headRotation / ((float)Constants::pi * 4.f));
|
||||
}
|
||||
drawables.append(std::move(drawable));
|
||||
};
|
||||
|
||||
if (!m_headFrameset.empty() && !m_bodyHidden) {
|
||||
String image = strf("{}:normal", m_headFrameset);
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(getBodyDirectives(), true);
|
||||
addDrawable(std::move(drawable), m_bodyFullbright);
|
||||
addHeadDrawable(std::move(drawable), m_bodyFullbright);
|
||||
}
|
||||
|
||||
if (!m_emoteFrameset.empty() && !m_bodyHidden) {
|
||||
@ -653,14 +694,14 @@ List<Drawable> Humanoid::render(bool withItems, bool withRotationAndScale) {
|
||||
String image = strf("{}:{}.{}{}", m_emoteFrameset, emoteFrameBase(m_emoteState), emoteStateSeq, emoteDirectives.prefix());
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(emoteDirectives, true);
|
||||
addDrawable(std::move(drawable), m_bodyFullbright);
|
||||
addHeadDrawable(std::move(drawable), m_bodyFullbright);
|
||||
}
|
||||
|
||||
if (!m_hairFrameset.empty() && !m_bodyHidden) {
|
||||
String image = strf("{}:normal", m_hairFrameset);
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(getHairDirectives(), true).addDirectives(getHelmetMaskDirectives(), true);
|
||||
addDrawable(std::move(drawable), m_bodyFullbright);
|
||||
addHeadDrawable(std::move(drawable), m_bodyFullbright);
|
||||
}
|
||||
|
||||
if (!m_bodyFrameset.empty() && !m_bodyHidden) {
|
||||
@ -719,21 +760,21 @@ List<Drawable> Humanoid::render(bool withItems, bool withRotationAndScale) {
|
||||
String image = strf("{}:normal", m_facialHairFrameset);
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(getFacialHairDirectives(), true).addDirectives(getHelmetMaskDirectives(), true);
|
||||
addDrawable(std::move(drawable), m_bodyFullbright);
|
||||
addHeadDrawable(std::move(drawable), m_bodyFullbright);
|
||||
}
|
||||
|
||||
if (!m_facialMaskFrameset.empty() && !m_bodyHidden) {
|
||||
String image = strf("{}:normal", m_facialMaskFrameset);
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(getFacialMaskDirectives(), true).addDirectives(getHelmetMaskDirectives(), true);
|
||||
addDrawable(std::move(drawable));
|
||||
addHeadDrawable(std::move(drawable));
|
||||
}
|
||||
|
||||
if (!m_headArmorFrameset.empty()) {
|
||||
String image = strf("{}:normal{}", m_headArmorFrameset, m_headArmorDirectives.prefix());
|
||||
auto drawable = Drawable::makeImage(std::move(image), 1.0f / TilePixels, true, headPosition);
|
||||
drawable.imagePart().addDirectives(getHeadDirectives(), true);
|
||||
addDrawable(std::move(drawable));
|
||||
addHeadDrawable(std::move(drawable));
|
||||
}
|
||||
|
||||
auto frontArmDrawable = [&](String const& frameSet, Directives const& directives) -> Drawable {
|
||||
|
@ -100,6 +100,8 @@ public:
|
||||
};
|
||||
static EnumMap<State> const StateNames;
|
||||
|
||||
static bool& globalHeadRotation(Maybe<bool> default = {});
|
||||
|
||||
Humanoid(Json const& config);
|
||||
Humanoid(HumanoidIdentity const& identity);
|
||||
Humanoid(Humanoid const&) = default;
|
||||
@ -163,6 +165,7 @@ public:
|
||||
void setDance(Maybe<String> const& dance);
|
||||
void setFacingDirection(Direction facingDirection);
|
||||
void setMovingBackwards(bool movingBackwards);
|
||||
void setHeadRotation(float headRotation);
|
||||
void setRotation(float rotation);
|
||||
void setScale(Vec2F scale);
|
||||
|
||||
@ -183,6 +186,7 @@ public:
|
||||
void setPrimaryHandFrameOverrides(String backFrameOverride, String frontFrameOverride);
|
||||
void setPrimaryHandDrawables(List<Drawable> drawables);
|
||||
void setPrimaryHandNonRotatedDrawables(List<Drawable> drawables);
|
||||
bool primaryHandHoldingItem() const;
|
||||
|
||||
// Same as primary hand.
|
||||
void setAltHandParameters(bool holdingItem, float angle, float itemAngle, bool recoil,
|
||||
@ -190,6 +194,7 @@ public:
|
||||
void setAltHandFrameOverrides(String backFrameOverride, String frontFrameOverride);
|
||||
void setAltHandDrawables(List<Drawable> drawables);
|
||||
void setAltHandNonRotatedDrawables(List<Drawable> drawables);
|
||||
bool altHandHoldingItem() const;
|
||||
|
||||
// Updates the animation based on whatever the current animation state is,
|
||||
// wrapping or clamping animation time as appropriate.
|
||||
@ -355,6 +360,8 @@ private:
|
||||
Maybe<String> m_dance;
|
||||
Direction m_facingDirection;
|
||||
bool m_movingBackwards;
|
||||
float m_headRotation;
|
||||
float m_headRotationTarget;
|
||||
float m_rotation;
|
||||
Vec2F m_scale;
|
||||
bool m_drawVaporTrail;
|
||||
|
@ -1021,16 +1021,16 @@ void Player::update(float dt, uint64_t) {
|
||||
|| m_humanoid->danceCyclicOrEnded() || m_movementController->running())
|
||||
m_humanoid->setDance({});
|
||||
|
||||
m_tools->suppressItems(suppressedItems);
|
||||
m_tools->tick(dt, m_shifting, m_pendingMoves);
|
||||
|
||||
if (auto overrideFacingDirection = m_tools->setupHumanoidHandItems(*m_humanoid, position(), aimPosition()))
|
||||
m_movementController->controlFace(*overrideFacingDirection);
|
||||
|
||||
bool isClient = world()->isClient();
|
||||
if (isClient)
|
||||
m_armor->setupHumanoidClothingDrawables(*m_humanoid, forceNude());
|
||||
|
||||
m_tools->suppressItems(suppressedItems);
|
||||
m_tools->tick(dt, m_shifting, m_pendingMoves);
|
||||
|
||||
if (auto overrideFacingDirection = m_tools->setupHumanoidHandItems(*m_humanoid, position(), aimPosition()))
|
||||
m_movementController->controlFace(*overrideFacingDirection);
|
||||
|
||||
m_effectsAnimator->resetTransformationGroup("flip");
|
||||
if (m_movementController->facingDirection() == Direction::Left)
|
||||
m_effectsAnimator->scaleTransformationGroup("flip", Vec2F(-1, 1));
|
||||
@ -1075,6 +1075,35 @@ void Player::update(float dt, uint64_t) {
|
||||
|
||||
m_effectEmitter->tick(dt, *entityMode());
|
||||
|
||||
if (isClient) {
|
||||
bool calculateHeadRotation = isMaster();
|
||||
if (!calculateHeadRotation) {
|
||||
auto headRotationProperty = getSecretProperty("humanoid.headRotation");
|
||||
if (headRotationProperty.isType(Json::Type::Float)) {
|
||||
m_humanoid->setHeadRotation(headRotationProperty.toFloat());
|
||||
} else
|
||||
calculateHeadRotation = true;
|
||||
}
|
||||
if (calculateHeadRotation) { // master or not an OpenStarbound player
|
||||
float headRotation = 0.f;
|
||||
if (m_humanoid->primaryHandHoldingItem() || m_humanoid->altHandHoldingItem()) {
|
||||
auto primary = m_tools->primaryHandItem();
|
||||
auto alt = m_tools->altHandItem();
|
||||
String const disableFlag = "disableHeadRotation";
|
||||
auto statusFlag = m_statusController->statusProperty(disableFlag);
|
||||
if (!(statusFlag.isType(Json::Type::Bool) && statusFlag.toBool())
|
||||
&& !(primary && primary->instanceValue(disableFlag))
|
||||
&& !(alt && alt->instanceValue(disableFlag))) {
|
||||
auto diff = world()->geometry().diff(aimPosition(), mouthPosition());
|
||||
diff.setX(fabsf(diff.x()));
|
||||
headRotation = diff.angle() * .25f * numericalDirection(m_humanoid->facingDirection());
|
||||
}
|
||||
}
|
||||
m_humanoid->setHeadRotation(headRotation);
|
||||
setSecretProperty("humanoid.headRotation", headRotation);
|
||||
}
|
||||
}
|
||||
|
||||
m_humanoid->setFacingDirection(m_movementController->facingDirection());
|
||||
m_humanoid->setMovingBackwards(m_movementController->facingDirection() != m_movementController->movingDirection());
|
||||
|
||||
@ -2610,6 +2639,7 @@ Maybe<StringView> Player::getSecretPropertyView(String const& name) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Json Player::getSecretProperty(String const& name, Json defaultValue) const {
|
||||
if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) {
|
||||
DataStreamExternalBuffer buffer(tag->utf8Ptr(), tag->utf8Size());
|
||||
|
Loading…
x
Reference in New Issue
Block a user