v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
268
source/game/interfaces/StarBeamItem.cpp
Normal file
268
source/game/interfaces/StarBeamItem.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue