Releases: SphereII/SphereII.Mods
2.6.10.1108
Version: 2.6.10.1108 [ Experimental ]
[ NPCv4 / IEntityAliveSDX - V4 Entity Support ]
NPCv4 (EntityAliveSDXV4) is a ground-up rewrite of the NPC entity that extends EntityTrader
instead of EntityAlive, using a component-based architecture (NPCLeaderComponent,
NPCPatrolComponent, NPCCombatComponent, NPCEffectsComponent) to replace the monolithic
EntityAliveSDX class. The goal is cleaner separation of concerns, better compatibility with
vanilla trader systems, and a more maintainable foundation for future NPC features.
The IEntityAliveSDX interface bridges V3 and V4, allowing shared code (UAI tasks, dialog
scripts, utility methods) to work with either entity type without hard casts.
- Updated EntityUtilities.ExecuteCMD to support EntityAliveSDXV4 via IEntityAliveSDX and
IEntityOrderReceiverSDX interfaces. V3-exclusive fields (bodyDamage) remain gated behind
a conditional EntityAliveSDX cast; all other operations now work for both V3 and V4.
- Updated EntityUtilities.TeleportNow to use IEntityAliveSDX type guard instead of a hard
EntityAliveSDX cast, allowing V4 entities to be teleported.
- Updated EntityUtilities.AddQuestToRadius to detect both EntityAliveSDX (V3) and
EntityAliveSDXV4 (V4) when adding quests to nearby NPCs.
- Updated DialogActionAnimatorSet to cast via IEntityAliveSDX instead of EntityAliveSDX,
allowing V4 entities to receive animator commands from dialogs.
- Updated DialogActionDisplayInfo to cast via IEntityAliveSDX, allowing V4 entities to
display info dialogs.
- Updated DialogActionRemoveBuffNPCSDX to cast via IEntityAliveSDX, allowing V4 entities
to have buffs removed through dialogs.
- Updated DialogActionSwapWeapon to cast via IEntityAliveSDX, using the interface's
UpdateWeapon() method so V4 entities can swap weapons through dialogs.
- Updated DialogRequirementHasQuestSDX to cast via IEntityAliveSDX; NPCInfo accessed via
EntityTrader cast, supporting V4 quest requirements.
- Updated DialogRequirementNPCHasItemSDX to cast via IEntityAliveSDX with null-safe loot
container checks, supporting V4 inventory requirements.
- Updated DialogActionPickUpNPC to cast via EntityAlive + IEntityAliveSDX, enabling V4
entity pickup. EntitySyncUtils.GetNPCItemValue, SetNPCItemValue, Collect, and
CollectClient now accept EntityAlive and use IEntityAliveSDX for name, title, and
weapon access; V3-specific fields (belongsPlayerId, _currentWeapon) use conditional
casts. IEntityAliveSDX gains FirstName and Title members. EntityAliveSDXV4.Title.set
was fixed (previously threw NotImplementedException). All deploy/place net packages
and ItemActionDeployNPCSDX updated to cast via EntityAlive rather than EntityAliveSDX.
[ UAI Troubleshooting / Diagnostics ]
- Added per-entity AIPackage diagnostics to EntityAliveSDXV4.PostInit via new
LogMissingAIPackages() method. On spawn, logs which packages were found vs. missing
from UAIBase.AIPackages, making load-order conflicts immediately visible in the log.
- Added empty-AIPackages-list warning in UAIBase.chooseAction Prefix: if an entity has
no AI packages, a single warning is logged explaining what to check.
- Added per-package missing-package warning in UAIBase.chooseAction Prefix using a
HashSet deduplication guard, preventing log spam while still surfacing undefined
package names on first encounter.
[ UAI Performance / Bug Fixes ]
- Replaced triple dictionary lookup (ContainsKey + two indexed accesses) in
UAIBase.chooseAction with a single TryGetValue call, reducing redundant hash lookups
per AI evaluation cycle.
- Removed dead-code sort in AddWaypointTargetsToConsider: the sort was applied to the
WaypointTargets list immediately after Clear(), so it operated on an empty list every
time and had no effect.
- Fixed incorrect log-line order in AddWaypointTargetsToConsider: the count was logged
before waypoints were gathered, reporting 0 every time.
[ UAI Farming ]
- Fixed two farmer NPCs targeting the same crop simultaneously. Added a shared static
HashSet<Vector3i> claim registry to UAITaskFarming and UAITaskFarmingV4. A plot is
claimed in Start() immediately after selection and released in Stop(), covering both
normal task completion and interruption. FindTargetFarmPlot() skips any plot already
in the registry, so each farmer always works a unique plot.
- Fixed harvested items not being added to the NPC's inventory. Three silent failure
modes were patched in HandleHarvestingAndCleanup for both UAITaskFarming and
UAITaskFarmingV4:
* FastMax(0, minCount) could produce a 0-count ItemStack that AddItem silently rejects;
clamped to FastMax(1, minCount).
* item.prob was never checked; items now roll against their drop probability.
* ItemClass.GetItem result was not validated; unknown item names now log a warning
and are skipped instead of producing an invalid ItemValue.None stack.
- Fixed NPC farmer not harvesting fully-grown crops whose block type does not extend
BlockPlant (e.g. BlockPickUpAndReplace-based final stages such as plantedCoffee3HarvestPlayer).
FarmPlotData.Manage() previously checked IsDeadPlant() before checking harvest drops,
so any non-BlockPlant block was cleared as a "dead plant" with no items returned.
The branch order is now: HasItemsToDropForEvent(Harvest) first (harvest), then
IsDeadPlant() (clear), then growing-plant skip, then empty-plot replant.
- Fixed NPC farmer not replanting immediately after harvesting. Two causes:
* CanPlaceBlockAt was checked against the live world while the harvested block was
still present, always returning false. The check is removed; placement validity is
guaranteed by the block name resolving correctly.
* SetBlocksRPC was called with two entries at the same position (air then seed).
The intermediate air state triggered a physics/support check that caused the seed
to fall through the world before it could stabilise. Replaced with a single-entry
RPC that writes the seed directly over the harvested block.
- Fixed malformed drop table entries (empty item name, zero maxCount) from block
itemsToDrop reaching the NPC inventory loop and producing unknown-item warnings.
FarmPlotData.Manage() now strips these entries during harvest-item processing.
- Fixed FarmPlotData.Manage() mutating the block singleton's shared itemsToDrop lists.
TryGetValue returns a direct reference to the block class's internal List; RemoveAt
and AddRange on that reference permanently altered shared drop data for all future
harvests. Drops are now copied into a pre-allocated local list via AddRange.
- Added Log.Out diagnostics throughout FarmPlotData.Manage() (block name, hasHarvestDrops,
raw drop list, seed extraction result, replant path taken) to aid future field diagnosis.
- Fixed NPC farmer not watering corner (diagonally adjacent) farm plots.
WaterPipeManager.GetWaterForPosition() only checked the 6 orthogonal neighbors for
direct water adjacency, so plots diagonally adjacent to a water source returned no
water and were skipped by the NPC despite being plantable by players. Added the 4
horizontal diagonal neighbors to the scan so corner plots are correctly detected.
[ Fire Manager ]
- Fixed fire particle left-behind after a burning block is destroyed by a player or
explosion. ChunkSetBlock Harmony Postfix previously skipped all non-POI-reset block
changes due to an inverted guard (if (!_fromReset) return). The fix restructures the
check: POI resets always clear fire unconditionally; for other block changes, the patch
now checks IsBurning() and immediately calls ClearFire() if the new block is air. This
prevents the particle from lingering until the next UpdateFires cycle (up to
CheckInterval seconds). IsBurning() is an O(1) dictionary lookup, so the overhead on
every block change is negligible.
[ Utility / Code Quality ]
- Removed duplicate ItemStack.Empty guard in EntityUtilities.CheckItemStack(ItemStack, Type):
the same Equals(stack, ItemStack.Empty) check was being evaluated twice with only a null
check in between.
Version: 2.6.8.1428 [ Experimental ]
[ Fire Manager ]
- Fixed fire state (active fires and smoke) not properly restoring on load. The Read() method now
correctly rebuilds fire data, restarts particle/light effects, and re-raises smoke events.
- Fixed smoke timers using real time (Time.time) instead of world time, causing smoke to
expire immediately on relog.
- Fixed loaded fires not being synced to clients after a server load.
- Fixed FireDamage property not being read from blocks due to an empty string check bug.
- Added FirePersists config option (default: false). When true, active fires and extinguished
positions are saved and restored across server restarts.
[ Fire Manager - Performance ]
- Cached flammable/inflammable FastTags as static fields in FireHandler; previously parsed
on every IsFlammable() call during fire spread.
- Replaced O(n²) string concatenation in FireHandler.Write() with string.Join; large fires
caused severe save slowdowns.
- Replaced the new Queue allocation in FireHandler.UpdateFires() with a clear-and-refill
on the existing queue to avoid per-cycle heap allocation.
- Replaced LINQ allocations in SmokeHandler.CheckSmokePositions() and Clear() with manual
loops using a reusable buffer field.
- Fixed dead loop in SmokeHandler.LoadState() that iterated the smoke timer dictionary
immediately after clearing it.
- Replaced LINQ allocations in LightManager.RemoveInvalidLights() with a manual loop
using a reusable buffer field.
- Replaced per-call new HashSet allocation in LightManager.UpdateFadingLights() with a
reusab...
2.6.8.837
Version: 2.6.8.837 [ Experimental ]
[ Farming ]
- Fixed water depletion not working: BlockLiquidv2 blocks are now drained via DoExchangeAction; A21+ voxel water is drained via chunk.GetWater/SetWater with chunk-local coordinates.
- Fixed pipe blocks being incorrectly identified as direct water sources (IsDirectWaterSource now excludes BlockWaterPipeSDX before checking world.IsWater).
- Fixed CropManager.IsNearWater ignoring the caller's waterRange parameter (was creating a temporary PlantData on an air block, falling back to default range of 5).
- Fixed NPC farming: seed loss and race condition on replant (now uses a single atomic SetBlocksRPC with [air, seed] entries).
- Fixed NPC farming: seed is now returned to harvest items or NPC inventory if CanPlaceBlockAt fails.
- Fixed NPC harvested items being silently lost when NPC inventory is full; items are now dropped on the ground instead.
[ Sprinklers ]
- Fixed sprinklers auto-activating when placed without a connected water source (CheckWaterConnection now always verifies actual water).
- Fixed RequirePipesForSprinklers config not being enforced: CanPlaceBlockAt now requires an adjacent pipe block when the setting is enabled.
- Simplified pipe block invalidation logic to use RefreshAllSprinklers.
[ XUi ]
- Moved the broadcast button position to prevent overlap.
[ Drop Box ]
- Fixed an issue with drop box without a sign
2.6.4.713
Version: 2.6.4.713 [ Experimental ]
[ Fire Manager ]
- Fixed an issue where fire wasn't restarted on relog when enabled.
[ Challenges ]
- Removed an unneccesary safety check that was causing problems with null reference
[ Farming ]
- Refactored the Manage method for planting crops, and added a catch if inventory is full
[ NPCs ]
- Fixed an issue where NPCs could be duplicated when the player dies.
2.5.53.911
Version: 2.5.53.911
[ NPCs ]
- Fixed an issue where an NPC would get run over while you were driving, and you'd pay the price.
[ Drop Box ]
- Refactored distribution code slightly to add guards against lost item stacks during distribution.
[ Challenges ]
- Added a check in Sleeper Cleared to make sure that another player is within the same POI as the clearer.
2.5.48.914
Version: 2.5.48.914
[ NPCs ]
- Added a patch by Gussak to fix NPCs getting stuck on corpses.
- https://github.com/SphereII/SphereII.Mods/issues/133
[ Remote Repair ]
- Added some fixes to prevent repairing from remote containers when enemies are around.
[ Quality ]
- Fixed an issue where item values of quality in the xml will scale to the quality settings over all.
- ie, if you have 1 to 600 quality levels, and your xml list only 6 possible values ( vanilla ), then the tier of the quality item will be used
- as an index value, rather than throwing out of bounds.
Example:
<! quality 56 would use 02. Quality 500 would use .1, etc -->
<triggered_effect trigger="onSelfEquipUpdate" action="ModifyCVar" cvar="$treatedPreacher" operation="set" value=".02,.04,.06,.08,.1,.2">
[ UAI ]
- Added a safety check for UAI Consideration Target Weapon Range
[ Quests ]
- Integrated some patches by khzmusik.
```
This implements the feature request from ticket 127, but also fixes other bugs that were introduced by vanilla game updates.
New feature for [Teleport Quests] Localization of Objective keyword in ObjectiveGotoPOISDX.cs and related Objective patches.
#127: Subtypes of ObjectiveRandomPOIGoto use their own localization keys for keywords (and not just "ObjectiveRallyPointHeadTo") -
by default their values match vanilla in Localization.txt
Subtypes of ObjectiveRandomPOIGoto no longer need their own implementations of SetDistanceOffset (the base class method was made public in a recent game update)
QuestUtils.ValidPrefabForQuest once again checks for quest tags (if it didn't, ObjectiveRandomTaggedPOIGotoSDX would not be compatible with vanilla quests)
I don't know why the code was originally commented out, but I took a guess that POIs weren't matching if the XML property was empty, so I additionally added a check for questTag.IsEmpty to make sure empty quest tags aren't considered
QuestUtils.GetRandomPOINearTrader code updated to have the same functionality as DynamicPrefabDecorator.GetRandomPOINearTrader (which was updated for 2.5)
Adjusted min search distances to match current vanilla value ObjectiveRandomTaggedPOIGotoSDX.CopyValues copies the values which are copied in the base class Clone method (this method can't be called by subclasses)
Minor fixes to ObjectiveRandomTaggedPOIGotoSDX (documentation, renamed inaccurately-named variable)
```
2.5.39.1103
Version: 2.5.39.1103
[ Quality]
- Backed out a bad patch causing cvars and buffs not to apply correctly.
2.5.36.1511
Version: 2.5.36.1511
[ Quality ]
- Fixed an issue where Quality levels above 255 were being reset to lower values when crafting in closed workstation.
- The game code was casting ItemQuality to a byte (limit 255) during the UI synchronization process,
causing any quality higher than 255 (e.g., 600) to overflow and wrap around (e.g., 600 became 88).
- We implemented a "Bit-Packing" strategy where the UI patch hides the high-quality value inside the
unused bits of the StartingEntityId integer (which survives the byte cast), and a second patch in AddCraftComplete
unpacks that value to restore the correct quality just before the item is finalized.
2.5.35.841
Version: 2.5.35.841
[ Challenges ]
- Added missing requirement check on BlockDestroyedByFire
[ Fire Manager ]
- Re-fixed(1)(2) fire extinguish being recursive.
- Also may have fixed the challenge for extinguish.
2.5.30.1209
Version: 2.5.30.1209
[ Challenges ]
- Removed debug lines
[ Quality ]
- Fixed an issue with ModifyCVar script not correctly calculating the quality tier levels.
- Added logging for crafting recipes where they lose their reference when crafting with the workstation closed.
[ EntityNPCBandit ]
- Removed some inappropriate checks, such as playerstat changed.
2.5.25.1105
Version: 2.5.25.1105
[ NPCs ]
- Fixed an issue where NPCs would duplicate on dedi, and respawn dupes when reloading the game.
[ UAI ]
- Fixed a bug where an NPC would sort of slide towards you rather than actually start running with you.
- Optimized the UAI Follow task to perform a bit better.
[ Challenges ]
- Reworked requirements for Challenges to work more smoothly with the game.
*** XML Change Required! ***
- Effect groups are now required for requirements.
BEFORE:
<challenge name="burntSurvivalPlantTrees" title_key="challengeBurntPlantTrees" icon="ui_game_symbol_tree" group="ScoreTest"
short_description_key="challengeBurntPlantTreesShort" description_key="challengeBurntPlantTreesDesc"
reward_text_key="challenge_reward_1000xp" reward_event="challenge_reward_1000">
<objective type="PlaceBlockByTagV2, SCore" count="25" description_key="xuiPlantTrees"/>
<requirement name="InBiome" biome="9"/>
<requirement name="BlockHasTags" tags="challenge_plant_trees"/>
</challenge>
AFTER:
<challenge name="burntSurvivalPlantTrees2" title_key="challengeBurntPlantTrees2" icon="ui_game_symbol_tree" group="ScoreTest"
short_description_key="challengeBurntPlantTreesShort" description_key="challengeBurntPlantTreesDesc"
reward_text_key="challenge_reward_1000xp" reward_event="challenge_reward_1000">
<objective type="PlaceBlockByTagV2, SCore" count="25" description_key="xuiPlantTrees"/>
<effect_group name="Groups are required">
<requirement name="InBiome" biome="9"/>
<requirement name="BlockHasTags" tags="challenge_plant_trees"/>
</effect_group>
</challenge>