Add collision cycling to Material items

This commit is contained in:
Kae 2023-08-19 20:47:58 +10:00
parent ec4f70340e
commit d65bc3cc8d
12 changed files with 134 additions and 56 deletions

View File

@ -2,7 +2,8 @@
"opensb": { "opensb": {
"groups": { "groups": {
"camera": { "name": "Camera" }, "camera": { "name": "Camera" },
"voice": { "name": "Voice" } "voice": { "name": "Voice" },
"building": { "name": "Building" }
}, },
"name": "Open^#ebd74a;Starbound", "name": "Open^#ebd74a;Starbound",
"binds": { "binds": {
@ -11,7 +12,7 @@
"type": "key", "type": "key",
"value": "=" "value": "="
}], }],
"group" : "camera", "group": "camera",
"name": "Zoom In" "name": "Zoom In"
}, },
"zoomOut": { "zoomOut": {
@ -19,13 +20,18 @@
"type": "key", "type": "key",
"value": "-" "value": "-"
}], }],
"group" : "camera", "group": "camera",
"name": "Zoom Out" "name": "Zoom Out"
}, },
"pushToTalk": { "pushToTalk": {
"default": [], "default": [],
"group" : "voice", "group": "voice",
"name": "Push To Talk" "name": "Push To Talk"
},
"materialCycleCollision": {
"default": [],
"group": "building",
"name": "Cycle Material Collision"
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

View File

@ -196,6 +196,7 @@ SET (star_game_HEADERS
interfaces/StarPointableItem.hpp interfaces/StarPointableItem.hpp
interfaces/StarPortraitEntity.hpp interfaces/StarPortraitEntity.hpp
interfaces/StarPreviewableItem.hpp interfaces/StarPreviewableItem.hpp
interfaces/StarRenderableItem.hpp
interfaces/StarScriptedEntity.hpp interfaces/StarScriptedEntity.hpp
interfaces/StarStatusEffectEntity.hpp interfaces/StarStatusEffectEntity.hpp
interfaces/StarStatusEffectItem.hpp interfaces/StarStatusEffectItem.hpp

View File

@ -22,7 +22,7 @@ enum class TileCollisionOverride : uint8_t {
None, None,
Empty, Empty,
Platform, Platform,
Dynamic Block
}; };
inline CollisionKind collisionKindFromOverride(TileCollisionOverride const& over) { inline CollisionKind collisionKindFromOverride(TileCollisionOverride const& over) {
@ -31,8 +31,8 @@ inline CollisionKind collisionKindFromOverride(TileCollisionOverride const& over
return CollisionKind::None; return CollisionKind::None;
case TileCollisionOverride::Platform: case TileCollisionOverride::Platform:
return CollisionKind::Platform; return CollisionKind::Platform;
case TileCollisionOverride::Dynamic: case TileCollisionOverride::Block:
return CollisionKind::Dynamic; return CollisionKind::Block;
default: default:
return CollisionKind::Null; return CollisionKind::Null;
} }

View File

@ -535,21 +535,16 @@ void ToolUser::render(RenderCallback* renderCallback, bool inToolRange, bool shi
return; return;
} }
// FIXME: Why isn't material item a PreviewTileTool, why is inToolRange
// passed in again, what is the difference here between the owner's tool
// range, can't MaterialItem figure this out?
if (inToolRange) {
if (auto materialItem = as<MaterialItem>(m_primaryHandItem.get()))
renderCallback->addTilePreviews(materialItem->preview(shifting));
else if (auto liquidItem = as<LiquidItem>(m_primaryHandItem.get()))
renderCallback->addTilePreviews(liquidItem->preview(shifting));
}
if (auto pri = as<PreviewTileTool>(m_primaryHandItem.get())) if (auto pri = as<PreviewTileTool>(m_primaryHandItem.get()))
renderCallback->addTilePreviews(pri->preview(shifting)); renderCallback->addTilePreviews(pri->preview(shifting));
else if (auto alt = as<PreviewTileTool>(m_altHandItem.get())) else if (auto alt = as<PreviewTileTool>(m_altHandItem.get()))
renderCallback->addTilePreviews(alt->preview(shifting)); renderCallback->addTilePreviews(alt->preview(shifting));
if (auto ren = as<RenderableItem>(m_primaryHandItem.get()))
ren->render(renderCallback, renderLayer);
if (auto ren = as<RenderableItem>(m_altHandItem.get()))
ren->render(renderCallback, renderLayer);
for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) {
if (auto activeItem = as<ActiveItem>(item)) { if (auto activeItem = as<ActiveItem>(item)) {
for (auto drawablePair : activeItem->entityDrawables()) for (auto drawablePair : activeItem->entityDrawables())

View File

@ -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

View File

@ -7,6 +7,7 @@
#include "StarWorld.hpp" #include "StarWorld.hpp"
#include "StarWorldClient.hpp" #include "StarWorldClient.hpp"
#include "StarWorldTemplate.hpp" #include "StarWorldTemplate.hpp"
#include "StarInput.hpp"
namespace Star { namespace Star {
@ -47,6 +48,8 @@ MaterialItem::MaterialItem(Json const& config, String const& directory, Json con
m_placeSounds.append(materialDatabase->defaultFootstepSound()); m_placeSounds.append(materialDatabase->defaultFootstepSound());
} }
m_shifting = false; m_shifting = false;
m_lastTileAreaRadiusCache = 0.0f;
m_collisionOverride = TileCollisionOverride::None;
} }
ItemPtr MaterialItem::clone() const { ItemPtr MaterialItem::clone() const {
@ -56,7 +59,7 @@ ItemPtr MaterialItem::clone() const {
void MaterialItem::init(ToolUserEntity* owner, ToolHand hand) { void MaterialItem::init(ToolUserEntity* owner, ToolHand hand) {
FireableItem::init(owner, hand); FireableItem::init(owner, hand);
BeamItem::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() { void MaterialItem::uninit() {
@ -71,6 +74,44 @@ void MaterialItem::update(float dt, FireMode fireMode, bool shifting, HashSet<Mo
else else
setEnd(BeamItem::EndType::TileGroup); setEnd(BeamItem::EndType::TileGroup);
m_shifting = shifting; m_shifting = shifting;
if (auto presses = Input::singleton().bindDown("opensb", "materialCycleCollision")) {
CollisionKind baseKind = Root::singleton().materialDatabase()->materialCollisionKind(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<Drawable> MaterialItem::nonRotatedDrawables() const { List<Drawable> 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); auto layer = (mode == FireMode::Primary || !twoHanded() ? TileLayer::Foreground : TileLayer::Background);
TileModificationList modifications; TileModificationList modifications;
float radius; float radius = calcRadius(shifting);
if (!shifting)
radius = m_blockRadius;
else
radius = m_altBlockRadius;
if (!multiplaceEnabled())
radius = 1;
auto geo = world()->geometry(); auto geo = world()->geometry();
auto aimPosition = owner()->aimPosition(); auto aimPosition = owner()->aimPosition();
auto& tilePositions = tileArea(radius);
if (!m_lastAimPosition) if (!m_lastAimPosition)
m_lastAimPosition = aimPosition; m_lastAimPosition = aimPosition;
@ -106,7 +141,7 @@ void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) {
float magnitude = diff.magnitude(); float magnitude = diff.magnitude();
float limit = max(4.f, 64.f / radius); float limit = max(4.f, 64.f / radius);
if (magnitude > limit) { if (magnitude > limit) {
m_lastAimPosition = aimPosition + diff.normalized() * limit; diff = diff.normalized() * limit;
magnitude = limit; magnitude = limit;
} }
@ -116,8 +151,8 @@ void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) {
size_t total = 0; size_t total = 0;
for (int i = 0; i != steps; ++i) { for (int i = 0; i != steps; ++i) {
auto placementOrigin = aimPosition + diff * (1.0f - ((float)i / steps)); auto placementOrigin = aimPosition + diff * (1.0f - ((float)i / steps));
for (Vec2I pos : tileAreaBrush(radius, placementOrigin, true)) for (Vec2I& pos : tilePositions)
modifications.append({ pos, PlaceMaterial{layer, materialId(), placementHueShift(pos)} }); modifications.append({ pos, PlaceMaterial{layer, materialId(), placementHueShift(pos), m_collisionOverride} });
// Make sure not to make any more modifications than we have consumables. // Make sure not to make any more modifications than we have consumables.
if (modifications.size() > count()) if (modifications.size() > count())
@ -147,6 +182,22 @@ MaterialId MaterialItem::materialId() const {
return m_material; return m_material;
} }
float MaterialItem::calcRadius(bool shifting) const {
if (!multiplaceEnabled())
return 1;
else
return !shifting ? m_blockRadius : m_altBlockRadius;
}
List<Vec2I>& 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 { MaterialHue MaterialItem::materialHueShift() const {
return m_materialHueShift; return m_materialHueShift;
} }
@ -155,16 +206,9 @@ bool MaterialItem::canPlace(bool shifting) const {
if (initialized()) { if (initialized()) {
MaterialId material = materialId(); MaterialId material = materialId();
float radius; float radius = calcRadius(shifting);
if (!shifting)
radius = m_blockRadius;
else
radius = m_altBlockRadius;
if (!multiplaceEnabled()) for (auto& pos : tileArea(radius)) {
radius = 1;
for (auto pos : tileAreaBrush(radius, owner()->aimPosition(), true)) {
MaterialHue hueShift = placementHueShift(pos); MaterialHue hueShift = placementHueShift(pos);
if (world()->canModifyTile(pos, PlaceMaterial{TileLayer::Foreground, material, hueShift}, false) if (world()->canModifyTile(pos, PlaceMaterial{TileLayer::Foreground, material, hueShift}, false)
|| world()->canModifyTile(pos, PlaceMaterial{TileLayer::Background, 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; return m_multiplace && count() > 1;
} }
float& MaterialItem::blockRadius() {
return m_blockRadius;
}
float& MaterialItem::altBlockRadius() {
return m_altBlockRadius;
}
TileCollisionOverride& MaterialItem::collisionOverride() {
return m_collisionOverride;
}
List<PreviewTile> MaterialItem::preview(bool shifting) const { List<PreviewTile> MaterialItem::preview(bool shifting) const {
List<PreviewTile> result; List<PreviewTile> result;
if (initialized()) { if (initialized()) {
@ -187,19 +243,8 @@ List<PreviewTile> MaterialItem::preview(bool shifting) const {
auto material = materialId(); auto material = materialId();
auto color = DefaultMaterialColorVariant; auto color = DefaultMaterialColorVariant;
float radius;
if (!shifting)
radius = m_blockRadius;
else
radius = m_altBlockRadius;
if (!multiplaceEnabled())
radius = 1;
size_t c = 0; size_t c = 0;
for (auto& pos : tileArea(calcRadius(shifting))) {
for (auto pos : tileAreaBrush(radius, owner()->aimPosition(), true)) {
MaterialHue hueShift = placementHueShift(pos); MaterialHue hueShift = placementHueShift(pos);
if (c >= count()) if (c >= count())
break; break;

View File

@ -5,12 +5,15 @@
#include "StarFireableItem.hpp" #include "StarFireableItem.hpp"
#include "StarBeamItem.hpp" #include "StarBeamItem.hpp"
#include "StarEntityRendering.hpp" #include "StarEntityRendering.hpp"
#include "StarPreviewTileTool.hpp"
#include "StarRenderableItem.hpp"
#include "StarCollisionBlock.hpp"
namespace Star { namespace Star {
STAR_CLASS(MaterialItem); STAR_CLASS(MaterialItem);
class MaterialItem : public Item, public FireableItem, public BeamItem { class MaterialItem : public Item, public FireableItem, public PreviewTileTool, public RenderableItem, public BeamItem {
public: public:
MaterialItem(Json const& config, String const& directory, Json const& settings); MaterialItem(Json const& config, String const& directory, Json const& settings);
virtual ~MaterialItem() {} virtual ~MaterialItem() {}
@ -20,6 +23,7 @@ public:
void init(ToolUserEntity* owner, ToolHand hand) override; void init(ToolUserEntity* owner, ToolHand hand) override;
void uninit() override; void uninit() override;
void update(float dt, FireMode fireMode, bool shifting, HashSet<MoveControlType> const& moves) override; void update(float dt, FireMode fireMode, bool shifting, HashSet<MoveControlType> const& moves) override;
void render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) override;
List<Drawable> nonRotatedDrawables() const override; List<Drawable> nonRotatedDrawables() const override;
@ -32,10 +36,14 @@ public:
bool canPlace(bool shifting) const; bool canPlace(bool shifting) const;
bool multiplaceEnabled() const; bool multiplaceEnabled() const;
// FIXME: Why isn't this a PreviewTileTool then?? float& blockRadius();
List<PreviewTile> preview(bool shifting) const; float& altBlockRadius();
TileCollisionOverride& collisionOverride();
List<PreviewTile> preview(bool shifting) const override;
private: private:
float calcRadius(bool shifting) const;
List<Vec2I>& tileArea(float radius) const;
MaterialHue placementHueShift(Vec2I const& position) const; MaterialHue placementHueShift(Vec2I const& position) const;
MaterialId m_material; MaterialId m_material;
@ -47,6 +55,10 @@ private:
bool m_multiplace; bool m_multiplace;
StringList m_placeSounds; StringList m_placeSounds;
Maybe<Vec2F> m_lastAimPosition; Maybe<Vec2F> m_lastAimPosition;
TileCollisionOverride m_collisionOverride;
mutable float m_lastTileAreaRadiusCache;
mutable List<Vec2I> m_tileAreasCache;
}; };
} }

View File

@ -1949,8 +1949,8 @@ namespace LuaBindings {
layerName = layerName.substr(0, split); layerName = layerName.substr(0, split);
if (overrideName == "empty" || overrideName == "none") if (overrideName == "empty" || overrideName == "none")
placeMaterial.collisionOverride = TileCollisionOverride::Empty; placeMaterial.collisionOverride = TileCollisionOverride::Empty;
else if (overrideName == "dynamic" || overrideName == "block") else if (overrideName == "block")
placeMaterial.collisionOverride = TileCollisionOverride::Dynamic; placeMaterial.collisionOverride = TileCollisionOverride::Block;
else if (overrideName == "platform") else if (overrideName == "platform")
placeMaterial.collisionOverride = TileCollisionOverride::Platform; placeMaterial.collisionOverride = TileCollisionOverride::Platform;
else else