diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/frontend/StarWireInterface.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/frontend/StarWireInterface.cpp')
-rw-r--r-- | source/frontend/StarWireInterface.cpp | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/source/frontend/StarWireInterface.cpp b/source/frontend/StarWireInterface.cpp new file mode 100644 index 0000000..6ea0bce --- /dev/null +++ b/source/frontend/StarWireInterface.cpp @@ -0,0 +1,332 @@ +#include "StarWireInterface.hpp" +#include "StarGuiReader.hpp" +#include "StarRoot.hpp" +#include "StarWorldClient.hpp" +#include "StarWireEntity.hpp" +#include "StarWorldGeometry.hpp" +#include "StarWorldPainter.hpp" +#include "StarPlayer.hpp" +#include "StarTools.hpp" +#include "StarAssets.hpp" + +namespace Star { + +WirePane::WirePane(WorldClientPtr worldClient, PlayerPtr player, WorldPainterPtr worldPainter) { + m_worldClient = worldClient; + m_player = player; + m_worldPainter = worldPainter; + + m_connecting = false; + + auto assets = Root::singleton().assets(); + GuiReader reader; + reader.construct(assets->json("/interface/wires/wires.config:gui"), this); + + m_insize = Vec2F(context()->textureSize("/interface/wires/inbound.png")) / TilePixels; + m_outsize = Vec2F(context()->textureSize("/interface/wires/outbound.png")) / TilePixels; + m_nodesize = Vec2F(1.8f, 1.8f); + + setTitle({}, "", "Wire you looking at me like that?"); + disableScissoring(); + markAsContainer(); +} + +void WirePane::reset() { + m_connecting = false; +} + +void WirePane::update() { + if (!active()) + return; + if (!m_worldClient->inWorld()) { + dismiss(); + return; + } + + if (m_connecting) { + for (auto entity : m_worldClient->atTile<WireEntity>(m_sourceConnector.entityLocation)) { + if (m_sourceConnector.nodeIndex < entity->nodeCount(m_sourceDirection)) + return; + } + + // stop pending connection if node has been removed + m_connecting = false; + } +} + +void WirePane::renderWire(Vec2F from, Vec2F to, Color baseColor) { + if (m_worldClient->isTileProtected(Vec2I::floor(from)) || m_worldClient->isTileProtected(Vec2I::floor(to))) + return; + + from = m_worldPainter->camera().worldToScreen(from); + to = m_worldPainter->camera().worldToScreen(to); + + auto rangeRand = [&](float dev, float min, float max) { + return clamp<float>(Random::nrandf(dev, max), min, max); + }; + + float m_beamWidthDev; + float m_minBeamWidth; + float m_maxBeamWidth; + float m_beamTransDev; + float m_minBeamTrans; + float m_maxBeamTrans; + float m_innerBrightnessScale; + float m_firstStripeThickness; + float m_secondStripeThickness; + + auto assets = Root::singleton().assets(); + JsonObject config = assets->json("/player.config:beamGunConfig").toObject(); + m_minBeamWidth = config.get("minBeamWidth").toFloat(); + m_maxBeamWidth = config.get("maxBeamWidth").toFloat(); + m_beamWidthDev = config.value("beamWidthDev", (m_maxBeamWidth - m_minBeamWidth) / 3).toFloat(); + m_minBeamTrans = config.get("minBeamTrans").toFloat(); + m_maxBeamTrans = config.get("maxBeamTrans").toFloat(); + m_beamTransDev = config.value("beamTransDev", (m_maxBeamTrans - m_minBeamTrans) / 3).toFloat(); + m_innerBrightnessScale = config.get("innerBrightnessScale").toFloat(); + m_firstStripeThickness = config.get("firstStripeThickness").toFloat(); + m_secondStripeThickness = config.get("secondStripeThickness").toFloat(); + + float lineThickness = m_worldPainter->camera().pixelRatio() * rangeRand(m_beamWidthDev, m_minBeamWidth, m_maxBeamWidth); + float beamTransparency = rangeRand(m_beamTransDev, m_minBeamTrans, m_maxBeamTrans); + baseColor.setAlphaF(baseColor.alphaF() * beamTransparency); + Color innerStripe = baseColor; + innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale); + innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale); + Color firstStripe = innerStripe; + innerStripe.setValue(1 - (1 - innerStripe.value()) / m_innerBrightnessScale); + innerStripe.setSaturation(innerStripe.saturation() / m_innerBrightnessScale); + Color secondStripe = innerStripe; + + context()->drawLine(from, to, baseColor.toRgba(), lineThickness); + context()->drawLine(from, to, firstStripe.toRgba(), lineThickness * m_firstStripeThickness); + context()->drawLine(from, to, secondStripe.toRgba(), lineThickness * m_secondStripeThickness); +} + +void WirePane::renderImpl() { + if (!m_worldClient->inWorld()) + return; + + auto region = RectF(m_worldClient->clientWindow()); + + auto const& camera = m_worldPainter->camera(); + auto highWire = Color::Red; + auto lowWire = Color::Red.mix(Color::Black, 0.8f); + auto white = Color::White.toRgba(); + float phase = 0.5f + 0.5f * std::sin((double)Time::monotonicMilliseconds() / 100.0); + auto drawLineColor = Color::Red.mix(Color::White, phase); + + for (auto entity : m_worldClient->query<WireEntity>(region)) { + for (size_t i = 0; i < entity->nodeCount(WireDirection::Input); ++i) { + Vec2I position = entity->tilePosition() + entity->nodePosition({WireDirection::Input, i}); + if (!m_worldClient->isTileProtected(position)) { + context()->drawQuad("/interface/wires/inbound.png", + camera.worldToScreen(centerOfTile(position) - (m_insize / 2.0f)), + camera.pixelRatio(), white); + } + } + + for (size_t i = 0; i < entity->nodeCount(WireDirection::Output); ++i) { + Vec2I position = entity->tilePosition() + entity->nodePosition({WireDirection::Output, i}); + if (!m_worldClient->isTileProtected(position)) { + context()->drawQuad("/interface/wires/outbound.png", + camera.worldToScreen(centerOfTile(position) - (m_outsize / 2.0f)), + camera.pixelRatio(), white); + } + } + } + + HashSet<pair<WireConnection, WireConnection>> visitedConnections; + for (auto entity : m_worldClient->query<WireEntity>(region)) { + for (size_t i = 0; i < entity->nodeCount(WireDirection::Input); ++i) { + Vec2I tilePosition = entity->tilePosition(); + Vec2I inPosition = tilePosition + entity->nodePosition({WireDirection::Input, i}); + + for (auto const& connection : entity->connectionsForNode({WireDirection::Input, i})) { + visitedConnections.add({{tilePosition, i}, connection}); + + auto wire = lowWire; + Vec2I outPosition = connection.entityLocation; + if (auto sourceEntity = m_worldClient->atTile<WireEntity>(connection.entityLocation).get(0)) { + outPosition += sourceEntity->nodePosition({WireDirection::Output, connection.nodeIndex}); + if (sourceEntity->nodeState(WireNode{WireDirection::Output, connection.nodeIndex})) + wire = highWire; + } + + renderWire(centerOfTile(inPosition), centerOfTile(outPosition), wire); + } + } + + for (size_t i = 0; i < entity->nodeCount(WireDirection::Output); ++i) { + Vec2I tilePosition = entity->tilePosition(); + Vec2I outPosition = tilePosition + entity->nodePosition({WireDirection::Output, i}); + + auto wire = lowWire; + if (entity->nodeState({WireDirection::Output, i})) + wire = highWire; + + for (auto const& connection : entity->connectionsForNode({WireDirection::Output, i})) { + visitedConnections.contains({connection, {tilePosition, i}}); + + Vec2I inPosition = connection.entityLocation; + if (auto sourceEntity = m_worldClient->atTile<WireEntity>(connection.entityLocation).get(0)) + inPosition += sourceEntity->nodePosition({WireDirection::Input, connection.nodeIndex}); + + renderWire(centerOfTile(outPosition), centerOfTile(inPosition), wire); + } + } + } + + if (m_connecting) { + Vec2F aimPos = m_worldPainter->camera().screenToWorld(Vec2F(m_mousePos) * m_context->interfaceScale()); + Vec2I sourcePosition = m_sourceConnector.entityLocation; + if (auto sourceEntity = m_worldClient->atTile<WireEntity>(m_sourceConnector.entityLocation).get(0)) { + if (m_sourceDirection == WireDirection::Input) + sourcePosition += sourceEntity->nodePosition({WireDirection::Input, m_sourceConnector.nodeIndex}); + else + sourcePosition += sourceEntity->nodePosition({WireDirection::Output, m_sourceConnector.nodeIndex}); + } + renderWire(centerOfTile(sourcePosition), aimPos, drawLineColor); + } +} + +bool WirePane::sendEvent(InputEvent const& event) { + if (event.is<MouseMoveEvent>()) + m_mousePos = *context()->mousePosition(event); + + if (event.is<MouseButtonDownEvent>()) + m_mousePos = *context()->mousePosition(event); + + return false; +} + +WireConnector::SwingResult WirePane::swing(WorldGeometry const& geometry, Vec2F pos, FireMode mode) { + pos = geometry.xwrap(pos); + + if (m_worldClient->isTileProtected((Vec2I)pos)) { + m_connecting = false; + return Protected; + } + + RectF bounds = {pos - Vec2F(16, 16), pos + Vec2F(16, 16)}; + + if (mode == FireMode::Primary) { + Maybe<WireConnection> matchNode; + WireDirection matchDirection = WireDirection::Output; + float bestDist = 10000; + for (auto entity : m_worldClient->query<WireEntity>(bounds)) { + for (size_t i = 0; i < entity->nodeCount(WireDirection::Input); ++i) { + RectF inbounds = RectF::withSize(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})) - (m_nodesize / 2.0f), m_nodesize); + if (geometry.rectContains(inbounds, pos)) { + if (!matchNode) { + matchNode = WireConnection{entity->tilePosition(), i}; + matchDirection = WireDirection::Input; + bestDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})), pos).magnitudeSquared(); + } else { + float thisDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})), pos).magnitudeSquared(); + if (thisDist < bestDist) { + matchNode = WireConnection{entity->tilePosition(), i}; + matchDirection = WireDirection::Input; + bestDist = thisDist; + } + } + } + } + + for (size_t i = 0; i < entity->nodeCount(WireDirection::Output); ++i) { + RectF outbounds = RectF::withSize(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})) - (m_nodesize / 2.0f), m_nodesize); + if (geometry.rectContains(outbounds, pos)) { + if (!matchNode) { + matchNode = WireConnection{entity->tilePosition(), i}; + matchDirection = WireDirection::Output; + bestDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})), pos).magnitudeSquared(); + } else { + float thisDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})), pos).magnitudeSquared(); + if (thisDist < bestDist) { + matchNode = WireConnection{entity->tilePosition(), i}; + matchDirection = WireDirection::Output; + bestDist = thisDist; + } + } + } + } + } + + if (matchNode) { + if (m_connecting) { + if (m_sourceDirection == matchDirection) { + return Mismatch; + } else if (m_sourceConnector.entityLocation == matchNode->entityLocation) { + return Mismatch; + } else { + m_connecting = false; + if (matchDirection == WireDirection::Output) + m_worldClient->connectWire(*matchNode, m_sourceConnector); + else + m_worldClient->connectWire(m_sourceConnector, *matchNode); + } + } else { + m_connecting = true; + m_sourceDirection = matchDirection; + m_sourceConnector = *matchNode; + } + return Connect; + } + + } else { + m_connecting = false; + + Maybe<WireNode> matchNode; + Maybe<Vec2I> matchPosition; + float bestDist = 10000; + for (auto entity : m_worldClient->query<WireEntity>(bounds)) { + for (size_t i = 0; i < entity->nodeCount(WireDirection::Input); ++i) { + RectF inbounds = RectF::withSize(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})) - (m_nodesize / 2.0f), m_nodesize); + if (geometry.rectContains(inbounds, pos) && entity->connectionsForNode({WireDirection::Input, i}).size() > 0) { + if (!matchNode) { + matchPosition = entity->tilePosition(); + matchNode = WireNode{WireDirection::Input, i}; + bestDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})), pos).magnitudeSquared(); + } else { + float thisDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Input, i})), pos).magnitudeSquared(); + if (thisDist < bestDist) { + matchPosition = entity->tilePosition(); + matchNode = WireNode{WireDirection::Input, i}; + bestDist = thisDist; + } + } + } + } + + for (size_t i = 0; i < entity->nodeCount(WireDirection::Output); ++i) { + RectF outbounds = RectF::withSize(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})) - (m_nodesize / 2.0f), m_nodesize); + if (geometry.rectContains(outbounds, pos) && entity->connectionsForNode({WireDirection::Output, i}).size() > 0) { + if (!matchNode) { + matchPosition = entity->tilePosition(); + matchNode = WireNode{WireDirection::Output, i}; + bestDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})), pos).magnitudeSquared(); + } else { + float thisDist = geometry.diff(centerOfTile(entity->tilePosition() + entity->nodePosition({WireDirection::Output, i})), pos).magnitudeSquared(); + if (thisDist < bestDist) { + matchPosition = entity->tilePosition(); + matchNode = WireNode{WireDirection::Output, i}; + bestDist = thisDist; + } + } + } + } + } + + if (matchNode) { + m_worldClient->disconnectAllWires(*matchPosition, *matchNode); + return Connect; + } + } + return Nothing; +} + +bool WirePane::connecting() { + return m_connecting; +} + +} |