All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
- None
ExtendToString
andExtendToStringGrid
extension methods fromUtility
class are removed (they now exist in the primitives library instead)
AdvancedEffect
andAdvancedEffectTrigger
have been added, and have aTrigger
function that takes a parameter of the type you specify, which allows the effect to be triggered with additional context information.- This is equivalent to the old method of passing parameters which entailed creating a subclass of
EffectArgs
. - These classes accept a parameter of an arbitrary type, rather than forcing you to subclass
EffectArgs
to pass parameters.
- This is equivalent to the old method of passing parameters which entailed creating a subclass of
- All classes related to the effects system have been moved to the
GoRogue.Effects
namespace. Effect
andEffectTrigger
no longer accept type parameters- Versions called
AdvancedEffect
andAdvancedEffectTrigger
have been added which do accept type parameters
- Versions called
- Cancellation of a trigger from an effect is now handled via an
out bool
parameter given toTrigger
andOnTrigger
- Set this boolean value to true to cancel the trigger
EffectArgs
has been removed.- Parameters to
AdvancedEffect
andAdvancedEffectTrigger
can now be of an arbitrary type.
- Parameters to
- The
Factories
namespace now containsLambdaFactoryBlueprint
andLambdaAdvancedFactoryBlueprint
which allow the user to create a blueprint by specifying a function to create the object as a constructor parameter, rather than having to create a class that implementsIFactoryBlueprint
/IAdvancedFactoryBlueprint
. - Factories now have an
AddRange
function which allows you to add many blueprints at once.
Factories.Factory
andFactories.AdvancedFactory
now take a type parameter which specifies the type of object used as the key for blueprints (rather than forcing string). This allows factories to use other keys for blueprints, like enum values.- Factory blueprint's Id property has been renamed to ID.
ComponentCollection
can now take a parent object for it's added components of any type, rather than justIObjectWithComponents
.
IParentAwareComponent
andParentAwareComponentBase
now record the parent as typeobject
ParentAwareComponentBase<T>
no longer has type restrictions on type T; it can now accept any type as the parent.- This makes integration into other component systems more feasible since
IObjectWithComponents
is no longer required
- This makes integration into other component systems more feasible since
- Added custom list enumerators fitting the primitive library's custom enumerable pattern for casting objects to a given type
- Most uses are internal, however they may be useful for exposing lists of objects in performance-critical situations.
- Upgraded primitives library to rc2, which enables some spatial map optimizations.
ComponentCollection
now uses custom iterators for component retrieval functions which are notably faster than the old implementations.- Miscellaneous optimizations to dictionary accesses in
ComponentCollection
GameFramework.Map
now uses custom iterators forGetEntitiesAt
andGetObjectsAt
functions which are notably faster than the old implementations.GameFramework.Map
implementsGridViewBase<MapObjectsAtEnumerator>
, rather thanGridViewBase<IEnumerable<IGameObject>>
.- This should be functionally identical, since the
MapObjectsAtEnumerator
also implementsIEnumerable
.
- This should be functionally identical, since the
- Fixed bug where
Map
would not correctly enforce collision detection if thelayersBlockingTransparency
parameter was set to only the terrain layer.
Map
now, by default, keeps a cachedBitArrayView
to supportWalkabilityView
andTransparencyView
, rather than calculating those values on the fly- This is much faster for operations which use these grid views (pathing ~9x faster in some test cases, FOV ~4x), although uses slightly more memory and makes adding/removing/moving objects around on the map slightly slower.
- If needed, you can disable this behavior and get the old calculate-on-the-fly behavior instead by passing
false
to thecacheGridViews
parameter of theMap
constructor.
- Primitives library now contains
IPositionable
interface (implemented byIGameObject
) and contains auto-syncing spatial maps that don't require manual sync
- Spatial map movement-related functions now tolerate a source location the same as a destination location
IGameObject
now requires you to implementIPositionable
- Requires implementation of Position field (already was existing), as well as PositionChanged event (replaces Moved) and PositionChanging
- Game objects now have a PositionChanging field which is fired before the value is actually changed
- ToEnumerable() functions on custom iterators (
Rectangle.Positions()
orIGridView.Positions()
, for example) are now obsolete IGameObject.Entities
is now synced to item's positions before the item'sPositionChanged
event fires (viaPositionChanging
)
- The following code has been removed because it now exists in the primitives library GoRogue depends on:
- Spatial maps
- LayerMasker
- IHasID
- IHasLayer
- LayeredSpatialMap.TryMoveAll (in primitives library) now handles cases where items on some layers won't move properly by returning false
- LayeredSpatialMap.GetLayersInMask (in primitives library) now returns the correct layers
GoRogue.Lines
class renamed toGoRogue.LineHelpers
- Line-drawing algorithms from
Lines
class moved to TheSadRogue.Primitives (underLines
class)- Where you previously called
Lines.Get
, you should call line-specific functions (GetBresenhamLine()
,GetDDALine()
, orGetOrthogonalLine()
where possible) - If you need an IEnumerable type specifically or need a generic function you can pass an algorithm to, call
Lines.GetLine()
; but this will not perform as well as the algorithm-specific functions which use custom iterators!
- Where you previously called
PolygonArea
now defaults toBresenham
lines, since they are ordered and generally fasterPolygonArea
now allowsOrthogonal
lines since they are also ordered
- Bresenham lines (now in the primitives library) now break x/y ties the traditional way
Lines.Get
method and all associated line drawing algorithms have been removed- Refactored and moved to TheSadRogue.Primitives
BresenhamOrdered
no longer exists (Bresenham
is ordered now instead)
- RNG extension functions for generating random positions from Rectangles have been added
MessageBus
now has aRegisterAllSubscribers
method which automatically registers all variations of ISubscriber the parameter is subscribed to in one call.- This is now a safer default than the old RegisterSubscribers function, but it uses reflection so may be slower.
TryRegisterAllSubscribers
is also included.
MessageBus
now has aUnregisterAllSubscribers
method which automatically unregisters all variations of ISubscriber the parameter is subscribed to in one call.- This is now a safer default than the old UnregisterSubscribers function, but it uses reflection so may be slower.
TryUnregisterAllSubscribers
is also included.
- Added
ISenseMap
interface which captures the interface of a sense map - Added
SenseMapBase
which implements boilerplate forISenseMap
and provides an easy way to create a custom implementation of that interface - Added
ISenseSource
interface which captures the interface of a sense source - Added
SenseSourceBase
which implements boilerplate forISenseSource
and provides an easy way to create a custom implementation of that interface - Added the ability to implement custom sense source spreading algorithms
- Added the ability to customize aggregation of values and parallelization in the provided concrete sense map implementation
- Updated minimum version of TheSadRogue.Primitives to v1.4.1
- See this package's changelog for change list, which include performance increases
- Redesigned sense maps (see added features)
- Basis of sense map system is now the two interfaces
ISenseMap
andISenseSource
- Custom sense source spread algorithms can be implemented by implementing
ISenseSource
- Sense maps now operate on arbitrary
ISenseSource
instances, rather than some concrete implementation - Sense maps no longer implement
IEnumerable
orIGridView
; instead, they provide aResultView
which exposes the results.
- Basis of sense map system is now the two interfaces
- Optimized Map
- TransparencyView and WalkabilityView now retrieve values faster
- Fixed bug where changing the
Span
of a sense source might not update theIsAngleRestricted
value correctly
GameFramework.Map
now containsCanAddEntityAt
andTryAddEntityAt
functions which allow you to specify a new position to check instead of using the entity's current one.
- The double version of the recursive shadowcasting FOV algorithm is now much, much faster (about on par with the boolean version in typical cases)
FOV.RecursiveShadowcastingFOV
now uses the double-based variation of the algorithm, since it now makes a more reasonable default
- The
CanAddEntity
function inGameFramework.Map
now properly accounts for situations where the entity is being added to a layer which does not support multiple items at a given position
FOV.BooleanBasedFOVBase
which defines aResultView
as aBitArray
, andDoubleResultView
as a grid view that performs appropriate calculations based on that.FOV.DoubleBasedFOVBase
which definesResultView
andBooleanResultView
exactly asFOVBase
did previouslyFOV.IFOV
now defines a property which is a list of all the parameters for all the Calculate/CalculateAppend calls that have happened since the last reset- This enables users to track what the source/radius was for each FOV calculation that makes up the current state
- DisjointSet now optionally accepts a custom hashing algorithm to use for the items.
FOV.RecursiveShadowcastingFOV
now inherits fromBooleanBasedFOVBased
, and the base algorithm sets booleans rather than doubles- This makes
Calculate
andBooleanResultView
much faster, particularly so as the map size increases - Memory usage is also reduced significantly
- Accessing values from
DoubleResultView
is typically a bit slower, but for many use cases the speed increase ofCalculate
will offset it DoubleResultView
does not perform as well as the previous implementation when there are multiple FOV calculations being appended together viaCalculateAppend
; in cases where this becomes problematic, usage ofRecursiveShadowcastingDoubleBasedFOV
is recommended
- This makes
- The
FOV.IFOV
interface'sRecalculated
event argument now contains aFOVCalculateParameters
struct which contains all of the values that used to be on the arguments class directlyFOVRecalculatedEventArgs.Origin
=>FOVRecalculatedEventArgs.CalculateParameters.Origin
, etc
DisjointSet<T>
correctly initializes when given an input array (fixes #266)
FOV.FOVBase
no longer defines aResultView
; this field is now defined as appropriate by its subclasses
GameFramework.Map
now hasTryAddEntity
,CanAddEntity
, andTryRemoveEntity
which match the equivalent functions from spatial maps- Allows users to check if an add will succeed/has succeeded without involving exceptions
IDGenerator
now exposes its state via read-only propertiesIDGenerator
now supportsDataContract
serialization (including JSON)IDGenerator
constructor now supports specifying the boolean parameter used to record the "last ID assigned state" (useful mostly for serialization)
ShaiRandom
will now function properly when debugging via SourceLink (bumped version to 0.0.1-beta03 which has appropriate symbols uploaded)
- Spatial maps now have a
MoveValid
overload which takes a list to fill instead of returning one as a result - Spatial maps now have
TryMoveAll
andTryRemove
functions which return false instead of throwing exceptions when the operations fail- More performant in cases where failure is expected
- Miscellaneous functions added which can increase performance compared to the alternative for their use case
- Pooling for
List<T>
structures (similar toSystem.Buffers.ArrayPool
but for lists) is now provided in theGoRogue.Pooling
namespace- An interface is provided which encompasses the core functionality; as well as a basic implementation of that interface, and a "dummy" implementation that can be used to disable pooling entirely (it simply allocations/GCs as you would do without the pool).
MultiSpatialMap
,LayeredSpatialMap
, andGameFramework.Map
now have optional constructor parameters which allow you to specify what list pool is used for the creation of internal lists.- Added convenience functions to
LayerMasker
that allow you to more easily add a mask to another mask.
- Optimized
SenseSource
algorithm and structure- ~30% faster
- Notably less memory usage and allocations performed
- Optimized
SpatialMap
(most functions)- Degree of speedup varies based on circumstance
- Optimized existing
MultiSpatialMap
functions- ~2x faster move operations in some cases
- Minor add/remove performance increases
- ~20-30% faster
TryAdd
- Significant reduction in number of allocations performed during most move operations
- Optimized
LayeredSpatialMap
functions- Since
LayeredSpatialMap
usesMultiSpatialMap
, all of those performance benefits translate here as well. IN ADDITION to those benefits, the following also apply. MoveAll
an additional 2x fasterMoveValid
an additional 40% faster
- Since
- Optimized
LayerMasker
- The
Layers
function now returns a custom enumerable instead ofIEnumerable<int>
, which significantly cuts down performance overhead
- The
MultiSpatialMap
,LayeredSpatialMap
, andGameFramework.Map
now implement "list pooling" by default, which reduce the amount of reallocations that take place during add, remove, and move operations.- List pooling can be disabled or customized via a constructor parameter.
- Renamed
LayerMasker.DEFAULT
toLayerMasker.Default
in order to match typical C# and GoRogue naming conventions. - If a move operation fails, spatial maps now do not guarantee that state will be restored to what it was before any objects were moved.
- This allows notable performance increase, and exceptions thrown by functions such as
MoveAll
generally should not be recovered from; theTryMoveAll
,CanMove
, and/orMoveValid
functions should be used instead.
- This allows notable performance increase, and exceptions thrown by functions such as
- Spatial map implementations now have
TryMove
,TryAdd
, andTryRemove
functions which return false instead of throwing exception when an operation fails- Assuming current implementations, this is 5-10% faster than the old method of first checking with the appropriate
Can
method then doing the appropriate operation - Note that
Add
,Remove
, andMove
have been optimized as well so this will likely produce a greater speed increase than 5-10% in existing code
- Assuming current implementations, this is 5-10% faster than the old method of first checking with the appropriate
- Spatial map implementations now have a
TryGetPositionOf
function which returns false instead of throwing exception when item given doesn't exist - Spatial map implementations now have a
GetPositionOfOrNull
function which returnsnull
instead of throwing exception when item given doesn't exist- Note that, unlike the original
GetPositionOf
implementation, it returnsnull
, notdefault(T)
orPoint.None
- Note that, unlike the original
MessageBus
now hasTryRegisterSubscriber
andTryUnregisterSubscriber
functions which return false instead of throw an exception on failure (fixes #248).SadRogue.Primitives.GridViews
namespace now has a classBitArrayView
, which is a grid view of boolean values implemented via a C#BitArray
- Recommend using this instead of
bool[]
orArrayView<bool>
(and sometimes instead ofHashSet<Point>
) to represent a set of locations within a grid
- Recommend using this instead of
- The
GetPositionOf
function on spatial map implementations now throws exception if the position doesn't exist- Note other methods have been added that return null or false
Move
,Add
, andRemove
function of spatial map implementations have been optimized- Gains vary but can be as much as 15-20% per operation, for some implementations and circumstances
- Updated primitives library to 1.3.0
- GoRogue algorithms now use the primitives library's cache for directions of neighbors, instead of creating their own
- Various performance improvements to goal maps/flee maps
WeightedGoalMap
up to 50-80% faster for value indexer andGetDirectionOfMinValue
operations- Other goal maps will see more limited performance increase in
GetDirectionOfMinValue
calls
- Other goal maps will see more limited performance increase in
- 45-55% speed increase in calls to
Update()
for goal maps and flee maps on open maps - Goal maps, and flee maps should now also use less memory (or at least produce less allocations)
GoalMap
instances now explicitly reject base grid views that change width/height (although doing so would not function appropriately in most cases previously anyway)- Optimized memory usage for
AStar
- Saves about 8.5 kb over a 100x100 map, and produces less allocation during
ShortestPath()
- Saves about 8.5 kb over a 100x100 map, and produces less allocation during
RegenerateMapException
message now contains more details about intended fix (fixes #253)- GoRogue now uses ShaiRandom instead of Troschuetz.Random instead of its RNG library
- Many changes, some breaking; check the v2 to v3 porting guide for details
- In summary: new RNG algorithms, performance improvements, more access to generating different types of numbers, more access to generator state, more serialization control
KnownSeriesRandom
has moved toShaiRandom.Generators
; a host of bugs were fixed in this class in the processMinRandom
andMaxRandom
have been moved toShaiRandom.Generators
, and now support floating-point numbersRandomItem
,RandomPosition
, andRandomIndex
methods for classes are now extension ofIEnhancedGenerator
, instead of extensions of the container class- GoRogue's
RandomItem
andRandomIndex
functions for built-in C# types are now part of ShaiRandom
- The
GetDirectionOfMinValue
function for goal maps now supports maps with open edges (thanks DavidFridge) - The
WeightedGoalMap
calculation is now additive, as the supplemental article indicates it should be (thanks DavidFridge) - API documentation now properly cross-references types in primitives library
- Map constructor taking custom terrain grid view now properly initializes existing terrain on that grid view (fixes #254)
- NOTE: Caveats about this constructor's usage have now been properly documented in API documentation
- If moving the position of an entity on the map throws an exception, the map state will now recover properly
- Spatial map implementations now allow you to specify a custom point hashing algorithm to use
- Added similar hashing algorithm parameter to
GameFramework.Map
- FleeMaps now properly support goal maps where the base map has open edges (fixes #211)
- Applied performance optimizations to A* algorithm
- ~20% improvements to speed when generating paths on open maps
- Performance gain varies on other test cases but should generally be measurable
- Applied performance optimizations to GoalMap and FleeMap algorithms
- ~50% improvement on a full-update operation, using a wide-open (obstacle free) base map
- Performance gain varies on other test cases but should generally be measurable
- Defaulted to a usually faster hashing algorithm (one based on the Map's width) in Map's SpatialMaps
- Added the
IGameObject.WalkabilityChanging
event that is fired directly before the walkability of the object is changed. - Added the
IGameObject.TransparencyChanging
event that is fired directly before the transparency of the object is changed. - Added
IGameObject.SafelySetProperty
overload that can deal with "changing" (pre) events as well as "changed" (post) events.
- Fixed bug in
GameFramework.Map
that prevented setting of the walkability of map objects.
- GameObject now has constructors that omit the parameter for starting position
MessageBus
now has improved performanceMessageBus
also now supports subscribers being registered while aSend
is in progress- No subscribers added will be called by the in-progress
Send
call, however any subsequentSend
calls (including nested ones) will see the new subscribers
- No subscribers added will be called by the in-progress
ParentAwareComponentBase
now specifies old parent inRemoved
event.
- Added a constructor to
Region
that takes aPolygonArea
as a parameter and avoided copies. - Added an event that will automatically fire when the
Area
property of aRegion
is changed.
- Comparison of
PolygonArea
now compares exclusively based off of defined corner equivalency.
- All functions and constructors in
Region
that forwarded to corresponding functions inPolygonArea
.- Such functions and constructors are now only available by accessing
Area
property
- Such functions and constructors are now only available by accessing
Parallelogram
static creation method forPolygonArea
now functions correctly for differing values of width/height (ensures correct 45 degree angles)
- FOV now has
CalculateAppend
functions which calculate FOV from a given source, but add that FOV into the existing one, as opposed to replacing it - FOV now also has a
Reset
function and aVisibilityReset
event that may be useful in synchronizing visibility with FOV state PolygonArea
class that implements theIReadOnlyArea
interface by taking in a list of corners defining a polygon, and tracking the outer boundary points of the polygon, as well as the inner points- Also offers transformation operations
Region
class (not the same class as was previously namedRegion
) that associates a polygon with components and provides a convenient interface for transformationsMultiArea
now offers aClear()
function to remove all sub-areasMapAreaFinder
now has a function that lets you get a single area based on a starting point (eg. boundary fill algorithm), rather than all areas in the map view
IFOV
interface (and all implementations) now takes angles on a scale where 0 points up, and angles proceed clockwise- This matches better with bearing-finding functions in primitives library and correctly uses the common compass clockwise rotation scale
SenseSource
now takes angles on a scale where 0 points up, and angles proceed clockwise (thus matching FOV)Region
has been rewritten and is replaced withPolygonArea
PolygonArea
supports arbitrary numbers of corners and now implementsIReadOnlyArea
- Contains the static creation methods for rectangles and parallelograms previously on
Region
, as well as new ones for regular polygons and regular stars - The class called
Region
associates aPolygonArea
with a set of components and provides convenient accesses to the area's functions/interfaces- This functions as a substitute for the sub-regions functionality previously on
Region
- This functions as a substitute for the sub-regions functionality previously on
- Significant performance optimizations applied to
MultiArea
andMapAreaFinder
- Updated minimum needed primitives library version to
1.1.1
FOVBase.OnCalculate
function (all overloads) is now protected, as was intended originally- Summary documentation for
FOVBase
is now complete FOV.RecursiveShadowcastingFOV
now handles negative angle values properlySenseMapping.SenseSource
now handles negativeAngle
values properly
- Created
GoRogue.FOV
namespace to hold everything related to FOV IFOV
interface now exists in theGoRogue.FOV
namespace which defines the public interface for a method of calculating FOVFOVBase
abstract class has been added to theGoRogue.FOV
namespace to simplify creating implementations of theIFOV
interface
- Attaching
IParentAwareComponent
instances to two objects at once now produces a more helpful exception message FOV
renamed toRecursiveShadowcastingFOV
and moved to theGoRogue.FOV
namespace- FOV classes no longer implement
IGridView<double>
; instead, access theirDoubleResultView
field for equivalent behavior - FOV's
BooleanFOV
property renamed toBooleanResultView
- The
GameFramework.Map.PlayerFOV
has changed types; it is now of typeIFOV
in order to support custom implementations of FOV calculations.
ParentAwareComponentBase.Added
is no longer fired when the component is detached from an object
Map
now has parent-aware component support via an optional interface (similar to what GameObjects support)- Added generic parent-aware component structure (objects that are aware of what they are attached to) that can support arbitrary parent types instead of just
GameObject
instances. - Added events to
IGameObject
that must be fired when an object is added to/removed from a map (helper method provided)
ComponentCollection
now handles parent-aware components intrinsically (meaningIGameObject
implementations don't need to worry about it anymore)IGameObject
implementations should now callSafelySetCurrentMap
in theirOnMapChanged
implementation (as demonstrated in GoRogue's GameObject implementation) to safely fire all eventsITaggableComponentCollection
has been effectively renamed toIComponentCollection
ParentAwareComponentBase.IncompatibleWith
component restriction now has proper nullability on parameter types to be compatible with theParentAwareComponentBase.Added
event
- Removed
GameFramework.IGameObjectComponent
interface (replaced withIParentAwareComponent
and/orParentAwareComponentBase<T>
- Removed
GameFramework.ComponentBase
andGameFramework.ComponentBase<T>
classes (replaced withParentAwareComponentBase
andParentAwareComponentBase<T>
) - Removed
IBasicComponentCollection
interface (contents merged intoIComponentCollection
)
MultiArea
class that implementsIReadOnlyArea
in terms of a list of "sub-areas"Clear()
andCount
functionality to component collections- Specific functions
RandomItem
andRandomIndex
forArea
(sinceArea
no longer exposes a list) - Added
RegenerateMapException
which can be thrown byGenerationStep
instances to indicate the map must be discarded and re-generated - Added
ConfigAndGenerateSafe
/ConfigAndGetStageEnumeratorSafe
functions that provide behavior in addition to the typicalAddStep/Generate
type sequence that can handle cases where the map must be re-generated. DisjointSet
now has events that fire when areas are joinedDisjointSet<T>
class which automatically assigns IDs to objects
- Updated to v1.0 of the primitives library
- Modified
Area
to implement new primitives libraryIReadOnlyArea
interface- List of positions no longer exposed
Area
has indexers that take indices and return points directly
- Modified map generation interface to support critical exceptions that require map re-generation
- You must now call
ConfigAndGenerateSafe
to get equivalent behavior togen.AddSteps(...).Generate()
that will automatically handle these exceptions
- You must now call
- Modified
ClosestMapAreaConnection
step to minimize chances of issues that cause connections to cut through other areas- Uses more accurate description of areas in connection point selection
- Uses the same
ConnectionPointSelector
used to determine points to connect for determining the distance between two areas; thus allowing more control over how distance is calculated.
- Incorrect nullable annotation for
Map.GoRogueComponents
(#219) DungeonMazeGeneration
returning rooms with unrecorded doors (#217)