fix: sync ZoneLocation radii/ClearArea from prefab's Location component#472
Conversation
LocationConfig.ExteriorRadius, InteriorRadius, and ClearArea are now nullable. GetZoneLocation falls back to the previous defaults (10f/0f/false) when null so existing callers are unaffected. CustomLocation now syncs these three fields from the prefab's Location component onto the ZoneLocation whenever the corresponding config field is unset (null). Applied in both constructors: - Non-SoftReference ctor: sync runs immediately after GetZoneLocation, using the component that was authored on (or added to) the prefab. - SoftReference ctor: sync runs inside OnLocationResolve once the prefab asset is actually loaded. The LocationConfig reference is stored so the callback can check which fields the caller left null. Before this change, ZoneLocation.m_exteriorRadius (used by world-gen terrain-delta sampling and spawn-time ClearArea/slope sampling) could diverge from Location.m_exteriorRadius authored in Unity Editor, leading to placements that failed terrain checks at a radius smaller than the authored footprint and vegetation clearing that stopped short. Semantics: config wins when set; otherwise prefab component wins; otherwise the original defaults apply. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Other custom managers could also benefit from a similar feature! I would argue all fields should be set this way, where if they do not exist it pulls the value from the prefab rather than setting a default value. It will help speed up development so we do not need to specify values in both Unity and in code. Specifying in code can be used for quick testing of changing config values or updates/overrides after the asset bundle has been created. |
|
Unfortunately, a change from E.g. arbitrarily with RtDMonsters, but any other mod that access one of the changed types: It's a MissingMethodException because C# converts getter/setter to methods internally. |
|
@MSchmoecker I made a change to address backward compatibility and tested with RtDMonsters. I did not get the MissingMethodException error you shared above. |
| Location.m_clearArea = locationConfig.ClearArea; | ||
| Location.m_exteriorRadius = locationConfig.ExteriorRadius; | ||
| if (locationConfig.HasClearArea) Location.m_clearArea = locationConfig.ClearArea; | ||
| if (locationConfig.HasExteriorRadius) Location.m_exteriorRadius = locationConfig.ExteriorRadius; | ||
| Location.m_interiorPrefab = interiorPrefab; | ||
| Location.m_hasInterior = locationConfig.HasInterior; | ||
| Location.m_interiorRadius = locationConfig.InteriorRadius; | ||
| if (locationConfig.HasInteriorRadius) Location.m_interiorRadius = locationConfig.InteriorRadius; |
There was a problem hiding this comment.
Shouldn't this stay as it was before and not check the HasClearArea etc flags?
The Location component is freshly created and thus uses initializes with default values, instead of the fallback values from locationConfig
There was a problem hiding this comment.
@MSchmoecker Yes, that's a good point. I made a change to that
MSchmoecker
left a comment
There was a problem hiding this comment.
Thanks, this is good to merge now!
The Issue
LocationConfig.ExteriorRadius,InteriorRadius, andClearAreaeach exist in two independent places:LocationMonoBehaviour on the prefab.ZoneSystem.ZoneLocationregistration record.CustomLocationsourcedZoneLocation.m_exteriorRadiusexclusively fromLocationConfig.ExteriorRadius(defaulting to10f). The prefab'sLocationcomponent was preserved when present but never read into theZoneLocation, so the two values could silently diverge.Where It Surfaced
The mismatch was caught via VentureValheim/LocationReset, which resets the contents of a location to their originally generated state. Its
LocationReset.LocationPositionconstructor reads the reset boundary fromZoneSystem.ZoneLocation:LocationReset calls
TryResetAfterLoadPrefab, withzonefetched viaZoneSystem.instance.GetLocation(hash). For More World Locations AIO locations,zone.m_exteriorRadiuscarried theLocationConfigvalue while the radius set in UnityEditor was larger. Therefore, LocationReset only reset a subset of the location's actual footprint and left objects beyond the config radius unreset.The Fix
LocationConfigThe three shared fields become nullable so Jötunn can distinguish "caller explicitly set this" from "caller left this unset." When a field is
null,CustomLocationtreats the prefab'sLocationcomponent as the source of truth; when it has a value, that value wins.GetZoneLocation()falls back to the prior defaults when unset, preserving behavior for prefabs without aLocationcomponent. Existing callers that assign a value compile unchanged via implicit conversion fromfloat/boolto the nullable types.CustomLocationSynchronizes the three shared fields onto the
ZoneLocationfrom the prefab'sLocationcomponent whenever the corresponding config field isnull. Config wins when set; otherwise the component's value flows through.SyncZoneLocationFromComponent()runs immediately afterGetZoneLocation().SyncZoneLocationFromComponent()runs insideOnLocationResolve(GameObject)once the asset loads, mutating the already-registeredZoneLocationin place. SinceZoneLocationis a reference type,ZoneSystem.m_locationssees the update.