diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/game/StarToolUser.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/game/StarToolUser.cpp')
-rw-r--r-- | source/game/StarToolUser.cpp | 858 |
1 files changed, 858 insertions, 0 deletions
diff --git a/source/game/StarToolUser.cpp b/source/game/StarToolUser.cpp new file mode 100644 index 0000000..f3b4d5d --- /dev/null +++ b/source/game/StarToolUser.cpp @@ -0,0 +1,858 @@ +#include "StarToolUser.hpp" +#include "StarRoot.hpp" +#include "StarItemDatabase.hpp" +#include "StarArmors.hpp" +#include "StarCasting.hpp" +#include "StarImageProcessing.hpp" +#include "StarLiquidItem.hpp" +#include "StarMaterialItem.hpp" +#include "StarObject.hpp" +#include "StarTools.hpp" +#include "StarActivatableItem.hpp" +#include "StarObjectItem.hpp" +#include "StarAssets.hpp" +#include "StarObjectDatabase.hpp" +#include "StarWorld.hpp" +#include "StarActiveItem.hpp" +#include "StarStatusController.hpp" +#include "StarInspectionTool.hpp" + +namespace Star { + +ToolUser::ToolUser() + : m_beamGunRadius(), m_beamGunGlowBorder(), m_objectPreviewInnerAlpha(), m_objectPreviewOuterAlpha(), m_user(nullptr), + m_fireMain(), m_fireAlt(), m_edgeTriggeredMain(), m_edgeTriggeredAlt(), m_edgeSuppressedMain(), m_edgeSuppressedAlt(), + m_suppress() { + auto assets = Root::singleton().assets(); + m_beamGunRadius = assets->json("/player.config:initialBeamGunRadius").toFloat(); + m_beamGunGlowBorder = assets->json("/player.config:previewGlowBorder").toInt(); + m_objectPreviewInnerAlpha = assets->json("/player.config:objectPreviewInnerAlpha").toFloat(); + m_objectPreviewOuterAlpha = assets->json("/player.config:objectPreviewOuterAlpha").toFloat(); + + addNetElement(&m_primaryHandItem); + addNetElement(&m_altHandItem); + addNetElement(&m_primaryFireTimerNetState); + addNetElement(&m_altFireTimerNetState); + addNetElement(&m_primaryTimeFiringNetState); + addNetElement(&m_altTimeFiringNetState); + addNetElement(&m_primaryItemActiveNetState); + addNetElement(&m_altItemActiveNetState); + + m_primaryFireTimerNetState.setFixedPointBase(WorldTimestep); + m_altFireTimerNetState.setFixedPointBase(WorldTimestep); + m_primaryTimeFiringNetState.setFixedPointBase(WorldTimestep); + m_altTimeFiringNetState.setFixedPointBase(WorldTimestep); + + auto interpolateTimer = [](double offset, double min, double max) -> double { + if (max > min) + return min + offset * (max - min); + else + return max; + }; + + m_primaryFireTimerNetState.setInterpolator(interpolateTimer); + m_altFireTimerNetState.setInterpolator(interpolateTimer); + m_primaryTimeFiringNetState.setInterpolator(interpolateTimer); + m_altTimeFiringNetState.setInterpolator(interpolateTimer); +} + +Json ToolUser::diskStore() const { + JsonObject res; + if (m_primaryHandItem.get()) + res["primaryHandItem"] = m_primaryHandItem.get()->descriptor().diskStore(); + if (m_altHandItem.get()) + res["altHandItem"] = m_altHandItem.get()->descriptor().diskStore(); + + return res; +} + +void ToolUser::diskLoad(Json const& diskStore) { + auto itemDb = Root::singleton().itemDatabase(); + m_primaryHandItem.set(itemDb->diskLoad(diskStore.get("primaryHandItem", {}))); + m_altHandItem.set(itemDb->diskLoad(diskStore.get("altHandItem", {}))); +} + +ItemPtr ToolUser::primaryHandItem() const { + return m_primaryHandItem.get(); +} + +ItemPtr ToolUser::altHandItem() const { + return m_altHandItem.get(); +} + +ItemDescriptor ToolUser::primaryHandItemDescriptor() const { + if (m_primaryHandItem.get()) + return m_primaryHandItem.get()->descriptor(); + return {}; +} + +ItemDescriptor ToolUser::altHandItemDescriptor() const { + if (m_altHandItem.get()) + return m_altHandItem.get()->descriptor(); + return {}; +} + +void ToolUser::init(ToolUserEntity* user) { + m_user = user; + + initPrimaryHandItem(); + if (!itemSafeTwoHanded(m_primaryHandItem.get())) + initAltHandItem(); +} + +void ToolUser::uninit() { + m_user = nullptr; + uninitItem(m_primaryHandItem.get()); + uninitItem(m_altHandItem.get()); +} + +List<LightSource> ToolUser::lightSources() const { + if (m_suppress.get() || !m_user) + return {}; + + List<LightSource> lights; + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto activeItem = as<ActiveItem>(item)) + lights.appendAll(activeItem->lights()); + + if (auto flashlight = as<Flashlight>(item)) + lights.appendAll(flashlight->lightSources()); + + if (auto inspectionTool = as<InspectionTool>(item)) + lights.appendAll(inspectionTool->lightSources()); + } + + return lights; +} + +void ToolUser::effects(EffectEmitter& emitter) const { + if (m_suppress.get()) + return; + + if (auto item = as<EffectSourceItem>(m_primaryHandItem.get())) + emitter.addEffectSources("primary", item->effectSources()); + if (auto item = as<EffectSourceItem>(m_altHandItem.get())) + emitter.addEffectSources("alt", item->effectSources()); +} + +List<PersistentStatusEffect> ToolUser::statusEffects() const { + if (m_suppress.get()) + return {}; + + List<PersistentStatusEffect> statusEffects; + auto addStatusFromItem = [&](ItemPtr const& item) { + if (auto effectItem = as<StatusEffectItem>(item)) + statusEffects.appendAll(effectItem->statusEffects()); + }; + + if (!is<ArmorItem>(m_primaryHandItem.get())) + addStatusFromItem(m_primaryHandItem.get()); + + if (!is<ArmorItem>(m_altHandItem.get())) + addStatusFromItem(m_altHandItem.get()); + + return statusEffects; +} + +Maybe<float> ToolUser::toolRadius() const { + if (m_suppress.get()) + return {}; + else if (is<BeamItem>(m_primaryHandItem.get()) || is<BeamItem>(m_altHandItem.get())) + return beamGunRadius(); + else if (is<WireTool>(m_primaryHandItem.get()) || is<WireTool>(m_altHandItem.get())) + return beamGunRadius(); + return {}; +} + +List<Drawable> ToolUser::renderObjectPreviews(Vec2F aimPosition, Direction walkingDirection, bool inToolRange, Vec4B favoriteColor) const { + if (m_suppress.get() || !m_user) + return {}; + + auto generate = [&](ObjectItemPtr item) -> List<Drawable> { + auto objectDatabase = Root::singleton().objectDatabase(); + + Vec2I aimPos(aimPosition.floor()); + auto drawables = objectDatabase->cursorHintDrawables(m_user->world(), item->objectName(), + aimPos, walkingDirection, item->objectParameters()); + + Color opacityMask = Color::White; + opacityMask.setAlphaF(item->getAppropriateOpacity()); + Vec4B favoriteColorTrans; + if (inToolRange && objectDatabase->canPlaceObject(m_user->world(), aimPos, item->objectName())) { + favoriteColorTrans = favoriteColor; + } else { + Color color = Color::rgba(favoriteColor); + color.setHue(color.hue() + 120); + favoriteColorTrans = color.toRgba(); + } + + favoriteColorTrans[3] = m_objectPreviewOuterAlpha * 255; + Color nearWhite = Color::rgba(favoriteColorTrans); + nearWhite.setValue(1 - (1 - nearWhite.value()) / 5); + nearWhite.setSaturation(nearWhite.saturation() / 5); + nearWhite.setAlphaF(m_objectPreviewInnerAlpha); + ImageOperation op = BorderImageOperation{m_beamGunGlowBorder, nearWhite.toRgba(), favoriteColorTrans, false}; + + for (Drawable& drawable : drawables) { + if (drawable.isImage()) + drawable.imagePart().addDirectives(imageOperationToString(op), true); + drawable.color = opacityMask; + } + return drawables; + }; + + if (auto pri = as<ObjectItem>(m_primaryHandItem.get())) + return generate(pri); + else if (auto alt = as<ObjectItem>(m_altHandItem.get())) + return generate(alt); + else + return {}; +} + +Maybe<Direction> ToolUser::setupHumanoidHandItems(Humanoid& humanoid, Vec2F position, Vec2F aimPosition) const { + if (m_suppress.get() || !m_user) { + humanoid.setPrimaryHandParameters(false, 0.0f, 0.0f, false, false, false); + humanoid.setAltHandParameters(false, 0.0f, 0.0f, false, false); + return {}; + } + + auto inner = [&](bool primary) -> Maybe<Direction> { + Maybe<Direction> overrideFacingDirection; + + auto setRotation = [&](bool holdingItem, float angle, float itemAngle, bool twoHanded, bool recoil, bool outsideOfHand) { + if (primary || twoHanded) + humanoid.setPrimaryHandParameters(holdingItem, angle, itemAngle, twoHanded, recoil, outsideOfHand); + else + humanoid.setAltHandParameters(holdingItem, angle, itemAngle, recoil, outsideOfHand); + }; + + ItemPtr handItem = primary ? m_primaryHandItem.get() : m_altHandItem.get(); + + auto angleSide = getAngleSide(m_user->world()->geometry().diff(aimPosition, position).angle()); + + if (auto swingItem = as<SwingableItem>(handItem)) { + float angle = swingItem->getAngleDir(angleSide.first, angleSide.second); + bool handedness = handItem->twoHanded(); + setRotation(true, angle, swingItem->getItemAngle(angleSide.first), handedness, false, false); + overrideFacingDirection = angleSide.second; + + } else if (auto pointableItem = as<PointableItem>(handItem)) { + float angle = pointableItem->getAngleDir(angleSide.first, angleSide.second); + setRotation(true, angle, angle, handItem->twoHanded(), false, false); + overrideFacingDirection = angleSide.second; + + } else if (auto activeItem = as<ActiveItem>(handItem)) { + setRotation(activeItem->holdingItem(), activeItem->armAngle(), activeItem->armAngle(), + activeItem->twoHandedGrip(), activeItem->recoil(), activeItem->outsideOfHand()); + if (auto fd = activeItem->facingDirection()) + overrideFacingDirection = *fd; + + } else if (auto beamItem = as<BeamItem>(handItem)) { + float angle = beamItem->getAngle(angleSide.first); + setRotation(true, angle, angle, false, false, false); + overrideFacingDirection = angleSide.second; + + } else { + setRotation(false, 0.0f, 0.0f, false, false, false); + } + + return overrideFacingDirection; + }; + + Maybe<Direction> overrideFacingDirection; + overrideFacingDirection = overrideFacingDirection.orMaybe(inner(true)); + if (itemSafeTwoHanded(m_primaryHandItem.get())) + humanoid.setAltHandParameters(false, 0.0f, 0.0f, false, false); + else + overrideFacingDirection = overrideFacingDirection.orMaybe(inner(false)); + + return overrideFacingDirection; +} + +void ToolUser::setupHumanoidHandItemDrawables(Humanoid& humanoid) const { + if (m_suppress.get() || !m_user) { + humanoid.setPrimaryHandFrameOverrides("", ""); + humanoid.setAltHandFrameOverrides("", ""); + humanoid.setPrimaryHandDrawables({}); + humanoid.setAltHandDrawables({}); + humanoid.setPrimaryHandNonRotatedDrawables({}); + humanoid.setAltHandNonRotatedDrawables({}); + return; + } + + auto inner = [&](bool primary) { + auto setRotated = [&](String const& backFrameOverride, String const& frontFrameOverride, List<Drawable> drawables, bool twoHanded) { + if (primary || twoHanded) { + humanoid.setPrimaryHandFrameOverrides(backFrameOverride, frontFrameOverride); + humanoid.setPrimaryHandDrawables(move(drawables)); + } else { + humanoid.setAltHandFrameOverrides(backFrameOverride, frontFrameOverride); + humanoid.setAltHandDrawables(move(drawables)); + } + }; + + auto setNonRotated = [&](List<Drawable> drawables) { + if (primary) + humanoid.setPrimaryHandNonRotatedDrawables(move(drawables)); + else + humanoid.setAltHandNonRotatedDrawables(move(drawables)); + }; + + ItemPtr handItem = primary ? m_primaryHandItem.get() : m_altHandItem.get(); + + if (auto swingItem = as<SwingableItem>(handItem)) { + setRotated(swingItem->getArmFrame(), swingItem->getArmFrame(), swingItem->drawables(), handItem->twoHanded()); + + } else if (auto pointableItem = as<PointableItem>(handItem)) { + setRotated("", "", pointableItem->drawables(), handItem->twoHanded()); + + } else if (auto activeItem = as<ActiveItem>(handItem)) { + setRotated(activeItem->backArmFrame().value(), activeItem->frontArmFrame().value(), + activeItem->handDrawables(), activeItem->twoHandedGrip()); + + } else if (auto beamItem = as<BeamItem>(handItem)) { + setRotated("", "", beamItem->drawables(), false); + + } else { + setRotated("", "", {}, false); + } + + if (auto drawItem = as<NonRotatedDrawablesItem>(handItem)) + setNonRotated(drawItem->nonRotatedDrawables()); + else + setNonRotated({}); + }; + + inner(true); + if (itemSafeTwoHanded(m_primaryHandItem.get())) { + humanoid.setAltHandFrameOverrides("", ""); + humanoid.setAltHandDrawables({}); + humanoid.setAltHandNonRotatedDrawables({}); + } else { + inner(false); + } +} + +Vec2F ToolUser::armPosition(Humanoid const& humanoid, ToolHand hand, Direction facingDirection, float armAngle, Vec2F offset) const { + if (hand == ToolHand::Primary) + return humanoid.primaryArmPosition(facingDirection, armAngle, offset); + else + return humanoid.altArmPosition(facingDirection, armAngle, offset); +} + +Vec2F ToolUser::handOffset(Humanoid const& humanoid, ToolHand hand, Direction direction) const { + if (hand == ToolHand::Primary) + return humanoid.primaryHandOffset(direction); + else + return humanoid.altHandOffset(direction); +} + +Vec2F ToolUser::handPosition(ToolHand hand, Humanoid const& humanoid, Vec2F const& handOffset) const { + if (hand == ToolHand::Primary) + return humanoid.primaryHandPosition(handOffset); + else + return humanoid.altHandPosition(handOffset); +} + +bool ToolUser::queryShieldHit(DamageSource const& source) const { + if (m_suppress.get() || !m_user) + return false; + + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto tool = as<ToolUserItem>(item)) + for (auto poly : tool->shieldPolys()) { + poly.translate(m_user->position()); + if (source.intersectsWithPoly(m_user->world()->geometry(), poly)) + return true; + } + } + return false; +} + +void ToolUser::tick(bool shifting, HashSet<MoveControlType> const& moves) { + if (auto toolUserItem = as<ToolUserItem>(m_primaryHandItem.get())) { + FireMode fireMode = FireMode::None; + if (!m_suppress.get()) { + if (m_fireMain) + fireMode = FireMode::Primary; + else if (m_fireAlt && m_primaryHandItem.get()->twoHanded()) + fireMode = FireMode::Alt; + } + toolUserItem->update(fireMode, shifting, moves); + } + + if (!m_primaryHandItem.get() || !m_primaryHandItem.get()->twoHanded()) { + if (auto toolUserItem = as<ToolUserItem>(m_altHandItem.get())) { + FireMode fireMode = FireMode::None; + if (!m_suppress.get() && m_fireAlt) + fireMode = FireMode::Primary; + toolUserItem->update(fireMode, shifting, moves); + } + } + + bool edgeTriggeredMain = m_edgeTriggeredMain; + m_edgeTriggeredMain = false; + bool edgeTriggeredAlt = m_edgeTriggeredAlt; + m_edgeTriggeredAlt = false; + + bool edgeSuppressedMain = m_edgeSuppressedMain; + m_edgeSuppressedMain = false; + bool edgeSuppressedAlt = m_edgeSuppressedAlt; + m_edgeSuppressedAlt = false; + + if (!m_suppress.get()) { + if (itemSafeTwoHanded(m_primaryHandItem.get()) && (m_fireMain || m_fireAlt)) { + FireableItemPtr fireableItem = as<FireableItem>(m_primaryHandItem.get()); + if (fireableItem) { + FireMode mode; + if (m_fireMain) + mode = FireMode::Primary; + else + mode = FireMode::Alt; + fireableItem->fire(mode, shifting, edgeTriggeredMain || edgeTriggeredAlt); + } + ActivatableItemPtr activatableItem = as<ActivatableItem>(m_primaryHandItem.get()); + if (activatableItem && activatableItem->usable()) + activatableItem->activate(); + } else if (edgeSuppressedMain || (itemSafeTwoHanded(m_primaryHandItem.get()) && edgeSuppressedAlt)) { + FireableItemPtr fireableItem = as<FireableItem>(m_primaryHandItem.get()); + if (fireableItem) { + FireMode mode = FireMode::Primary; + fireableItem->endFire(mode, shifting); + } + + if (m_fireAlt) { + FireableItemPtr fireableItem = as<FireableItem>(m_altHandItem.get()); + if (fireableItem) + fireableItem->fire(FireMode::Alt, shifting, edgeTriggeredAlt); + ActivatableItemPtr activatableItem = as<ActivatableItem>(m_altHandItem.get()); + if (activatableItem && activatableItem->usable()) + activatableItem->activate(); + } + + } else if (edgeSuppressedAlt) { + FireableItemPtr fireableItem = as<FireableItem>(m_altHandItem.get()); + if (fireableItem) { + FireMode mode = FireMode::Alt; + fireableItem->endFire(mode, shifting); + } + + if (m_fireMain) { + FireableItemPtr fireableItem = as<FireableItem>(m_primaryHandItem.get()); + if (fireableItem) + fireableItem->fire(FireMode::Primary, shifting, edgeTriggeredMain); + ActivatableItemPtr activatableItem = as<ActivatableItem>(m_primaryHandItem.get()); + if (activatableItem && activatableItem->usable()) + activatableItem->activate(); + } + + } else { + if (m_fireMain) { + FireableItemPtr fireableItem = as<FireableItem>(m_primaryHandItem.get()); + if (fireableItem) + fireableItem->fire(FireMode::Primary, shifting, edgeTriggeredMain); + ActivatableItemPtr activatableItem = as<ActivatableItem>(m_primaryHandItem.get()); + if (activatableItem && activatableItem->usable()) + activatableItem->activate(); + } + if (m_fireAlt) { + FireableItemPtr fireableItem = as<FireableItem>(m_altHandItem.get()); + if (fireableItem) + fireableItem->fire(FireMode::Alt, shifting, edgeTriggeredAlt); + ActivatableItemPtr activatableItem = as<ActivatableItem>(m_altHandItem.get()); + if (activatableItem && activatableItem->usable()) + activatableItem->activate(); + } + } + } +} + +void ToolUser::beginPrimaryFire() { + if (!m_fireMain) + m_edgeTriggeredMain = true; + m_fireMain = true; +} + +void ToolUser::beginAltFire() { + if (!m_fireAlt) + m_edgeTriggeredAlt = true; + m_fireAlt = true; +} + +void ToolUser::endPrimaryFire() { + if (m_fireMain) + m_edgeSuppressedMain = true; + m_fireMain = false; +} + +void ToolUser::endAltFire() { + if (m_fireAlt) + m_edgeSuppressedAlt = true; + m_fireAlt = false; +} + +bool ToolUser::firingPrimary() const { + return m_fireMain; +} + +bool ToolUser::firingAlt() const { + return m_fireAlt; +} + +List<DamageSource> ToolUser::damageSources() const { + if (m_suppress.get()) + return {}; + + List<DamageSource> ds; + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto toolItem = as<ToolUserItem>(item)) + ds.appendAll(toolItem->damageSources()); + } + return ds; +} + +List<PhysicsForceRegion> ToolUser::forceRegions() const { + if (m_suppress.get()) + return {}; + + List<PhysicsForceRegion> ds; + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto toolItem = as<ToolUserItem>(item)) + ds.appendAll(toolItem->forceRegions()); + } + return ds; +} + +void ToolUser::render(RenderCallback* renderCallback, bool inToolRange, bool shifting, EntityRenderLayer renderLayer) { + if (m_suppress.get()) { + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto activeItem = as<ActiveItem>(item)) { + activeItem->pullNewAudios(); + activeItem->pullNewParticles(); + } + } + 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)); + + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto activeItem = as<ActiveItem>(item)) { + for (auto drawablePair : activeItem->entityDrawables()) + renderCallback->addDrawable(drawablePair.first, drawablePair.second.value(renderLayer)); + renderCallback->addAudios(activeItem->pullNewAudios()); + renderCallback->addParticles(activeItem->pullNewParticles()); + } + } +} + +void ToolUser::setItems(ItemPtr newPrimaryHandItem, ItemPtr newAltHandItem) { + if (itemSafeTwoHanded(newPrimaryHandItem)) + newAltHandItem = {}; + + if (m_suppress.get()) { + newPrimaryHandItem = {}; + newAltHandItem = {}; + } + + // Only skip if BOTH items match, to easily handle the edge cases where the + // primary and alt hands are swapped or share a pointer, to make sure both + // items end up initialized at the end. + if (newPrimaryHandItem == m_primaryHandItem.get() && newAltHandItem == m_altHandItem.get()) + return; + + uninitItem(m_primaryHandItem.get()); + uninitItem(m_altHandItem.get()); + + // Cancel held fire if we switch primary / alt hand items, to prevent + // accidentally triggering a switched item without a new edge trigger. + + if (m_primaryHandItem.get() != newPrimaryHandItem) { + m_fireMain = false; + m_fireAlt = false; + } + + if (m_altHandItem.get() != newAltHandItem) + m_fireAlt = false; + + m_primaryHandItem.set(move(newPrimaryHandItem)); + m_altHandItem.set(move(newAltHandItem)); + + initPrimaryHandItem(); + initAltHandItem(); +} + +void ToolUser::suppressItems(bool suppress) { + m_suppress.set(suppress); +} + +Maybe<Json> ToolUser::receiveMessage(String const& message, bool localMessage, JsonArray const& args) { + Maybe<Json> result; + for (auto item : {m_primaryHandItem.get(), m_altHandItem.get()}) { + if (auto activeItem = as<ActiveItem>(item)) + result = activeItem->receiveMessage(message, localMessage, args); + if (result) + break; + } + return result; +} + +float ToolUser::beamGunRadius() const { + return m_beamGunRadius + m_user->statusController()->statusProperty("bonusBeamGunRadius", 0).toFloat(); +} + +void ToolUser::NetItem::initNetVersion(NetElementVersion const* version) { + m_netVersion = version; + m_itemDescriptor.initNetVersion(m_netVersion); + if (auto netItem = as<NetElement>(m_item.get())) + netItem->initNetVersion(m_netVersion); +} + +void ToolUser::NetItem::netStore(DataStream& ds) const { + const_cast<NetItem*>(this)->updateItemDescriptor(); + m_itemDescriptor.netStore(ds); + if (auto netItem = as<NetElement>(m_item.get())) + netItem->netStore(ds); +} + +void ToolUser::NetItem::netLoad(DataStream& ds) { + m_itemDescriptor.netLoad(ds); + + auto itemDatabase = Root::singleton().itemDatabase(); + if (itemDatabase->loadItem(m_itemDescriptor.get(), m_item)) { + m_newItem = true; + if (auto netItem = as<NetElement>(m_item.get())) { + netItem->initNetVersion(m_netVersion); + if (m_netInterpolationEnabled) + netItem->enableNetInterpolation(m_netExtrapolationHint); + } + } + + if (auto netItem = as<NetElement>(m_item.get())) + netItem->netLoad(ds); +} + +void ToolUser::NetItem::enableNetInterpolation(float extrapolationHint) { + m_netInterpolationEnabled = true; + m_netExtrapolationHint = extrapolationHint; + if (auto netItem = as<NetElement>(m_item.get())) + netItem->enableNetInterpolation(extrapolationHint); +} + +void ToolUser::NetItem::disableNetInterpolation() { + m_netInterpolationEnabled = false; + m_netExtrapolationHint = 0; + if (auto netItem = as<NetElement>(m_item.get())) + netItem->disableNetInterpolation(); +} + +void ToolUser::NetItem::tickNetInterpolation(float dt) { + if (m_netInterpolationEnabled) { + if (auto netItem = as<NetElement>(m_item.get())) + netItem->tickNetInterpolation(dt); + } +} + +bool ToolUser::NetItem::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { + bool deltaWritten = false; + const_cast<NetItem*>(this)->updateItemDescriptor(); + m_buffer.clear(); + if (m_itemDescriptor.writeNetDelta(m_buffer, fromVersion)) { + deltaWritten = true; + ds.write<uint8_t>(1); + ds.writeBytes(m_buffer.data()); + if (auto netItem = as<NetElement>(m_item.get())) { + ds.write<uint8_t>(2); + netItem->netStore(ds); + } + } + + if (auto netItem = as<NetElement>(m_item.get())) { + m_buffer.clear(); + if (netItem->writeNetDelta(m_buffer, fromVersion)) { + deltaWritten = true; + ds.write<uint8_t>(3); + ds.writeBytes(m_buffer.data()); + } + } + + if (deltaWritten) + ds.write<uint8_t>(0); + return deltaWritten; +} + +void ToolUser::NetItem::readNetDelta(DataStream& ds, float interpolationTime) { + while (true) { + uint8_t code = ds.read<uint8_t>(); + if (code == 0) { + break; + } else if (code == 1) { + m_itemDescriptor.readNetDelta(ds); + if (!m_item || !m_item->matches(m_itemDescriptor.get(), true)) { + auto itemDatabase = Root::singleton().itemDatabase(); + if (itemDatabase->loadItem(m_itemDescriptor.get(), m_item)) { + m_newItem = true; + if (auto netItem = as<NetElement>(m_item.get())) { + netItem->initNetVersion(m_netVersion); + if (m_netInterpolationEnabled) + netItem->enableNetInterpolation(m_netExtrapolationHint); + } + } + } + } else if (code == 2) { + if (auto netItem = as<NetElement>(m_item.get())) + netItem->netLoad(ds); + else + throw IOException("Server/Client disagreement about whether an Item is a NetElement in NetItem::readNetDelta"); + } else if (code == 3) { + if (auto netItem = as<NetElement>(m_item.get())) + netItem->readNetDelta(ds, interpolationTime); + else + throw IOException("Server/Client disagreement about whether an Item is a NetElement in NetItem::readNetDelta"); + } else { + throw IOException("Improper code received in NetItem::readDelta"); + } + } +} + +void ToolUser::NetItem::blankNetDelta(float interpolationTime) { + if (m_netInterpolationEnabled) { + if (auto netItem = as<NetElement>(m_item.get())) + netItem->blankNetDelta(interpolationTime); + } +} + +ItemPtr const& ToolUser::NetItem::get() const { + return m_item; +} + +void ToolUser::NetItem::set(ItemPtr item) { + if (m_item != item) { + m_item = move(item); + m_newItem = true; + if (auto netItem = as<NetElement>(m_item.get())) { + netItem->initNetVersion(m_netVersion); + if (m_netInterpolationEnabled) + netItem->enableNetInterpolation(m_netExtrapolationHint); + else + netItem->disableNetInterpolation(); + } + updateItemDescriptor(); + } +} + +bool ToolUser::NetItem::pullNewItem() { + return take(m_newItem); +} + +void ToolUser::NetItem::updateItemDescriptor() { + if (m_item) + m_itemDescriptor.set(m_item->descriptor()); + else + m_itemDescriptor.set(ItemDescriptor()); +} + +void ToolUser::initPrimaryHandItem() { + if (m_user && m_primaryHandItem.get()) { + if (auto toolUserItem = as<ToolUserItem>(m_primaryHandItem.get())) + toolUserItem->init(m_user, ToolHand::Primary); + + if (auto fireable = as<FireableItem>(m_primaryHandItem.get())) + fireable->triggerCooldown(); + } +} + +void ToolUser::initAltHandItem() { + if (m_altHandItem.get() == m_primaryHandItem.get()) + m_altHandItem.set({}); + + if (m_user && m_altHandItem.get()) { + if (auto toolUserItem = as<ToolUserItem>(m_altHandItem.get())) + toolUserItem->init(m_user, ToolHand::Alt); + + if (auto fireable = as<FireableItem>(m_altHandItem.get())) + fireable->triggerCooldown(); + } +} + +void ToolUser::uninitItem(ItemPtr const& item) { + if (auto toolUserItem = as<ToolUserItem>(item)) + toolUserItem->uninit(); +} + +void ToolUser::netElementsNeedLoad(bool) { + if (m_primaryHandItem.pullNewItem()) + initPrimaryHandItem(); + + if (m_altHandItem.pullNewItem()) + initAltHandItem(); + + if (auto fireableItem = as<FireableItem>(m_primaryHandItem.get())) { + auto fireTime = m_primaryFireTimerNetState.get(); + fireableItem->setCoolingDown(fireTime < 0); + fireableItem->setFireTimer(abs(fireTime)); + + auto timeFiring = m_primaryTimeFiringNetState.get(); + fireableItem->setTimeFiring(timeFiring); + } + if (auto fireableItem = as<FireableItem>(m_altHandItem.get())) { + auto fireTime = m_altFireTimerNetState.get(); + fireableItem->setCoolingDown(fireTime < 0); + fireableItem->setFireTimer(abs(fireTime)); + + auto timeFiring = m_altTimeFiringNetState.get(); + fireableItem->setTimeFiring(timeFiring); + } + + if (auto activatableItem = as<ActivatableItem>(m_primaryHandItem.get())) + activatableItem->setActive(m_primaryItemActiveNetState.get()); + if (auto activatableItem = as<ActivatableItem>(m_altHandItem.get())) + activatableItem->setActive(m_altItemActiveNetState.get()); +} + +void ToolUser::netElementsNeedStore() { + if (auto fireableItem = as<FireableItem>(m_primaryHandItem.get())) { + auto t = std::max(0.0f, fireableItem->fireTimer()); + if (fireableItem->coolingDown()) + t *= -1; + m_primaryFireTimerNetState.set(t); + m_primaryTimeFiringNetState.set(fireableItem->timeFiring()); + } else { + m_primaryFireTimerNetState.set(0.0); + m_primaryTimeFiringNetState.set(0.0); + } + if (auto fireableItem = as<FireableItem>(m_altHandItem.get())) { + auto t = std::max(0.0f, fireableItem->fireTimer()); + if (fireableItem->coolingDown()) + t *= -1; + m_altFireTimerNetState.set(t); + m_altTimeFiringNetState.set(fireableItem->timeFiring()); + } else { + m_altFireTimerNetState.set(0.0); + m_altTimeFiringNetState.set(0.0); + } + + if (auto activatableItem = as<ActivatableItem>(m_primaryHandItem.get())) + m_primaryItemActiveNetState.set(activatableItem->active()); + else + m_primaryItemActiveNetState.set(false); + if (auto activatableItem = as<ActivatableItem>(m_altHandItem.get())) + m_altItemActiveNetState.set(activatableItem->active()); + else + m_altItemActiveNetState.set(false); +} + +} |