From d65bc3cc8d3bb9793e5d4f8d7aa223abbc001886 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sat, 19 Aug 2023 20:47:58 +1000 Subject: [PATCH] Add collision cycling to Material items --- assets/opensb/binds/opensb.binds | 14 ++- .../interface/building/collisionblock.png | Bin 0 -> 219 bytes .../interface/building/collisionempty.png | Bin 0 -> 176 bytes .../interface/building/collisionplatform.png | Bin 0 -> 178 bytes assets/opensb/sfx/tools/cyclematcollision.ogg | Bin 0 -> 4995 bytes source/game/CMakeLists.txt | 1 + source/game/StarCollisionBlock.hpp | 6 +- source/game/StarToolUser.cpp | 15 +-- source/game/interfaces/StarRenderableItem.hpp | 19 +++ source/game/items/StarMaterialItem.cpp | 113 ++++++++++++------ source/game/items/StarMaterialItem.hpp | 18 ++- .../game/scripting/StarWorldLuaBindings.cpp | 4 +- 12 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 assets/opensb/interface/building/collisionblock.png create mode 100644 assets/opensb/interface/building/collisionempty.png create mode 100644 assets/opensb/interface/building/collisionplatform.png create mode 100644 assets/opensb/sfx/tools/cyclematcollision.ogg create mode 100644 source/game/interfaces/StarRenderableItem.hpp diff --git a/assets/opensb/binds/opensb.binds b/assets/opensb/binds/opensb.binds index 2f84e37..43493e8 100644 --- a/assets/opensb/binds/opensb.binds +++ b/assets/opensb/binds/opensb.binds @@ -2,7 +2,8 @@ "opensb": { "groups": { "camera": { "name": "Camera" }, - "voice": { "name": "Voice" } + "voice": { "name": "Voice" }, + "building": { "name": "Building" } }, "name": "Open^#ebd74a;Starbound", "binds": { @@ -11,7 +12,7 @@ "type": "key", "value": "=" }], - "group" : "camera", + "group": "camera", "name": "Zoom In" }, "zoomOut": { @@ -19,13 +20,18 @@ "type": "key", "value": "-" }], - "group" : "camera", + "group": "camera", "name": "Zoom Out" }, "pushToTalk": { "default": [], - "group" : "voice", + "group": "voice", "name": "Push To Talk" + }, + "materialCycleCollision": { + "default": [], + "group": "building", + "name": "Cycle Material Collision" } } } diff --git a/assets/opensb/interface/building/collisionblock.png b/assets/opensb/interface/building/collisionblock.png new file mode 100644 index 0000000000000000000000000000000000000000..b1d5f9d36b8bc21b220dfd5f68b685f375462f3f GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DkxL735kHCP2GSm5d67@`q8by6em0R;}0jjt^lVz>Q%!7%su zzwoS$hAU&7Y6K>%Sjfc4xI%BkWJ7_(lW#gpHa4h#KN{wkHfxvh{=5jwV)n=HwAj~* zgvq}8b0}usgC^EJyQHn(A66HOoG>fzMvG^&VmWU~q3xW{*&Q)?pJRlj3+9yp?PlbP0l+XkK-_uH{ literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/building/collisionempty.png b/assets/opensb/interface/building/collisionempty.png new file mode 100644 index 0000000000000000000000000000000000000000..6289ba15fdc95bcccef8c8d565ee9d61d72bf8e0 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DkxL735kHCP2G=<4a>7@`rJyg>fP|Ns9vYfQozrmQ*cn4Hzb z$&|@%BDIE7yTOwwlU0NFvda`+17DY$4exaJm>n=#cp-vulG+**TLxxk2Gga5?!qn! RF+c+uJYD@<);T3K0RV1+FKGY( literal 0 HcmV?d00001 diff --git a/assets/opensb/interface/building/collisionplatform.png b/assets/opensb/interface/building/collisionplatform.png new file mode 100644 index 0000000000000000000000000000000000000000..b8b2b65dbbdb7dbca1fa3b6ca5ac3ff81744292c GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DkxL735kHCP2G=`DiM0nvDlr6{H`Wm5 zyKpq1gi-an!%Pi^j0%n^otzD`CN1<4G2=0C%uFee^lD}>susDxWnjR-kbhpnFjsg6 P$Q}kyS3j3^P6a;qAlV3N5GtWQAc~SL&kd+Vu$q7Zp{N8owdyhQDj*>vbTAjo=a)MWogAxbDpxZ4X9o@A|QURr@7t%)hg;Aut+wo{lntR0bajk1mn3Zpvs zo0zs)vBmsIAD41AE#3b%-{}1FS(2Et+A?WO2WmtAH24z)aC7tIB%9%6Ax`C_FroOC z72CooSH_OFt0+zw@7Tgl^l}_7-S6W%TyZSR^**<@Q!v4ywj1-}U0o%qIyWwKr>ujo zQ;rly)IQj-7Qr~nFou9mnomW`3Sz;Oep_rr4%$drVliF{&!Es4n9By02PK*|(lDrc}V^W69MZ z#gT<&|LfftoFD$*U$=-BJH!pJY~IRh-fHcETQ|qr(

n5b&uRZr>E^v_j_8EL#Zg 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.get())) - renderCallback->addTilePreviews(materialItem->preview(shifting)); - else if (auto liquidItem = as(m_primaryHandItem.get())) - renderCallback->addTilePreviews(liquidItem->preview(shifting)); - } - if (auto pri = as(m_primaryHandItem.get())) renderCallback->addTilePreviews(pri->preview(shifting)); else if (auto alt = as(m_altHandItem.get())) renderCallback->addTilePreviews(alt->preview(shifting)); + if (auto ren = as(m_primaryHandItem.get())) + ren->render(renderCallback, renderLayer); + if (auto ren = as(m_altHandItem.get())) + ren->render(renderCallback, renderLayer); + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { if (auto activeItem = as(item)) { for (auto drawablePair : activeItem->entityDrawables()) diff --git a/source/game/interfaces/StarRenderableItem.hpp b/source/game/interfaces/StarRenderableItem.hpp new file mode 100644 index 0000000..9b02763 --- /dev/null +++ b/source/game/interfaces/StarRenderableItem.hpp @@ -0,0 +1,19 @@ +#ifndef STAR_RENDERABLE_ITEM_HPP +#define STAR_RENDERABLE_ITEM_HPP + +#include "StarEntityRendering.hpp" + +namespace Star { + + STAR_CLASS(RenderableItem); + + class RenderableItem { + public: + virtual ~RenderableItem() {} + + virtual void render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) = 0; + }; + +} + +#endif diff --git a/source/game/items/StarMaterialItem.cpp b/source/game/items/StarMaterialItem.cpp index e7b83da..acfe805 100644 --- a/source/game/items/StarMaterialItem.cpp +++ b/source/game/items/StarMaterialItem.cpp @@ -7,6 +7,7 @@ #include "StarWorld.hpp" #include "StarWorldClient.hpp" #include "StarWorldTemplate.hpp" +#include "StarInput.hpp" namespace Star { @@ -47,6 +48,8 @@ MaterialItem::MaterialItem(Json const& config, String const& directory, Json con m_placeSounds.append(materialDatabase->defaultFootstepSound()); } m_shifting = false; + m_lastTileAreaRadiusCache = 0.0f; + m_collisionOverride = TileCollisionOverride::None; } ItemPtr MaterialItem::clone() const { @@ -56,7 +59,7 @@ ItemPtr MaterialItem::clone() const { void MaterialItem::init(ToolUserEntity* owner, ToolHand hand) { FireableItem::init(owner, hand); BeamItem::init(owner, hand); - owner->addSound(Random::randValueFrom(m_placeSounds), 0.8f, 2.0f); + owner->addSound(Random::randValueFrom(m_placeSounds), 1.0f, 2.0f); } void MaterialItem::uninit() { @@ -71,6 +74,44 @@ void MaterialItem::update(float dt, FireMode fireMode, bool shifting, HashSetmaterialCollisionKind(m_material); + for (size_t i = 0; i != *presses; ++i) { + constexpr auto limit = (uint8_t)TileCollisionOverride::Block + 1; + while (true) { + m_collisionOverride = (TileCollisionOverride)(((uint8_t)m_collisionOverride + 1) % limit); + if (collisionKindFromOverride(m_collisionOverride) != baseKind) + break; + } + } + owner()->addSound("/sfx/tools/cyclematcollision.ogg", 1.0f, Random::randf(0.9f, 1.1f)); + } +} + +void MaterialItem::render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) { + if (m_collisionOverride != TileCollisionOverride::None) { + float pulseLevel = 1.f - 0.3f * 0.5f * ((float)sin(2 * Constants::pi * 4.0 * Time::monotonicTime()) + 1.f); + Color color = Color::rgba(owner()->favoriteColor()).mix(Color::White); + color.setAlphaF(color.alphaF() * pulseLevel * 0.95f); + auto addIndicator = [&](String const& path) { + Vec2F basePosition = Vec2F(0.5f, 0.5f); + auto indicator = Drawable::makeImage(path, 1.0f / TilePixels, true, basePosition); + indicator.fullbright = true; + indicator.color = color; + for (auto& tilePos : tileArea(calcRadius(m_shifting))) { + indicator.position = basePosition + Vec2F(tilePos); + renderCallback->addDrawable(indicator, RenderLayerForegroundTile); + } + }; + + if (m_collisionOverride == TileCollisionOverride::Empty) + addIndicator("/interface/building/collisionempty.png"); + else if (m_collisionOverride == TileCollisionOverride::Platform) + addIndicator("/interface/building/collisionplatform.png"); + else if (m_collisionOverride == TileCollisionOverride::Block) + addIndicator("/interface/building/collisionblock.png"); + } } List MaterialItem::nonRotatedDrawables() const { @@ -84,18 +125,12 @@ void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) { auto layer = (mode == FireMode::Primary || !twoHanded() ? TileLayer::Foreground : TileLayer::Background); TileModificationList modifications; - float radius; - - if (!shifting) - radius = m_blockRadius; - else - radius = m_altBlockRadius; - - if (!multiplaceEnabled()) - radius = 1; + float radius = calcRadius(shifting); auto geo = world()->geometry(); auto aimPosition = owner()->aimPosition(); + auto& tilePositions = tileArea(radius); + if (!m_lastAimPosition) m_lastAimPosition = aimPosition; @@ -106,7 +141,7 @@ void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) { float magnitude = diff.magnitude(); float limit = max(4.f, 64.f / radius); if (magnitude > limit) { - m_lastAimPosition = aimPosition + diff.normalized() * limit; + diff = diff.normalized() * limit; magnitude = limit; } @@ -116,8 +151,8 @@ void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) { size_t total = 0; for (int i = 0; i != steps; ++i) { auto placementOrigin = aimPosition + diff * (1.0f - ((float)i / steps)); - for (Vec2I pos : tileAreaBrush(radius, placementOrigin, true)) - modifications.append({ pos, PlaceMaterial{layer, materialId(), placementHueShift(pos)} }); + for (Vec2I& pos : tilePositions) + modifications.append({ pos, PlaceMaterial{layer, materialId(), placementHueShift(pos), m_collisionOverride} }); // Make sure not to make any more modifications than we have consumables. if (modifications.size() > count()) @@ -147,6 +182,22 @@ MaterialId MaterialItem::materialId() const { return m_material; } +float MaterialItem::calcRadius(bool shifting) const { + if (!multiplaceEnabled()) + return 1; + else + return !shifting ? m_blockRadius : m_altBlockRadius; +} + +List& MaterialItem::tileArea(float radius) const { + auto aimPosition = owner()->aimPosition(); + if (!m_lastAimPosition || *m_lastAimPosition != aimPosition || m_lastTileAreaRadiusCache != radius) { + m_lastTileAreaRadiusCache = radius; + m_tileAreasCache = tileAreaBrush(radius, owner()->aimPosition(), true); + } + return m_tileAreasCache; +} + MaterialHue MaterialItem::materialHueShift() const { return m_materialHueShift; } @@ -155,16 +206,9 @@ bool MaterialItem::canPlace(bool shifting) const { if (initialized()) { MaterialId material = materialId(); - float radius; - if (!shifting) - radius = m_blockRadius; - else - radius = m_altBlockRadius; + float radius = calcRadius(shifting); - if (!multiplaceEnabled()) - radius = 1; - - for (auto pos : tileAreaBrush(radius, owner()->aimPosition(), true)) { + for (auto& pos : tileArea(radius)) { MaterialHue hueShift = placementHueShift(pos); if (world()->canModifyTile(pos, PlaceMaterial{TileLayer::Foreground, material, hueShift}, false) || world()->canModifyTile(pos, PlaceMaterial{TileLayer::Background, material, hueShift}, false)) @@ -178,6 +222,18 @@ bool MaterialItem::multiplaceEnabled() const { return m_multiplace && count() > 1; } +float& MaterialItem::blockRadius() { + return m_blockRadius; +} + +float& MaterialItem::altBlockRadius() { + return m_altBlockRadius; +} + +TileCollisionOverride& MaterialItem::collisionOverride() { + return m_collisionOverride; +} + List MaterialItem::preview(bool shifting) const { List result; if (initialized()) { @@ -187,19 +243,8 @@ List MaterialItem::preview(bool shifting) const { auto material = materialId(); auto color = DefaultMaterialColorVariant; - float radius; - - if (!shifting) - radius = m_blockRadius; - else - radius = m_altBlockRadius; - - if (!multiplaceEnabled()) - radius = 1; - size_t c = 0; - - for (auto pos : tileAreaBrush(radius, owner()->aimPosition(), true)) { + for (auto& pos : tileArea(calcRadius(shifting))) { MaterialHue hueShift = placementHueShift(pos); if (c >= count()) break; diff --git a/source/game/items/StarMaterialItem.hpp b/source/game/items/StarMaterialItem.hpp index ca345eb..e5b1f01 100644 --- a/source/game/items/StarMaterialItem.hpp +++ b/source/game/items/StarMaterialItem.hpp @@ -5,12 +5,15 @@ #include "StarFireableItem.hpp" #include "StarBeamItem.hpp" #include "StarEntityRendering.hpp" +#include "StarPreviewTileTool.hpp" +#include "StarRenderableItem.hpp" +#include "StarCollisionBlock.hpp" namespace Star { STAR_CLASS(MaterialItem); -class MaterialItem : public Item, public FireableItem, public BeamItem { +class MaterialItem : public Item, public FireableItem, public PreviewTileTool, public RenderableItem, public BeamItem { public: MaterialItem(Json const& config, String const& directory, Json const& settings); virtual ~MaterialItem() {} @@ -20,6 +23,7 @@ public: void init(ToolUserEntity* owner, ToolHand hand) override; void uninit() override; void update(float dt, FireMode fireMode, bool shifting, HashSet const& moves) override; + void render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) override; List nonRotatedDrawables() const override; @@ -32,10 +36,14 @@ public: bool canPlace(bool shifting) const; bool multiplaceEnabled() const; - // FIXME: Why isn't this a PreviewTileTool then?? - List preview(bool shifting) const; + float& blockRadius(); + float& altBlockRadius(); + TileCollisionOverride& collisionOverride(); + List preview(bool shifting) const override; private: + float calcRadius(bool shifting) const; + List& tileArea(float radius) const; MaterialHue placementHueShift(Vec2I const& position) const; MaterialId m_material; @@ -47,6 +55,10 @@ private: bool m_multiplace; StringList m_placeSounds; Maybe m_lastAimPosition; + TileCollisionOverride m_collisionOverride; + + mutable float m_lastTileAreaRadiusCache; + mutable List m_tileAreasCache; }; } diff --git a/source/game/scripting/StarWorldLuaBindings.cpp b/source/game/scripting/StarWorldLuaBindings.cpp index e1c8822..dc1c5be 100644 --- a/source/game/scripting/StarWorldLuaBindings.cpp +++ b/source/game/scripting/StarWorldLuaBindings.cpp @@ -1949,8 +1949,8 @@ namespace LuaBindings { layerName = layerName.substr(0, split); if (overrideName == "empty" || overrideName == "none") placeMaterial.collisionOverride = TileCollisionOverride::Empty; - else if (overrideName == "dynamic" || overrideName == "block") - placeMaterial.collisionOverride = TileCollisionOverride::Dynamic; + else if (overrideName == "block") + placeMaterial.collisionOverride = TileCollisionOverride::Block; else if (overrideName == "platform") placeMaterial.collisionOverride = TileCollisionOverride::Platform; else