Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/interfaces/StarBeamItem.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/game/interfaces/StarBeamItem.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/game/interfaces/StarBeamItem.cpp')
-rw-r--r--source/game/interfaces/StarBeamItem.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/source/game/interfaces/StarBeamItem.cpp b/source/game/interfaces/StarBeamItem.cpp
new file mode 100644
index 0000000..4a6a98b
--- /dev/null
+++ b/source/game/interfaces/StarBeamItem.cpp
@@ -0,0 +1,268 @@
+#include "StarBeamItem.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarImageProcessing.hpp"
+#include "StarRoot.hpp"
+#include "StarAssets.hpp"
+#include "StarRandom.hpp"
+#include "StarItem.hpp"
+#include "StarToolUserEntity.hpp"
+#include "StarWorld.hpp"
+
+namespace Star {
+
+BeamItem::BeamItem(Json config) {
+ config = Root::singleton().assets()->json("/player.config:beamGunConfig").setAll(config.toObject());
+
+ m_image = config.get("image").toString();
+ m_endImages = jsonToStringList(config.get("endImages"));
+ m_endType = EndType::Invalid;
+ m_segmentsPerUnit = config.get("segmentsPerUnit").toFloat();
+ m_nearControlPointElasticity = config.get("nearControlPointElasticity").toFloat();
+ m_farControlPointElasticity = config.get("farControlPointElasticity").toFloat();
+ m_nearControlPointDistance = config.get("nearControlPointDistance").toFloat();
+ m_handPosition = jsonToVec2F(config.get("handPosition"));
+ m_firePosition = jsonToVec2F(config.get("firePosition"));
+ m_range = 1.0f;
+ m_targetSegmentRun = config.get("targetSegmentRun").toFloat();
+ m_minBeamWidth = config.get("minBeamWidth").toFloat();
+ m_maxBeamWidth = config.get("maxBeamWidth").toFloat();
+ m_beamWidthDev = config.getFloat("beamWidthDev", (m_maxBeamWidth - m_minBeamWidth) / 3);
+ m_minBeamJitter = config.get("minBeamJitter").toFloat();
+ m_maxBeamJitter = config.get("maxBeamJitter").toFloat();
+ m_beamJitterDev = config.getFloat("beamJitterDev", (m_maxBeamJitter * 2) / 3);
+ m_minBeamTrans = config.get("minBeamTrans").toFloat();
+ m_maxBeamTrans = config.get("maxBeamTrans").toFloat();
+ m_beamTransDev = config.getFloat("beamTransDev", (m_maxBeamTrans - m_minBeamTrans) / 3);
+ m_minBeamLines = config.get("minBeamLines").toInt();
+ m_maxBeamLines = config.get("maxBeamLines").toInt();
+ m_innerBrightnessScale = config.get("innerBrightnessScale").toFloat();
+ m_firstStripeThickness = config.get("firstStripeThickness").toFloat();
+ m_secondStripeThickness = config.get("secondStripeThickness").toFloat();
+ m_color = {255, 255, 255, 255};
+ m_particleGenerateCooldown = .25;
+ m_inRangeLastUpdate = false;
+}
+
+void BeamItem::init(ToolUserEntity* owner, ToolHand hand) {
+ ToolUserItem::init(owner, hand);
+
+ m_beamCurve = CSplineF();
+
+ if (initialized()) {
+ m_color = owner->favoriteColor();
+ m_range = owner->beamGunRadius();
+ return;
+ }
+
+ throw ItemException("BeamItem::init: Beam Gun not init'd properly, or user not recognized as Tool User.");
+}
+
+void BeamItem::update(FireMode, bool, HashSet<MoveControlType> const&) {
+ if (m_particleGenerateCooldown >= 0)
+ m_particleGenerateCooldown -= WorldTimestep;
+
+ if (!initialized())
+ throw ItemException("BeamItem::update: Beam Gun not init'd properly, or user not recognized as Tool User.");
+
+ m_beamCurve.origin() = owner()->handPosition(hand(), (m_firePosition - m_handPosition) / TilePixels);
+
+ if (m_endType == EndType::TileGroup)
+ m_beamCurve.dest() = world()->geometry().diff(owner()->aimPosition().round(), owner()->position());
+ else if (m_endType == EndType::Wire)
+ m_beamCurve.dest() = world()->geometry().diff(owner()->aimPosition(), owner()->position());
+ else
+ m_beamCurve.dest() = world()->geometry().diff(centerOfTile(owner()->aimPosition()), owner()->position());
+
+ if (m_beamCurve.dest().magnitudeSquared() < m_beamCurve.origin().magnitudeSquared())
+ m_beamCurve[2] = m_beamCurve.dest();
+ else
+ m_beamCurve[2] = m_beamCurve[2] + (m_beamCurve.dest() - m_beamCurve[2]) * m_farControlPointElasticity;
+
+ Vec2F desiredNearControlPoint = (m_beamCurve.dest() - m_beamCurve.origin()) * m_nearControlPointDistance;
+
+ if (m_beamCurve.dest().magnitudeSquared() < m_beamCurve.origin().magnitudeSquared())
+ m_beamCurve[1] = m_beamCurve.origin();
+ else if (owner()->facingDirection() != getAngleSide(m_beamCurve[1].angle()).second)
+ m_beamCurve[1] = desiredNearControlPoint;
+ else
+ m_beamCurve[1] = m_beamCurve[1] + (desiredNearControlPoint - m_beamCurve[1]) * m_nearControlPointElasticity;
+}
+
+List<Drawable> BeamItem::nonRotatedDrawables() const {
+ return beamDrawables();
+}
+
+float BeamItem::getAngle(float angle) {
+ if (m_beamCurve.dest().magnitudeSquared() < m_beamCurve.origin().magnitudeSquared()
+ || m_beamCurve.origin() == m_beamCurve[1])
+ return angle;
+ return getAngleSide(m_beamCurve[1].angle()).first;
+}
+
+List<Drawable> BeamItem::drawables() const {
+ return {Drawable::makeImage(m_image, 1.0f / TilePixels, true, -handPosition() / TilePixels)};
+}
+
+Vec2F BeamItem::handPosition() const {
+ return m_handPosition;
+}
+
+Vec2F BeamItem::firePosition() const {
+ return m_firePosition;
+}
+
+void BeamItem::setRange(float range) {
+ m_range = range;
+}
+
+float BeamItem::getAppropriateOpacity() const {
+ float curveLen = m_beamCurve.length();
+ const float rangeEffect = (m_range - curveLen) / m_range;
+
+ auto projectOntoRange = [&](float min, float max) { return rangeEffect * (max - min) + min; };
+ auto rangeRand = [&](float dev, float min, float max) {
+ return clamp<float>(Random::nrandf(dev, projectOntoRange(min, max)), min, max);
+ };
+
+ int numLines = projectOntoRange(m_minBeamLines, m_maxBeamLines);
+ float res = (1 - rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans));
+ if (numLines > 0) {
+ for (auto line = 0; line < numLines - 1; line++)
+ res *= (1 - rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans));
+ }
+ return 1 - res;
+}
+
+void BeamItem::setEnd(EndType type) {
+ m_endType = type;
+}
+
+List<Drawable> BeamItem::beamDrawables(bool canPlace) const {
+ List<Drawable> res;
+
+ float curveLen = m_beamCurve.length();
+ const float rangeEffect = (m_range - curveLen) / m_range;
+
+ auto projectOntoRange = [&](float min, float max) { return rangeEffect * (max - min) + min; };
+ auto rangeRand = [&](float dev, float min, float max) {
+ return clamp<float>(Random::nrandf(dev, projectOntoRange(min, max)), min, max);
+ };
+
+ if (initialized()) {
+ Vec2F endPoint;
+ if (m_endType == EndType::TileGroup)
+ endPoint = owner()->aimPosition().round();
+ else if (m_endType == EndType::Wire)
+ endPoint = owner()->aimPosition();
+ else
+ endPoint = centerOfTile(owner()->aimPosition());
+
+ if ((endPoint - owner()->position()).magnitude() <= m_range && curveLen <= m_range) {
+ m_inRangeLastUpdate = true;
+ int numLines = projectOntoRange(m_minBeamLines, m_maxBeamLines);
+ Vec4B mainColor = m_color;
+ if (!canPlace) {
+ Color temp = Color::rgba(m_color);
+ temp.setHue(temp.hue() + 120);
+ mainColor = temp.toRgba();
+ }
+ m_lastUpdateColor = mainColor;
+
+ String endImage = "";
+ if (m_endType != EndType::Invalid) {
+ endImage = m_endImages[(unsigned)m_endType];
+ }
+
+ if (!endImage.empty()) {
+ if (!canPlace) {
+ ImageOperation op = HueShiftImageOperation::hueShiftDegrees(120);
+ endImage = strf("%s?%s", endImage, imageOperationToString(op));
+ }
+
+ Drawable ball = Drawable::makeImage(endImage, 1.0f / TilePixels, true, m_beamCurve.dest());
+ Color ballColor = Color::White;
+ ballColor.setAlphaF(getAppropriateOpacity());
+ ball.color = ballColor;
+ res.push_back(ball);
+ }
+
+ for (auto line = 0; line < numLines; line++) {
+ float lineThickness = rangeRand(m_beamWidthDev, m_minBeamWidth, m_maxBeamWidth);
+ float beamTransparency = rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans);
+ mainColor[3] = mainColor[3] * beamTransparency;
+ Vec2F previousLoc = m_beamCurve.origin(); // lines meet at origin and dest.
+ Color innerStripe = Color::rgba(mainColor);
+ innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale);
+ innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale);
+ Vec4B firstStripe = innerStripe.toRgba();
+ innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale);
+ innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale);
+ Vec4B secondStripe = innerStripe.toRgba();
+
+ for (auto i = 1; i < (int)(curveLen * m_targetSegmentRun - .5); i++) { // one less than full length
+ float pos = (float)i / (float)(int)(curveLen * m_targetSegmentRun + .5); // project the discrete steps evenly
+
+ Vec2F currentLoc =
+ m_beamCurve.pointAt(pos) + Vec2F(rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter),
+ rangeRand(m_beamJitterDev, -m_maxBeamJitter, m_maxBeamJitter));
+ res.push_back(
+ Drawable::makeLine(Line2F(previousLoc, currentLoc), lineThickness, Color::rgba(mainColor), Vec2F()));
+ res.push_back(Drawable::makeLine(Line2F(previousLoc, currentLoc),
+ lineThickness * m_firstStripeThickness,
+ Color::rgba(firstStripe),
+ Vec2F()));
+ res.push_back(Drawable::makeLine(Line2F(previousLoc, currentLoc),
+ lineThickness * m_secondStripeThickness,
+ Color::rgba(secondStripe),
+ Vec2F()));
+ previousLoc = std::move(currentLoc);
+ }
+ res.push_back(Drawable::makeLine(
+ Line2F(previousLoc, m_beamCurve.dest()), lineThickness, Color::rgba(mainColor), Vec2F()));
+ res.push_back(Drawable::makeLine(Line2F(previousLoc, m_beamCurve.dest()),
+ lineThickness * m_firstStripeThickness,
+ Color::rgba(firstStripe),
+ Vec2F()));
+ res.push_back(Drawable::makeLine(Line2F(previousLoc, m_beamCurve.dest()),
+ lineThickness * m_secondStripeThickness,
+ Color::rgba(secondStripe),
+ Vec2F()));
+ }
+ } else {
+ if (m_inRangeLastUpdate) {
+ m_inRangeLastUpdate = false;
+ m_particleGenerateCooldown = .25; // TODO, expose to json
+ List<Particle> beamLeftovers;
+ for (auto i = 1; i < (int)(curveLen * m_targetSegmentRun * 2 - .5); i++) { // one less than full length
+ float pos =
+ (float)i / (float)(int)(curveLen * m_targetSegmentRun * 2 + .5); // project the discrete steps evenly
+ float curveLoc = m_beamCurve.arcLenPara(pos);
+
+ Particle beamParticle;
+ beamParticle.type = Particle::Type::Ember;
+ beamParticle.position = m_beamCurve.pointAt(curveLoc);
+ beamParticle.size = 1.0f;
+
+ Color randomColor = Color::rgba(m_lastUpdateColor);
+ randomColor.setValue(1 - (1 - randomColor.value()) / Random::randf(1, 4));
+ randomColor.setSaturation(randomColor.saturation() / Random::randf(1, 4));
+
+ beamParticle.color = randomColor;
+ beamParticle.velocity = Vec2F::filled(Random::randf());
+ beamParticle.finalVelocity = Vec2F(0.0f, -20.0f);
+ beamParticle.approach = Vec2F(0.0f, 5.0f);
+ beamParticle.timeToLive = 0.25f;
+ beamParticle.destructionAction = Particle::DestructionAction::Shrink;
+ beamParticle.destructionTime = 0.2f;
+ beamLeftovers.append(beamParticle);
+ }
+
+ owner()->addParticles(beamLeftovers);
+ }
+ }
+ }
+
+ return res;
+}
+
+}