osb/source/game/StarPlantDrop.cpp
Kai Blaschke 431a9c00a5
Fixed a huge amount of Clang warnings
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably.

99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified.

Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects.

Most remaining warnings are now unused parameters.
2024-02-19 16:55:19 +01:00

382 lines
12 KiB
C++

#include "StarPlantDrop.hpp"
#include "StarDataStreamExtra.hpp"
#include "StarPlayer.hpp"
#include "StarRoot.hpp"
#include "StarImageMetadataDatabase.hpp"
#include "StarItemDrop.hpp"
#include "StarAssets.hpp"
#include "StarEntityRendering.hpp"
#include "StarWorld.hpp"
#include "StarRandom.hpp"
#include "StarParticleDatabase.hpp"
namespace Star {
PlantDrop::PlantDropPiece::PlantDropPiece() {
image = "";
offset = {};
segmentIdx = 0;
kind = Plant::PlantPieceKind::None;
flip = false;
}
PlantDrop::PlantDrop(List<Plant::PlantPiece> pieces, Vec2F const& position, Vec2F const& strikeVector, String const& description,
bool upsideDown, Json stemConfig, Json foliageConfig, Json saplingConfig, bool master, float random) {
m_netGroup.addNetElement(&m_movementController);
m_netGroup.addNetElement(&m_spawnedDrops);
m_stemConfig = stemConfig;
if (m_stemConfig.isNull())
m_stemConfig = JsonObject();
m_foliageConfig = foliageConfig;
if (m_foliageConfig.isNull())
m_foliageConfig = JsonObject();
m_saplingConfig = saplingConfig;
m_master = master;
m_firstTick = true;
m_spawnedDrops.set(false);
m_spawnedDropEffects = false;
m_time = 5000;
m_description = description;
m_movementController.setPosition(position);
if (!upsideDown) {
m_rotationRate = copysign(0.00001f, -strikeVector[0] + random);
m_rotationFallThreshold = Constants::pi / (3 + random);
m_rotationCap = Constants::pi - m_rotationFallThreshold;
} else {
m_rotationRate = 0;
m_rotationFallThreshold = 0;
m_rotationCap = 0;
}
bool structuralFound = false;
RectF stemBounds = RectF::null();
RectF fullBounds = RectF::null();
// note structuralSegment is only available in the constructor
for (auto& piece : pieces) {
for (auto& pos : piece.spaces) {
fullBounds.combine(Vec2F(pos));
fullBounds.combine(Vec2F(pos) + Vec2F(1, 1));
if (piece.structuralSegment) {
structuralFound = true;
stemBounds.combine(Vec2F(pos));
stemBounds.combine(Vec2F(pos) + Vec2F(1, 1));
}
}
PlantDropPiece pdp;
pdp.image = piece.image;
pdp.offset = piece.offset;
pdp.segmentIdx = piece.segmentIdx;
pdp.kind = piece.kind;
pdp.flip = piece.flip;
m_pieces.append(pdp);
}
if (fullBounds.isNull())
fullBounds = RectF(position, position);
if (stemBounds.isNull())
stemBounds = RectF(position, position);
m_boundingBox = fullBounds;
if (structuralFound)
m_collisionRect = stemBounds;
else
m_collisionRect = fullBounds;
}
PlantDrop::PlantDrop(ByteArray const& netStore) {
m_netGroup.addNetElement(&m_movementController);
m_netGroup.addNetElement(&m_spawnedDrops);
DataStreamBuffer ds(netStore);
ds >> m_time;
ds >> m_master;
ds >> m_description;
ds >> m_boundingBox;
ds >> m_collisionRect;
ds >> m_rotationRate;
ds.readContainer(m_pieces,
[](DataStream& ds, PlantDropPiece& piece) {
ds.read(piece.image);
ds.read(piece.offset[0]);
ds.read(piece.offset[1]);
ds.read(piece.flip);
ds.read(piece.kind);
});
ds >> m_stemConfig;
ds >> m_foliageConfig;
ds >> m_saplingConfig;
m_firstTick = true;
m_spawnedDropEffects = true;
}
ByteArray PlantDrop::netStore() {
DataStreamBuffer ds;
ds << m_time;
ds << m_master;
ds << m_description;
ds << m_boundingBox;
ds << m_collisionRect;
ds << m_rotationRate;
ds.writeContainer(m_pieces,
[](DataStream& ds, PlantDropPiece const& piece) {
ds.write(piece.image);
ds.write(piece.offset[0]);
ds.write(piece.offset[1]);
ds.write(piece.flip);
ds.write(piece.kind);
});
ds << m_stemConfig;
ds << m_foliageConfig;
ds << m_saplingConfig;
return ds.data();
}
EntityType PlantDrop::entityType() const {
return EntityType::PlantDrop;
}
void PlantDrop::init(World* world, EntityId entityId, EntityMode mode) {
Entity::init(world, entityId, mode);
m_movementController.init(world);
PolyF collisionPoly = PolyF(RectF::withCenter(m_collisionRect.center(), m_collisionRect.size() / 2.0f));
MovementParameters parameters;
parameters.collisionPoly = collisionPoly;
parameters.ignorePlatformCollision = true;
parameters.gravityMultiplier = 0.2f;
parameters.physicsEffectCategories = StringSet({"plantdrop"});
m_movementController.applyParameters(parameters);
}
void PlantDrop::uninit() {
Entity::uninit();
m_movementController.uninit();
}
String PlantDrop::description() const {
return m_description;
}
Vec2F PlantDrop::position() const {
return m_movementController.position();
}
RectF PlantDrop::metaBoundBox() const {
return m_boundingBox;
}
RectF PlantDrop::collisionRect() const {
PolyF shape = PolyF(m_collisionRect);
shape.rotate(m_movementController.rotation());
return shape.boundBox();
}
void PlantDrop::update(float dt, uint64_t) {
m_time -= dt;
m_movementController.setTimestep(dt);
if (isMaster()) {
if (m_spawnedDropEffects && !m_spawnedDrops.get())
m_spawnedDropEffects = false; // false positive assumption over already having done the effect
// to avoid effects for newly joining players.
if (m_spawnedDrops.get())
m_firstTick = false;
// think up a better curve then sin
auto rotationAcceleration = 0.01f * world()->gravity(position()) * copysign(1.0f, m_rotationRate) * dt;
if (abs(m_movementController.rotation()) > m_rotationCap)
m_rotationRate -= rotationAcceleration;
else if (std::fabs(m_movementController.rotation()) < m_rotationFallThreshold)
m_rotationRate += rotationAcceleration;
m_movementController.rotate(m_rotationRate);
PolyF collisionPoly = PolyF(RectF::withCenter(m_collisionRect.center(), m_collisionRect.size() / 2.0f));
if (m_time > 0) {
MovementParameters parameters;
parameters.collisionPoly = collisionPoly;
parameters.gravityEnabled = std::fabs(m_movementController.rotation()) >= m_rotationFallThreshold;
m_movementController.applyParameters(parameters);
m_movementController.tickMaster(dt);
if (m_movementController.onGround())
m_time = 0;
}
auto imgMetadata = Root::singleton().imageMetadataDatabase();
if ((m_time <= 0 || world()->gravity(position()) == 0) && !m_spawnedDrops.get()) {
m_spawnedDrops.set(true);
for (auto& plantPiece : m_pieces) {
JsonArray dropOptions;
if (plantPiece.kind == Plant::PlantPieceKind::Stem)
dropOptions = m_stemConfig.getArray("drops", {});
if (plantPiece.kind == Plant::PlantPieceKind::Foliage)
dropOptions = m_foliageConfig.getArray("drops", {});
if (dropOptions.size()) {
auto option = Random::randFrom(dropOptions).toArray();
for (auto drop : option) {
auto size = imgMetadata->imageSize(plantPiece.image);
Vec2F pos = Vec2F(plantPiece.offset + Vec2F(size) * .5f / TilePixels).rotate(m_movementController.rotation())
+ Vec2F(Random::randf(-0.2f, 0.2f), Random::randf(-0.2f, 0.2f));
if (drop.getString("item") == "sapling")
world()->addEntity(ItemDrop::createRandomizedDrop(
ItemDescriptor("sapling", (size_t)drop.getInt("count", 1), m_saplingConfig), position() + pos));
else
world()->addEntity(ItemDrop::createRandomizedDrop(
{drop.getString("item"), (size_t)drop.getInt("count", 1)}, position() + pos));
}
}
}
}
} else {
m_netGroup.tickNetInterpolation(GlobalTimestep);
if (m_spawnedDropEffects && !m_spawnedDrops.get())
m_spawnedDropEffects = false; // false positive assumption over already having done the effect
// to avoid effects for newly joining players.
if (m_spawnedDrops.get())
m_firstTick = false;
m_movementController.tickSlave(dt);
}
}
void PlantDrop::destroy(RenderCallback* renderCallback) {
if (renderCallback)
render(renderCallback);
}
void PlantDrop::particleForPlantPart(PlantDropPiece const& piece, String const& mode, Json const& mainConfig, RenderCallback* renderCallback) {
Json particleConfig = mainConfig.get("particles", JsonObject()).get(mode, JsonObject());
JsonArray particleOptions = particleConfig.getArray("options", {});
if (!particleOptions.size())
return;
Particle particle;
auto imgMetadata = Root::singleton().imageMetadataDatabase();
Vec2F imageSize = Vec2F(imgMetadata->imageSize(piece.image)) / TilePixels;
float density = (imageSize.x() * imageSize.y()) / particleConfig.getFloat("density", 1);
auto spaces = Set<Vec2I>::from(imgMetadata->imageSpaces(piece.image, piece.offset * TilePixels, Plant::PlantScanThreshold, piece.flip));
if (spaces.empty())
return;
while (density > 0) {
Vec2F particlePos = piece.offset + Vec2F(imageSize) / 2.0f + Vec2F(Random::nrandf(imageSize.x() / 8.0f, 0), Random::nrandf(imageSize.y() / 8.0f, 0));
if (!spaces.contains(Vec2I(particlePos.floor())))
continue;
auto config = Random::randValueFrom(particleOptions, {});
particle = Root::singleton().particleDatabase()->particle(config);
particle.color.hueShift(mainConfig.getFloat("hueshift", 0) / 360.0f);
for (Directives const& directives : piece.image.directives.list())
particle.directives.append(directives);
density--;
particle.position = position() + particlePos.rotate(m_movementController.rotation());
renderCallback->addParticle(particle);
}
}
void PlantDrop::render(RenderCallback* renderCallback) {
auto assets = Root::singleton().assets();
if (m_firstTick) {
m_firstTick = false;
// smoke, particles
if (m_master) {
auto playBreakSound = [&](Json const& config) {
JsonArray breakTreeOptions = config.get("sounds", JsonObject()).getArray("breakTree", JsonArray());
if (breakTreeOptions.size()) {
auto sound = Random::randFrom(breakTreeOptions);
auto audioInstance = make_shared<AudioInstance>(*assets->audio(sound.getString("file")));
audioInstance->setPosition(collisionRect().center() + position());
audioInstance->setVolume(sound.getFloat("volume", 1.0f));
renderCallback->addAudio(std::move(audioInstance));
}
};
playBreakSound(m_stemConfig);
playBreakSound(m_foliageConfig);
}
for (auto const& plantPiece : m_pieces) {
if (plantPiece.kind == Plant::PlantPieceKind::Stem)
particleForPlantPart(plantPiece, "breakTree", m_stemConfig, renderCallback);
if (plantPiece.kind == Plant::PlantPieceKind::Foliage)
particleForPlantPart(plantPiece, "breakTree", m_foliageConfig, renderCallback);
}
}
if (m_spawnedDrops.get() && !m_spawnedDropEffects) {
m_spawnedDropEffects = true;
// smoke, particles
auto playHitSound = [&](Json const& config) {
JsonArray hitGroundOptions = config.get("sounds", JsonObject()).getArray("hitGround", JsonArray());
if (hitGroundOptions.size()) {
auto sound = Random::randFrom(hitGroundOptions);
auto audioInstance = make_shared<AudioInstance>(*assets->audio(sound.getString("file")));
audioInstance->setPosition(collisionRect().center() + position());
audioInstance->setVolume(sound.getFloat("volume", 1.0f));
renderCallback->addAudio(std::move(audioInstance));
}
};
playHitSound(m_stemConfig);
playHitSound(m_foliageConfig);
for (auto const& plantPiece : m_pieces) {
if (plantPiece.kind == Plant::PlantPieceKind::Stem)
particleForPlantPart(plantPiece, "hitGround", m_stemConfig, renderCallback);
if (plantPiece.kind == Plant::PlantPieceKind::Foliage)
particleForPlantPart(plantPiece, "hitGround", m_foliageConfig, renderCallback);
}
}
if (m_time > 0 && !m_spawnedDrops.get()) {
for (auto const& plantPiece : m_pieces) {
auto drawable = Drawable::makeImage(plantPiece.image, 1.0f / TilePixels, false, plantPiece.offset);
if (plantPiece.flip)
drawable.scale(Vec2F(-1, 1));
drawable.rotate(m_movementController.rotation());
drawable.translate(position());
renderCallback->addDrawable(std::move(drawable), RenderLayerPlantDrop);
}
}
}
pair<ByteArray, uint64_t> PlantDrop::writeNetState(uint64_t fromVersion) {
return m_netGroup.writeNetState(fromVersion);
}
void PlantDrop::readNetState(ByteArray data, float interpolationTime) {
m_netGroup.readNetState(std::move(data), interpolationTime);
}
void PlantDrop::enableInterpolation(float extrapolationHint) {
m_netGroup.enableNetInterpolation(extrapolationHint);
}
void PlantDrop::disableInterpolation() {
m_netGroup.disableNetInterpolation();
}
bool PlantDrop::shouldDestroy() const {
return m_time <= 0.0f;
}
}