osb/source/game/items/StarMaterialItem.cpp

233 lines
7.4 KiB
C++
Raw Normal View History

2023-06-20 04:33:09 +00:00
#include "StarMaterialItem.hpp"
#include "StarJson.hpp"
#include "StarJsonExtra.hpp"
2023-06-20 04:33:09 +00:00
#include "StarMaterialDatabase.hpp"
#include "StarRoot.hpp"
#include "StarAssets.hpp"
#include "StarWorld.hpp"
#include "StarWorldClient.hpp"
#include "StarWorldTemplate.hpp"
namespace Star {
MaterialItem::MaterialItem(Json const& config, String const& directory, Json const& settings)
: Item(config, directory, settings), FireableItem(config), BeamItem(config) {
m_material = config.getInt("materialId");
m_materialHueShift = materialHueFromDegrees(instanceValue("materialHueShift", 0).toFloat());
if (materialHueShift() != MaterialHue()) {
auto drawables = iconDrawables();
for (auto& d : drawables) {
2023-06-24 12:49:47 +00:00
if (d.isImage()) {
2023-06-27 10:23:44 +00:00
String image = strf("?hueshift={}", materialHueToDegrees(m_materialHueShift));
2023-06-24 12:49:47 +00:00
d.imagePart().addDirectives(image, false);
}
2023-06-20 04:33:09 +00:00
}
setIconDrawables(move(drawables));
}
setTwoHanded(config.getBool("twoHanded", true));
auto defaultParameters = Root::singleton().assets()->json("/items/defaultParameters.config");
setCooldownTime(config.queryFloat("materialItems.cooldown", defaultParameters.queryFloat("materialItems.cooldown")));
m_blockRadius = config.getFloat("blockRadius", defaultParameters.getFloat("blockRadius"));
m_altBlockRadius = config.getFloat("altBlockRadius", defaultParameters.getFloat("altBlockRadius"));
2023-06-20 04:33:09 +00:00
auto materialDatabase = Root::singleton().materialDatabase();
m_multiplace = config.getBool("allowMultiplace", BlockCollisionSet.contains(materialDatabase->materialCollisionKind(m_material)));
m_placeSounds = jsonToStringList(config.get("placeSounds", JsonArray()));
if (m_placeSounds.empty()) {
auto miningSound = materialDatabase->miningSound(m_material);
if (!miningSound.empty())
m_placeSounds.append(move(miningSound));
auto stepSound = materialDatabase->footstepSound(m_material);
if (!stepSound.empty())
m_placeSounds.append(move(stepSound));
else if (m_placeSounds.empty())
m_placeSounds.append(materialDatabase->defaultFootstepSound());
}
2023-06-20 04:33:09 +00:00
m_shifting = false;
}
ItemPtr MaterialItem::clone() const {
return make_shared<MaterialItem>(*this);
}
void MaterialItem::init(ToolUserEntity* owner, ToolHand hand) {
FireableItem::init(owner, hand);
BeamItem::init(owner, hand);
2023-08-19 02:34:35 +00:00
owner->addSound(Random::randValueFrom(m_placeSounds), 0.8f, 2.0f);
2023-06-20 04:33:09 +00:00
}
void MaterialItem::uninit() {
m_lastAimPosition.reset();
}
void MaterialItem::update(float dt, FireMode fireMode, bool shifting, HashSet<MoveControlType> const& moves) {
FireableItem::update(dt, fireMode, shifting, moves);
BeamItem::update(dt, fireMode, shifting, moves);
2023-06-20 04:33:09 +00:00
if (shifting || !multiplaceEnabled())
setEnd(BeamItem::EndType::Tile);
else
setEnd(BeamItem::EndType::TileGroup);
m_shifting = shifting;
}
List<Drawable> MaterialItem::nonRotatedDrawables() const {
return beamDrawables(canPlace(m_shifting));
}
void MaterialItem::fire(FireMode mode, bool shifting, bool edgeTriggered) {
if (!initialized() || !ready() || !owner()->inToolRange())
return;
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;
auto geo = world()->geometry();
auto aimPosition = owner()->aimPosition();
if (!m_lastAimPosition)
m_lastAimPosition = aimPosition;
unsigned steps = 1;
Vec2F diff = {};
if (*m_lastAimPosition != aimPosition) {
diff = geo.diff(*m_lastAimPosition, aimPosition);
float magnitude = diff.magnitude();
float limit = max(4.f, 64.f / radius);
if (magnitude > limit) {
m_lastAimPosition = aimPosition + diff.normalized() * limit;
magnitude = limit;
}
2023-06-20 04:33:09 +00:00
steps = (int)ceil(magnitude * (Constants::pi / 2));
}
2023-08-19 02:34:35 +00:00
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)} });
// Make sure not to make any more modifications than we have consumables.
if (modifications.size() > count())
modifications.resize(count());
size_t failed = world()->applyTileModifications(modifications, false).size();
if (failed < modifications.size()) {
2023-08-19 02:34:35 +00:00
size_t placed = modifications.size() - failed;
consume(placed);
total += placed;
}
2023-06-20 04:33:09 +00:00
}
2023-08-19 02:34:35 +00:00
if (total) {
float intensity = clamp((float)total / 96, 0.0f, 1.0f);
owner()->addSound(Random::randValueFrom(m_placeSounds), 1.0f + intensity, (1.125f - intensity * 0.75f) * Random::randf(0.9f, 1.1f));
FireableItem::fire(mode, shifting, edgeTriggered);
}
m_lastAimPosition = aimPosition;
}
void MaterialItem::endFire(FireMode mode, bool shifting) {
m_lastAimPosition.reset();
2023-06-20 04:33:09 +00:00
}
MaterialId MaterialItem::materialId() const {
return m_material;
}
MaterialHue MaterialItem::materialHueShift() const {
return m_materialHueShift;
}
bool MaterialItem::canPlace(bool shifting) const {
if (initialized()) {
MaterialId material = materialId();
float radius;
if (!shifting)
radius = m_blockRadius;
else
radius = m_altBlockRadius;
if (!multiplaceEnabled())
radius = 1;
for (auto pos : tileAreaBrush(radius, owner()->aimPosition(), true)) {
MaterialHue hueShift = placementHueShift(pos);
if (world()->canModifyTile(pos, PlaceMaterial{TileLayer::Foreground, material, hueShift}, false)
|| world()->canModifyTile(pos, PlaceMaterial{TileLayer::Background, material, hueShift}, false))
return true;
}
}
return false;
}
bool MaterialItem::multiplaceEnabled() const {
return m_multiplace && count() > 1;
}
List<PreviewTile> MaterialItem::preview(bool shifting) const {
List<PreviewTile> result;
if (initialized()) {
Color lightColor = Color::rgba(owner()->favoriteColor());
Vec3B light = lightColor.toRgb();
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)) {
MaterialHue hueShift = placementHueShift(pos);
if (c >= count())
break;
if (world()->canModifyTile(pos, PlaceMaterial{TileLayer::Foreground, material, hueShift}, false)) {
result.append({pos, true, material, hueShift, true});
c++;
} else if (twoHanded()
&& world()->canModifyTile(pos, PlaceMaterial{TileLayer::Background, material, hueShift}, false)) {
result.append({pos, true, material, hueShift, true, light, true, color});
c++;
} else {
result.append({pos, true, material, hueShift, true});
}
}
}
return result;
}
MaterialHue MaterialItem::placementHueShift(Vec2I const& pos) const {
if (auto hue = instanceValue("materialHueShift")) {
return materialHueFromDegrees(hue.toFloat());
} else if (auto worldClient = as<WorldClient>(world())) {
auto worldTemplate = worldClient->currentTemplate();
return worldTemplate->biomeMaterialHueShift(worldTemplate->blockBiomeIndex(pos[0], pos[1]), m_material);
} else {
return materialHueShift();
}
}
}