All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
None
- Added .NET 10 explicit multi-target
- AStar path objects now record the weight of the path that was found as part of the structure (thanks BoThompson!)
- Added .NET 8 and 9 explicit multi-targets
GameFramework.Mapnow handles transparency changes properly (thanks Sofistico!)DiceNotation.Parsernow handles negative numbers correctly
ExtendToStringandExtendToStringGridextension methods fromUtilityclass are removed (they now exist in the primitives library instead)
AdvancedEffectandAdvancedEffectTriggerhave been added, and have aTriggerfunction 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
EffectArgsto 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.Effectsnamespace. EffectandEffectTriggerno longer accept type parameters- Versions called
AdvancedEffectandAdvancedEffectTriggerhave been added which do accept type parameters
- Versions called
- Cancellation of a trigger from an effect is now handled via an
out boolparameter given toTriggerandOnTrigger- Set this boolean value to true to cancel the trigger
EffectArgshas been removed.- Parameters to
AdvancedEffectandAdvancedEffectTriggercan now be of an arbitrary type.
- Parameters to
- The
Factoriesnamespace now containsLambdaFactoryBlueprintandLambdaAdvancedFactoryBlueprintwhich 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
AddRangefunction which allows you to add many blueprints at once.
Factories.FactoryandFactories.AdvancedFactorynow 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.
ComponentCollectioncan now take a parent object for it's added components of any type, rather than justIObjectWithComponents.
IParentAwareComponentandParentAwareComponentBasenow record the parent as typeobjectParentAwareComponentBase<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
IObjectWithComponentsis 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.
ComponentCollectionnow uses custom iterators for component retrieval functions which are notably faster than the old implementations.- Miscellaneous optimizations to dictionary accesses in
ComponentCollection GameFramework.Mapnow uses custom iterators forGetEntitiesAtandGetObjectsAtfunctions which are notably faster than the old implementations.GameFramework.MapimplementsGridViewBase<MapObjectsAtEnumerator>, rather thanGridViewBase<IEnumerable<IGameObject>>.- This should be functionally identical, since the
MapObjectsAtEnumeratoralso implementsIEnumerable.
- This should be functionally identical, since the
- Fixed bug where
Mapwould not correctly enforce collision detection if thelayersBlockingTransparencyparameter was set to only the terrain layer.
Mapnow, by default, keeps a cachedBitArrayViewto supportWalkabilityViewandTransparencyView, 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
falseto thecacheGridViewsparameter of theMapconstructor.
- Primitives library now contains
IPositionableinterface (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
IGameObjectnow 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.Entitiesis now synced to item's positions before the item'sPositionChangedevent 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.Linesclass renamed toGoRogue.LineHelpers- Line-drawing algorithms from
Linesclass moved to TheSadRogue.Primitives (underLinesclass)- 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
PolygonAreanow defaults toBresenhamlines, since they are ordered and generally fasterPolygonAreanow allowsOrthogonallines since they are also ordered
- Bresenham lines (now in the primitives library) now break x/y ties the traditional way
Lines.Getmethod and all associated line drawing algorithms have been removed- Refactored and moved to TheSadRogue.Primitives
BresenhamOrderedno longer exists (Bresenhamis ordered now instead)
- RNG extension functions for generating random positions from Rectangles have been added
MessageBusnow has aRegisterAllSubscribersmethod 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.
TryRegisterAllSubscribersis also included.
MessageBusnow has aUnregisterAllSubscribersmethod 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.
TryUnregisterAllSubscribersis also included.
- Added
ISenseMapinterface which captures the interface of a sense map - Added
SenseMapBasewhich implements boilerplate forISenseMapand provides an easy way to create a custom implementation of that interface - Added
ISenseSourceinterface which captures the interface of a sense source - Added
SenseSourceBasewhich implements boilerplate forISenseSourceand 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
ISenseMapandISenseSource - Custom sense source spread algorithms can be implemented by implementing
ISenseSource - Sense maps now operate on arbitrary
ISenseSourceinstances, rather than some concrete implementation - Sense maps no longer implement
IEnumerableorIGridView; instead, they provide aResultViewwhich 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
Spanof a sense source might not update theIsAngleRestrictedvalue correctly
GameFramework.Mapnow containsCanAddEntityAtandTryAddEntityAtfunctions 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.RecursiveShadowcastingFOVnow uses the double-based variation of the algorithm, since it now makes a more reasonable default
- The
CanAddEntityfunction inGameFramework.Mapnow properly accounts for situations where the entity is being added to a layer which does not support multiple items at a given position
FOV.BooleanBasedFOVBasewhich defines aResultViewas aBitArray, andDoubleResultViewas a grid view that performs appropriate calculations based on that.FOV.DoubleBasedFOVBasewhich definesResultViewandBooleanResultViewexactly asFOVBasedid previouslyFOV.IFOVnow 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.RecursiveShadowcastingFOVnow inherits fromBooleanBasedFOVBased, and the base algorithm sets booleans rather than doubles- This makes
CalculateandBooleanResultViewmuch faster, particularly so as the map size increases - Memory usage is also reduced significantly
- Accessing values from
DoubleResultViewis typically a bit slower, but for many use cases the speed increase ofCalculatewill offset it DoubleResultViewdoes 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 ofRecursiveShadowcastingDoubleBasedFOVis recommended
- This makes
- The
FOV.IFOVinterface'sRecalculatedevent argument now contains aFOVCalculateParametersstruct 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.FOVBaseno longer defines aResultView; this field is now defined as appropriate by its subclasses
GameFramework.Mapnow hasTryAddEntity,CanAddEntity, andTryRemoveEntitywhich match the equivalent functions from spatial maps- Allows users to check if an add will succeed/has succeeded without involving exceptions
IDGeneratornow exposes its state via read-only propertiesIDGeneratornow supportsDataContractserialization (including JSON)IDGeneratorconstructor now supports specifying the boolean parameter used to record the "last ID assigned state" (useful mostly for serialization)
ShaiRandomwill now function properly when debugging via SourceLink (bumped version to 0.0.1-beta03 which has appropriate symbols uploaded)
- Spatial maps now have a
MoveValidoverload which takes a list to fill instead of returning one as a result - Spatial maps now have
TryMoveAllandTryRemovefunctions 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.ArrayPoolbut for lists) is now provided in theGoRogue.Poolingnamespace- 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.Mapnow have optional constructor parameters which allow you to specify what list pool is used for the creation of internal lists.- Added convenience functions to
LayerMaskerthat allow you to more easily add a mask to another mask.
- Optimized
SenseSourcealgorithm and structure- ~30% faster
- Notably less memory usage and allocations performed
- Optimized
SpatialMap(most functions)- Degree of speedup varies based on circumstance
- Optimized existing
MultiSpatialMapfunctions- ~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
LayeredSpatialMapfunctions- Since
LayeredSpatialMapusesMultiSpatialMap, all of those performance benefits translate here as well. IN ADDITION to those benefits, the following also apply. MoveAllan additional 2x fasterMoveValidan additional 40% faster
- Since
- Optimized
LayerMasker- The
Layersfunction now returns a custom enumerable instead ofIEnumerable<int>, which significantly cuts down performance overhead
- The
MultiSpatialMap,LayeredSpatialMap, andGameFramework.Mapnow 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.DEFAULTtoLayerMasker.Defaultin 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
MoveAllgenerally should not be recovered from; theTryMoveAll,CanMove, and/orMoveValidfunctions should be used instead.
- This allows notable performance increase, and exceptions thrown by functions such as
- Spatial map implementations now have
TryMove,TryAdd, andTryRemovefunctions 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
Canmethod then doing the appropriate operation - Note that
Add,Remove, andMovehave 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
TryGetPositionOffunction which returns false instead of throwing exception when item given doesn't exist - Spatial map implementations now have a
GetPositionOfOrNullfunction which returnsnullinstead of throwing exception when item given doesn't exist- Note that, unlike the original
GetPositionOfimplementation, it returnsnull, notdefault(T)orPoint.None
- Note that, unlike the original
MessageBusnow hasTryRegisterSubscriberandTryUnregisterSubscriberfunctions which return false instead of throw an exception on failure (fixes #248).SadRogue.Primitives.GridViewsnamespace 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
GetPositionOffunction 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, andRemovefunction 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
WeightedGoalMapup to 50-80% faster for value indexer andGetDirectionOfMinValueoperations- Other goal maps will see more limited performance increase in
GetDirectionOfMinValuecalls
- 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)
GoalMapinstances 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
RegenerateMapExceptionmessage 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
KnownSeriesRandomhas moved toShaiRandom.Generators; a host of bugs were fixed in this class in the processMinRandomandMaxRandomhave been moved toShaiRandom.Generators, and now support floating-point numbersRandomItem,RandomPosition, andRandomIndexmethods for classes are now extension ofIEnhancedGenerator, instead of extensions of the container class- GoRogue's
RandomItemandRandomIndexfunctions for built-in C# types are now part of ShaiRandom
- The
GetDirectionOfMinValuefunction for goal maps now supports maps with open edges (thanks DavidFridge) - The
WeightedGoalMapcalculation 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.WalkabilityChangingevent that is fired directly before the walkability of the object is changed. - Added the
IGameObject.TransparencyChangingevent that is fired directly before the transparency of the object is changed. - Added
IGameObject.SafelySetPropertyoverload that can deal with "changing" (pre) events as well as "changed" (post) events.
- Fixed bug in
GameFramework.Mapthat prevented setting of the walkability of map objects.
- GameObject now has constructors that omit the parameter for starting position
MessageBusnow has improved performanceMessageBusalso now supports subscribers being registered while aSendis in progress- No subscribers added will be called by the in-progress
Sendcall, however any subsequentSendcalls (including nested ones) will see the new subscribers
- No subscribers added will be called by the in-progress
ParentAwareComponentBasenow specifies old parent inRemovedevent.
- Added a constructor to
Regionthat takes aPolygonAreaas a parameter and avoided copies. - Added an event that will automatically fire when the
Areaproperty of aRegionis changed.
- Comparison of
PolygonAreanow compares exclusively based off of defined corner equivalency.
- All functions and constructors in
Regionthat forwarded to corresponding functions inPolygonArea.- Such functions and constructors are now only available by accessing
Areaproperty
- Such functions and constructors are now only available by accessing
Parallelogramstatic creation method forPolygonAreanow functions correctly for differing values of width/height (ensures correct 45 degree angles)
- FOV now has
CalculateAppendfunctions 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
Resetfunction and aVisibilityResetevent that may be useful in synchronizing visibility with FOV state PolygonAreaclass that implements theIReadOnlyAreainterface 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
Regionclass (not the same class as was previously namedRegion) that associates a polygon with components and provides a convenient interface for transformationsMultiAreanow offers aClear()function to remove all sub-areasMapAreaFindernow 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
IFOVinterface (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
SenseSourcenow takes angles on a scale where 0 points up, and angles proceed clockwise (thus matching FOV)Regionhas been rewritten and is replaced withPolygonAreaPolygonAreasupports 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
Regionassociates aPolygonAreawith 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
MultiAreaandMapAreaFinder - Updated minimum needed primitives library version to
1.1.1
FOVBase.OnCalculatefunction (all overloads) is now protected, as was intended originally- Summary documentation for
FOVBaseis now complete FOV.RecursiveShadowcastingFOVnow handles negative angle values properlySenseMapping.SenseSourcenow handles negativeAnglevalues properly
- Created
GoRogue.FOVnamespace to hold everything related to FOV IFOVinterface now exists in theGoRogue.FOVnamespace which defines the public interface for a method of calculating FOVFOVBaseabstract class has been added to theGoRogue.FOVnamespace to simplify creating implementations of theIFOVinterface
- Attaching
IParentAwareComponentinstances to two objects at once now produces a more helpful exception message FOVrenamed toRecursiveShadowcastingFOVand moved to theGoRogue.FOVnamespace- FOV classes no longer implement
IGridView<double>; instead, access theirDoubleResultViewfield for equivalent behavior - FOV's
BooleanFOVproperty renamed toBooleanResultView - The
GameFramework.Map.PlayerFOVhas changed types; it is now of typeIFOVin order to support custom implementations of FOV calculations.
ParentAwareComponentBase.Addedis no longer fired when the component is detached from an object
Mapnow 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
GameObjectinstances. - Added events to
IGameObjectthat must be fired when an object is added to/removed from a map (helper method provided)
ComponentCollectionnow handles parent-aware components intrinsically (meaningIGameObjectimplementations don't need to worry about it anymore)IGameObjectimplementations should now callSafelySetCurrentMapin theirOnMapChangedimplementation (as demonstrated in GoRogue's GameObject implementation) to safely fire all eventsITaggableComponentCollectionhas been effectively renamed toIComponentCollection
ParentAwareComponentBase.IncompatibleWithcomponent restriction now has proper nullability on parameter types to be compatible with theParentAwareComponentBase.Addedevent
- Removed
GameFramework.IGameObjectComponentinterface (replaced withIParentAwareComponentand/orParentAwareComponentBase<T> - Removed
GameFramework.ComponentBaseandGameFramework.ComponentBase<T>classes (replaced withParentAwareComponentBaseandParentAwareComponentBase<T>) - Removed
IBasicComponentCollectioninterface (contents merged intoIComponentCollection)
MultiAreaclass that implementsIReadOnlyAreain terms of a list of "sub-areas"Clear()andCountfunctionality to component collections- Specific functions
RandomItemandRandomIndexforArea(sinceAreano longer exposes a list) - Added
RegenerateMapExceptionwhich can be thrown byGenerationStepinstances to indicate the map must be discarded and re-generated - Added
ConfigAndGenerateSafe/ConfigAndGetStageEnumeratorSafefunctions that provide behavior in addition to the typicalAddStep/Generatetype sequence that can handle cases where the map must be re-generated. DisjointSetnow 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
Areato implement new primitives libraryIReadOnlyAreainterface- List of positions no longer exposed
Areahas 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
ConfigAndGenerateSafeto get equivalent behavior togen.AddSteps(...).Generate()that will automatically handle these exceptions
- You must now call
- Modified
ClosestMapAreaConnectionstep 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
ConnectionPointSelectorused 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) DungeonMazeGenerationreturning rooms with unrecorded doors (#217)