#pragma once #include "Tree/Service.hpp" #include "Utility/RunStateOwner.hpp" #include "Voxel/Cell.hpp" #include "Voxel/CellChangeListener.hpp" #include "Utility/SpatialRegion.hpp" #include #include "Voxel2/GridListener.hpp" namespace Aya { class DataModel; class MegaClusterInstance; extern const char* const sChangeHistoryService; class ChangeHistoryService : public DescribedCreatable , public Service , public Voxel::CellChangeListener , public Voxel2::GridListener { private: typedef DescribedCreatable Super; DataModel* dataModel; class Item; class Waypoint; shared_ptr megaClusterInstance; Waypoint* recording; typedef std::list Waypoints; // must be a list and not a vector because we maintain iterators after reallocs Waypoints waypoints; Waypoints::iterator playWaypoint; // playing nextWaypoint is a "redo" Waypoints::iterator unplayWaypoint; // unplaying unplayWaypoint is an "undo" Waypoints::iterator runStartWaypoint; // unplaying up to runStartWaypoint is a "reset" bool playing; bool enabled; int dataSize; shared_ptr runService; shared_ptr statsItem; Aya::signals::scoped_connection itemAddedConnection; Aya::signals::scoped_connection itemRemovedConnection; Aya::signals::scoped_connection itemChangedConnection; Aya::signals::scoped_connection runTransitionConnection; public: static const int minWaypoints = 3; static const int maxWaypoints = 250; static const int maxMemoryUsage = 250 * 1024 * 1024; typedef enum { Aggregate, // Don't allow waypoints while running. All changes are recorded and made part of the reset waypoint Snapshot, // When recording waypoints while running, take a snapshot of positions and velocities Hybrid // When recording waypoints while running, ignore unrelated position and velocity changes } RuntimeUndoBehavior; static RuntimeUndoBehavior runtimeUndoBehavior; ChangeHistoryService(); ~ChangeHistoryService(); static void requestWaypoint(const char* name, const Instance* context); void resetBaseWaypoint(); void clearWaypoints(); void setEnabled(bool state); bool isEnabled() const { return enabled; } void requestWaypoint(const char* name); void requestWaypoint2(std::string name) { requestWaypoint(name.c_str()); } // TODO: rename play-->redo unplay-->undo??? bool getPlayWaypoint(std::string& name, int steps = 0) const; bool getUnplayWaypoint(std::string& name, int steps = 0) const; bool canPlay() const { std::string name; return getPlayWaypoint(name); } bool canUnplay() const { std::string name; return getUnplayWaypoint(name); } // returns a tuple: bool state, [string name] shared_ptr canUnplay2(); shared_ptr canPlay2(); // std::string getPlayName() const { std::string name; getPlayWaypoint(name); return name; } // std::string getUnplayName() const { std::string name; getUnplayWaypoint(name); return name; } void play(); void playLua(); void unplay(); void unplayLua(); // to show progress dialog in studio boost::function, std::string)> withLongRunningOperation; bool isResetEnabled() const; void reset(); int getWaypointDataSize() const { return dataSize; } int getWaypointCount() const { return waypoints.size(); } Aya::signal waypointChangedSignal; Aya::signal undoSignal; Aya::signal redoSignal; MegaClusterInstance* getTerrain() const { return megaClusterInstance.get(); } protected: virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); private: void attach(); void dettach(); void trimWaypoints(); void mergeFirstTwoWaypoints(); void computeDataSize(); virtual void onItemAdded(shared_ptr item); virtual void onItemRemoved(shared_ptr item); virtual void onItemChanged(shared_ptr item, const Reflection::PropertyDescriptor* descriptor); bool isRecordable(Instance* instance); /*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& cell); /*override*/ virtual void onTerrainRegionChanged(const Voxel2::Region& region); void setCell(const SpatialRegion::Id& chunkPos, const Vector3int16& cellInChunk, Voxel::Cell detail, Voxel::CellMaterial material); typedef enum { Accept, Reject } CheckResult; CheckResult checkSettingWaypoint(); void onRunTransition(RunTransition event); void setWaypoint(const char* name); void setRunWaypoint(); void reportMissedPhysicsChanges(shared_ptr instance); }; } // namespace Aya