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