Moves specate and pause-api to plugins/spectate/
parent
ddf7850d90
commit
63e9192367
@ -0,0 +1,8 @@
|
||||
|
||||
project(spectate)
|
||||
|
||||
SET(SOURCES
|
||||
spectate.cpp
|
||||
pause.cpp)
|
||||
|
||||
dfhack_plugin(${PROJECT_NAME} ${SOURCES} LINK_LIBRARIES lua)
|
@ -0,0 +1,160 @@
|
||||
#include "pause.h"
|
||||
#include <VTableInterpose.h>
|
||||
#include <df/global_objects.h>
|
||||
#include <df/viewscreen_dwarfmodest.h>
|
||||
#include <df/announcements.h>
|
||||
#include <df/d_init.h>
|
||||
#include <df/ui.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace DFHack;
|
||||
using namespace Pausing;
|
||||
using namespace df::enums;
|
||||
|
||||
namespace pausing {
|
||||
AnnouncementLock announcementLock("monitor");
|
||||
PlayerLock playerLock("monitor");
|
||||
|
||||
const size_t announcement_flag_arr_size = sizeof(decltype(df::announcements::flags)) / sizeof(df::announcement_flags);
|
||||
bool state_saved = false; // indicates whether a restore state is ok
|
||||
bool announcements_disabled = false; // indicates whether disable or restore was last enacted, could use a better name
|
||||
bool saved_states[announcement_flag_arr_size]; // state to restore
|
||||
bool locked_states[announcement_flag_arr_size]; // locked state (re-applied each frame)
|
||||
bool allow_player_pause = true; // toggles player pause ability
|
||||
|
||||
using df::global::ui;
|
||||
using namespace df::enums;
|
||||
struct player_pause_hook : df::viewscreen_dwarfmodest {
|
||||
typedef df::viewscreen_dwarfmodest interpose_base;
|
||||
DEFINE_VMETHOD_INTERPOSE(void, feed, (std::set<df::interface_key>* input)) {
|
||||
if ((ui->main.mode == ui_sidebar_mode::Default) && !allow_player_pause) {
|
||||
input->erase(interface_key::D_PAUSE);
|
||||
}
|
||||
INTERPOSE_NEXT(feed)(input);
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_VMETHOD_INTERPOSE(player_pause_hook, feed);
|
||||
}
|
||||
using namespace pausing;
|
||||
|
||||
std::unordered_set<Lock*> PlayerLock::locks;
|
||||
std::unordered_set<Lock*> AnnouncementLock::locks;
|
||||
|
||||
template<typename Locks>
|
||||
inline bool any_lock(Locks locks) {
|
||||
return std::any_of(locks.begin(), locks.end(), [](Lock* lock) { return lock->isLocked(); });
|
||||
}
|
||||
|
||||
template<typename Locks, typename LockT>
|
||||
inline bool only_lock(Locks locks, LockT* this_lock) {
|
||||
return std::all_of(locks.begin(), locks.end(), [&](Lock* lock) {
|
||||
if (lock == this_lock) {
|
||||
return lock->isLocked();
|
||||
}
|
||||
return !lock->isLocked();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Locks, typename LockT>
|
||||
inline bool only_or_none_locked(Locks locks, LockT* this_lock) {
|
||||
for (auto &L: locks) {
|
||||
if (L == this_lock) {
|
||||
continue;
|
||||
}
|
||||
if (L->isLocked()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnnouncementLock::captureState() {
|
||||
if (only_or_none_locked(locks, this)) {
|
||||
for (size_t i = 0; i < announcement_flag_arr_size; ++i) {
|
||||
locked_states[i] = df::global::d_init->announcements.flags[i].bits.PAUSE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnnouncementLock::lock() {
|
||||
Lock::lock();
|
||||
captureState();
|
||||
}
|
||||
|
||||
bool AnnouncementLock::isAnyLocked() const {
|
||||
return any_lock(locks);
|
||||
}
|
||||
|
||||
bool AnnouncementLock::isOnlyLocked() const {
|
||||
return only_lock(locks, this);
|
||||
}
|
||||
|
||||
bool PlayerLock::isAnyLocked() const {
|
||||
return any_lock(locks);
|
||||
}
|
||||
|
||||
bool PlayerLock::isOnlyLocked() const {
|
||||
return only_lock(locks, this);
|
||||
}
|
||||
|
||||
bool World::DisableAnnouncementPausing() {
|
||||
if (!announcementLock.isAnyLocked()) {
|
||||
for (auto& flag : df::global::d_init->announcements.flags) {
|
||||
flag.bits.PAUSE = false;
|
||||
}
|
||||
announcements_disabled = true;
|
||||
}
|
||||
return announcements_disabled;
|
||||
}
|
||||
|
||||
bool World::SaveAnnouncementSettings() {
|
||||
if (!announcementLock.isAnyLocked()) {
|
||||
for (size_t i = 0; i < announcement_flag_arr_size; ++i) {
|
||||
saved_states[i] = df::global::d_init->announcements.flags[i].bits.PAUSE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool World::RestoreAnnouncementSettings() {
|
||||
if (!announcementLock.isAnyLocked() && state_saved) {
|
||||
for (size_t i = 0; i < announcement_flag_arr_size; ++i) {
|
||||
df::global::d_init->announcements.flags[i].bits.PAUSE = saved_states[i];
|
||||
}
|
||||
announcements_disabled = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool World::EnablePlayerPausing() {
|
||||
if (!playerLock.isAnyLocked()) {
|
||||
allow_player_pause = true;
|
||||
}
|
||||
return allow_player_pause;
|
||||
}
|
||||
|
||||
bool World::DisablePlayerPausing() {
|
||||
if (!playerLock.isAnyLocked()) {
|
||||
allow_player_pause = false;
|
||||
}
|
||||
return !allow_player_pause;
|
||||
}
|
||||
|
||||
void World::Update() {
|
||||
static bool did_once = false;
|
||||
if (!did_once) {
|
||||
did_once = true;
|
||||
INTERPOSE_HOOK(player_pause_hook, feed).apply();
|
||||
}
|
||||
if (announcementLock.isAnyLocked()) {
|
||||
for (size_t i = 0; i < announcement_flag_arr_size; ++i) {
|
||||
df::global::d_init->announcements.flags[i].bits.PAUSE = locked_states[i];
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
namespace DFHack {
|
||||
////////////
|
||||
// Locking mechanisms for control over pausing
|
||||
namespace Pausing
|
||||
{
|
||||
class Lock
|
||||
{
|
||||
bool locked = false;
|
||||
std::string name;
|
||||
public:
|
||||
explicit Lock(const char* name) { this->name = name;};
|
||||
virtual ~Lock()= default;
|
||||
virtual bool isAnyLocked() const = 0;
|
||||
virtual bool isOnlyLocked() const = 0;
|
||||
bool isLocked() const { return locked; }
|
||||
virtual void lock() { locked = true; }; //simply locks the lock
|
||||
void unlock() { locked = false; };
|
||||
};
|
||||
|
||||
// non-blocking lock resource used in conjunction with the announcement functions in World
|
||||
class AnnouncementLock : public Lock
|
||||
{
|
||||
static std::unordered_set<Lock*> locks;
|
||||
public:
|
||||
explicit AnnouncementLock(const char* name): Lock(name) { locks.emplace(this); }
|
||||
~AnnouncementLock() override { locks.erase(this); }
|
||||
bool captureState(); // captures the state of announcement settings, iff this is the only locked lock (note it does nothing if 0 locks are engaged)
|
||||
void lock() override; // locks and attempts to capture state
|
||||
bool isAnyLocked() const override; // returns true if any instance of AnnouncementLock is locked
|
||||
bool isOnlyLocked() const override; // returns true if locked and no other instance is locked
|
||||
};
|
||||
|
||||
// non-blocking lock resource used in conjunction with the Player pause functions in World
|
||||
class PlayerLock : public Lock
|
||||
{
|
||||
static std::unordered_set<Lock*> locks;
|
||||
public:
|
||||
explicit PlayerLock(const char* name): Lock(name) { locks.emplace(this); }
|
||||
~PlayerLock() override { locks.erase(this); }
|
||||
bool isAnyLocked() const override; // returns true if any instance of PlayerLock is locked
|
||||
bool isOnlyLocked() const override; // returns true if locked and no other instance is locked
|
||||
};
|
||||
}
|
||||
namespace World {
|
||||
bool DisableAnnouncementPausing(); // disable announcement pausing if all locks are open
|
||||
bool SaveAnnouncementSettings(); // save current announcement pause settings if all locks are open
|
||||
bool RestoreAnnouncementSettings(); // restore saved announcement pause settings if all locks are open
|
||||
|
||||
bool EnablePlayerPausing(); // enable player pausing if all locks are open
|
||||
bool DisablePlayerPausing(); // disable player pausing if all locks are open
|
||||
|
||||
void Update();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue