#include "StarFireableItem.hpp" #include "StarJsonExtra.hpp" #include "StarWorldLuaBindings.hpp" #include "StarConfigLuaBindings.hpp" #include "StarItemLuaBindings.hpp" #include "StarFireableItemLuaBindings.hpp" #include "StarItem.hpp" #include "StarWorld.hpp" namespace Star { FireableItem::FireableItem() : m_fireTimer(0), m_cooldownTime(10), m_windupTime(0), m_fireWhenReady(false), m_startWhenReady(false), m_cooldown(false), m_alreadyInit(false), m_requireEdgeTrigger(false), m_attemptedFire(false), m_fireOnRelease(false), m_timeFiring(0.0f), m_startTimingFire(false), m_inUse(false), m_walkWhileFiring(false), m_stopWhileFiring(false), m_mode(FireMode::None) {} FireableItem::FireableItem(Json const& params) : FireableItem() { setParams(params); m_fireableParams = params; } FireableItem::FireableItem(FireableItem const& rhs) : ToolUserItem(rhs), StatusEffectItem(rhs) { m_fireTimer = rhs.m_fireTimer; m_cooldownTime = rhs.m_cooldownTime; m_windupTime = rhs.m_windupTime; m_fireWhenReady = rhs.m_fireWhenReady; m_startWhenReady = rhs.m_startWhenReady; m_cooldown = rhs.m_cooldown; m_alreadyInit = rhs.m_alreadyInit; m_requireEdgeTrigger = rhs.m_requireEdgeTrigger; m_attemptedFire = rhs.m_attemptedFire; m_fireOnRelease = rhs.m_fireOnRelease; m_timeFiring = rhs.m_timeFiring; m_startTimingFire = rhs.m_startTimingFire; m_inUse = rhs.m_inUse; m_walkWhileFiring = rhs.m_walkWhileFiring; m_stopWhileFiring = rhs.m_stopWhileFiring; m_fireableParams = rhs.m_fireableParams; m_handPosition = rhs.m_handPosition; m_mode = rhs.m_mode; } void FireableItem::init(ToolUserEntity* owner, ToolHand hand) { ToolUserItem::init(owner, hand); m_fireWhenReady = false; m_startWhenReady = false; auto scripts = m_fireableParams.opt("scripts").apply(jsonToStringList); if (entityMode() == EntityMode::Master && scripts) { if (!m_scriptComponent) { m_scriptComponent.emplace(); m_scriptComponent->setScripts(*scripts); } m_scriptComponent->addCallbacks( "config", LuaBindings::makeConfigCallbacks(bind(&Item::instanceValue, as(this), _1, _2))); m_scriptComponent->addCallbacks("fireableItem", LuaBindings::makeFireableItemCallbacks(this)); m_scriptComponent->addCallbacks("item", LuaBindings::makeItemCallbacks(as(this))); m_scriptComponent->init(world()); } } void FireableItem::uninit() { if (m_scriptComponent) { m_scriptComponent->uninit(); m_scriptComponent->removeCallbacks("config"); m_scriptComponent->removeCallbacks("fireableItem"); m_scriptComponent->removeCallbacks("item"); } ToolUserItem::uninit(); } void FireableItem::fire(FireMode mode, bool, bool edgeTriggered) { m_attemptedFire = true; if (ready()) { m_inUse = true; m_startTimingFire = true; m_mode = mode; if (!m_requireEdgeTrigger || edgeTriggered) { setFireTimer(windupTime() + cooldownTime()); if (!m_fireOnRelease) { m_fireWhenReady = true; m_startWhenReady = true; } } } if (m_scriptComponent) m_scriptComponent->invoke("attemptedFire"); } void FireableItem::endFire(FireMode mode, bool) { if (m_scriptComponent) m_scriptComponent->invoke("endFire"); m_attemptedFire = false; if (m_fireOnRelease && m_timeFiring) { m_mode = mode; triggerCooldown(); fireTriggered(); } } FireMode FireableItem::fireMode() const { return m_mode; } float FireableItem::cooldownTime() const { return m_cooldownTime; } void FireableItem::setCooldownTime(float cooldownTime) { m_cooldownTime = cooldownTime; } float FireableItem::fireTimer() const { return m_fireTimer; } void FireableItem::setFireTimer(float fireTimer) { m_fireTimer = fireTimer; } bool FireableItem::ready() const { return fireTimer() <= 0; } bool FireableItem::firing() const { return m_timeFiring > 0; } bool FireableItem::inUse() const { return m_inUse; } bool FireableItem::walkWhileFiring() const { return m_walkWhileFiring; } bool FireableItem::stopWhileFiring() const { return m_stopWhileFiring; } bool FireableItem::windup() const { if (ready()) return false; if (m_scriptComponent) m_scriptComponent->invoke("triggerWindup"); return fireTimer() > cooldownTime(); } void FireableItem::update(FireMode fireMode, bool shifting, HashSet const&) { if (m_scriptComponent) m_scriptComponent->invoke("update", WorldTimestep, FireModeNames.getRight(fireMode), shifting); if (m_attemptedFire) { if (m_startTimingFire) { m_timeFiring += WorldTimestep; if (m_scriptComponent) m_scriptComponent->invoke("continueFire", WorldTimestep); } } else { m_timeFiring = 0.0f; m_startTimingFire = false; } m_attemptedFire = false; if (entityMode() == EntityMode::Master) { if (fireTimer() > 0.0f) { setFireTimer(fireTimer() - WorldTimestep); if (fireTimer() < 0.0f) { setFireTimer(0.0f); m_inUse = false; } } if (fireTimer() <= 0) { m_cooldown = false; } if (m_startWhenReady) { m_startWhenReady = false; startTriggered(); } if (m_fireWhenReady) { if (fireTimer() <= cooldownTime()) { m_fireWhenReady = false; fireTriggered(); } } } } void FireableItem::triggerCooldown() { setFireTimer(cooldownTime()); m_cooldown = true; if (m_scriptComponent) m_scriptComponent->invoke("triggerCooldown"); } bool FireableItem::coolingDown() const { return m_cooldown; } void FireableItem::setCoolingDown(bool coolingdown) { m_cooldown = coolingdown; } float FireableItem::timeFiring() const { return m_timeFiring; } void FireableItem::setTimeFiring(float timeFiring) { m_timeFiring = timeFiring; } Vec2F FireableItem::handPosition() const { return m_handPosition; } Vec2F FireableItem::firePosition() const { return Vec2F(); } Json FireableItem::fireableParam(String const& key) const { return m_fireableParams.get(key); } Json FireableItem::fireableParam(String const& key, Json const& defaultVal) const { return m_fireableParams.get(key, defaultVal); } bool FireableItem::validAimPos(Vec2F const&) { return true; } void FireableItem::setParams(Json const& params) { if (!m_alreadyInit) { // cannot use setWindupTime or setCooldownTime here, because object is not fully constructed m_windupTime = params.getFloat("windupTime", 0.0f); m_cooldownTime = params.getFloat("cooldown", params.getFloat("fireTime", 0.15f) - m_windupTime); if (params.contains("handPosition")) { m_handPosition = jsonToVec2F(params.get("handPosition")); } m_requireEdgeTrigger = params.getBool("edgeTrigger", false); m_fireOnRelease = params.getBool("fireOnRelease", false); m_walkWhileFiring = params.getBool("walkWhileFiring", false); m_stopWhileFiring = params.getBool("stopWhileFiring", false); m_alreadyInit = true; } } void FireableItem::setFireableParam(String const& key, Json const& value) { m_fireableParams = m_fireableParams.set(key, value); } void FireableItem::startTriggered() { if (m_scriptComponent) m_scriptComponent->invoke("startTriggered"); } void FireableItem::fireTriggered() { if (m_scriptComponent) m_scriptComponent->invoke("fireTriggered"); } Vec2F FireableItem::ownerFirePosition() const { if (!initialized()) throw StarException("FireableItem uninitialized in ownerFirePosition"); return owner()->handPosition(hand(), (this->firePosition() - handPosition()) / TilePixels); } float FireableItem::windupTime() const { return m_windupTime; } void FireableItem::setWindupTime(float time) { m_windupTime = time; } List FireableItem::statusEffects() const { return {}; } }