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": {
"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"
}
}
}

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/StarPortraitEntity.hpp
interfaces/StarPreviewableItem.hpp
interfaces/StarRenderableItem.hpp
interfaces/StarScriptedEntity.hpp
interfaces/StarStatusEffectEntity.hpp
interfaces/StarStatusEffectItem.hpp

View File

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

View File

@ -535,21 +535,16 @@ void ToolUser::render(RenderCallback* renderCallback, bool inToolRange, bool shi
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()))
renderCallback->addTilePreviews(pri->preview(shifting));
else if (auto alt = as<PreviewTileTool>(m_altHandItem.get()))
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()}) {
if (auto activeItem = as<ActiveItem>(item)) {
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 "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, HashSet<Mo
else
setEnd(BeamItem::EndType::TileGroup);
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 {
@ -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<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 {
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<PreviewTile> MaterialItem::preview(bool shifting) const {
List<PreviewTile> result;
if (initialized()) {
@ -187,19 +243,8 @@ List<PreviewTile> 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;

View File

@ -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<MoveControlType> const& moves) override;
void render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) override;
List<Drawable> 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<PreviewTile> preview(bool shifting) const;
float& blockRadius();
float& altBlockRadius();
TileCollisionOverride& collisionOverride();
List<PreviewTile> preview(bool shifting) const override;
private:
float calcRadius(bool shifting) const;
List<Vec2I>& 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<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);
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