osb/source/game/interfaces/StarFireableItem.cpp
Kae 4b0bc220e4 Support for changing the game's timescale
Context-specific (like per-world) timescales can also be added later
2023-07-21 00:58:49 +10:00

301 lines
7.6 KiB
C++

#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<Item>(this), _1, _2)));
m_scriptComponent->addCallbacks("fireableItem", LuaBindings::makeFireableItemCallbacks(this));
m_scriptComponent->addCallbacks("item", LuaBindings::makeItemCallbacks(as<Item>(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(float dt, FireMode fireMode, bool shifting, HashSet<MoveControlType> const&) {
if (m_scriptComponent)
m_scriptComponent->invoke("update", dt, FireModeNames.getRight(fireMode), shifting);
if (m_attemptedFire) {
if (m_startTimingFire) {
m_timeFiring += dt;
if (m_scriptComponent)
m_scriptComponent->invoke("continueFire", dt);
}
} else {
m_timeFiring = 0.0f;
m_startTimingFire = false;
}
m_attemptedFire = false;
if (entityMode() == EntityMode::Master) {
if (fireTimer() > 0.0f) {
setFireTimer(fireTimer() - dt);
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<PersistentStatusEffect> FireableItem::statusEffects() const {
return {};
}
}