Jump to content

MCGUFIN

  • entries
    9
  • comments
    0
  • views
    389

LocOps


Monoman1

79 views

LocOps - Location Operations - The yellow pages of Skyrim. 

Get information about the area you're in. The system is data driven and can be expanded via Json. I've already got one for JK's Skyrim. Multiple jsons are 'rolled together' for a complete picture. 

 

Highlights: 

  • Look up people by roles/location, Jarl's, Stewards, House carls, Court wizards, Inn keepers, blacksmiths, shop keepers, etc. 
  • Respects civil war status. 
  • Location agnostic. You don't need to know where the player is. MGF will figure that out for you. 
  • Layered area, hold, world scripts/functions. 
  • Slaverun status. 
  • Vanilla home ownership. 
  • Thane status. 
  • Custom roles. You can expand the lookup with custom roles (not Jarl, Blacksmith etc) via Json. Eg: 'Tailor'. Again duplicate roles will be rolled together. 
  • Coming soon: Get the closest whatever - Example in LocOpsWorld. 

 

2 'Points Of Interest' quests - (Area / Hold) with aliases that are automatically populated on area/hold change: 
image.png

 

Some of these I'm still cleaning up. 


LocOpsArea: 
 

Spoiler
Scriptname _MGF_LocOpsArea extends Quest

; The yellow pages for Skyrim (local area edition)

;========================================================================================
; User Functions ========================================================================
;========================================================================================

; Should probably stick to calling 'user functions' only.

; AreaStateName = Primary Location Editor ID. See LocationMap.json. Anywhere in Riverwood = 'RiverwoodLocation'
; Walled cities are the exception.
; Inside city walls = Primary location editor ID. Eg: Anywhere inside Whiterun = 'WhiterunLocation'
; Outside city walls are '<City>Outskirts'. Eg: 'WhiterunOutskirts'

Import _MGF_ArrayCreate
Import _MGF_ArrayEdit
Import _MGF_ArrayQuery

Function UpdatePointsOfInterest()
	BeginPoiUpdate(Store.LocTrack.AreaStateName)
EndFunction

Bool Function GetIsSlaverunFreeTownByString(String AreaStateName)
	; True - free
	; False - enslaved

	_MGF_LocOpsArea_Base Handler = GetHandler(AreaStateName)
	If !Handler
		; No dedicated handler for this AreaStateName. 
		; Fall back to the hold's overall enslavement status rather than defaulting straight to "free". 
		If AreaStateName != "Wilderness"
			Return LocOpsHold.GetIsSlaverunFreeHoldByString(Store.LocTrack.GetHoldStateNameForArea(AreaStateName))
		EndIf
		Handler = DefaultHandler
	EndIf
	Return Handler.IsSlaverunFreeTown()
EndFunction

Bool Function GetIsSlaverunFreeTownByIndex(Int Index)
	Return GetIsSlaverunFreeTownByString(Store.LocTrack.GetAreaStateNameByIndex(Index))
EndFunction

Bool Function GetIsSlaverunFreeTownHere()
	Return GetIsSlaverunFreeTownByString(Store.LocTrack.AreaStateName)
EndFunction

Bool Function GetOwnsVanillaHomeInArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).OwnsVanillaHomeHere()
EndFunction

Bool Function GetOwnsVanillaHomeHere()
	Return GetOwnsVanillaHomeInArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetLeaderForArea(String AreaStateName)
	; Returns the guy in charge. The big cheese. The head honcho. The main cahone.
	; Main towns - this is always going to be the jarl.
	; Orc strongholds - The orc chief
	Return GetHandlerOrDefault(AreaStateName).GetLeader(AreaStateName)
EndFunction

Actor Function GetLeaderHere()
	Return GetLeaderForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetStewardForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetSteward(AreaStateName)
EndFunction

Actor Function GetStewardHere()
	Return GetStewardForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetCourtWizardForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetCourtWizard(AreaStateName)
EndFunction

Actor Function GetCourtWizardHere()
	Return GetCourtWizardForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetHousecarlForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetHousecarl(AreaStateName)
EndFunction

Actor Function GetHousecarlHere()
	Return GetHousecarlForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetHostlerForArea(String AreaStateName)
	; The guy that sells horses. 
	Return GetHandlerOrDefault(AreaStateName).GetHostler(AreaStateName)
EndFunction

Actor Function GetHostlerHere()
	; The guy that sells horses. 
	Return GetHostlerForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetLicenceOfficerForArea(String AreaStateName)
	Return Store.ForkLicence.GetLicenceOfficerForArea(AreaStateName)
EndFunction

Actor Function GetLicenceOfficerHere()
	Return GetLicenceOfficerForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor Function GetDriverForArea(String AreaStateName)
	; Carriage driver
	Return GetHandlerOrDefault(AreaStateName).GetDriver(AreaStateName)
EndFunction

Actor Function GetDriverHere()
	; Carriage driver
	Return GetDriverForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor[] Function GetInnkeepersForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetInnkeepers(AreaStateName)
EndFunction

Actor[] Function GetInnkeepersHere()
	Return GetInnkeepersForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor[] Function GetBlacksmithsForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetBlacksmiths(AreaStateName)
EndFunction

Actor[] Function GetBlacksmithsHere()
	Return GetBlacksmithsForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor[] Function GetGeneralTradersForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetGeneralTraders(AreaStateName)
EndFunction

Actor[] Function GetGeneralTradersHere()
	Return GetGeneralTradersForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor[] Function GetAlchemistsForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetAlchemists(AreaStateName)
EndFunction

Actor[] Function GetAlchemistsHere()
	Return GetAlchemistsForArea(Store.LocTrack.AreaStateName)
EndFunction

Actor[] Function GetPriestsForArea(String AreaStateName)
	Return GetHandlerOrDefault(AreaStateName).GetPriests(AreaStateName)
EndFunction

Actor[] Function GetPriestsHere()
	Return GetPriestsForArea(Store.LocTrack.AreaStateName)
EndFunction

ObjectReference[] Function GetCustomRoleForArea(String AreaStateName, String RoleName)
	; Custom role lookup for third-party mods - see _MGF_LocOpsArea_Base.GetCustomRole() for details. 
	; Any role name works, e.g. GetCustomRoleForArea("WindhelmLocation", "Tailor"). 
	; No changes to this script ever required. Wrapped in ValidateRefs() as a safety net. 
	; If two mods use the same role for the same location (but different jsons obviously) then all actors 
	; from both mods will be rolled into the same role. 
	; Deliberately ObjectReference[] typed to allow the return of 'things' and not just actors.

	Return _MGF_Util.ValidateRefs(GetHandlerOrDefault(AreaStateName).GetCustomRole(AreaStateName, RoleName))
EndFunction

ObjectReference[] Function GetCustomRoleHere(String RoleName)
	Return GetCustomRoleForArea(Store.LocTrack.AreaStateName, RoleName)
EndFunction

; =======================================================================================
; Internal Stuff ========================================================================
; =======================================================================================


; Handler Dispatch ======================================================================
; Replaced the old state machine mechanics which had the potential to mix up requests 
; if LocOps ever got clobbered with simultaneous requests. 


_MGF_LocOpsArea_Base Function GetHandler(String AreaStateName)
	; Returns None if Index is out of range (AreaStateName not in the table) 
	
	Int Index = AreaStateNames.Find(AreaStateName)
	If Index >= 0
		Return GetNthAlias(Index) as _MGF_LocOpsArea_Base
	EndIf
	Return None
EndFunction

_MGF_LocOpsArea_Base Function GetHandlerOrDefault(String AreaStateName)
	_MGF_LocOpsArea_Base Handler = GetHandler(AreaStateName)
	If Handler
		Return Handler
	EndIf
	Return DefaultHandler
EndFunction

Event OnInit()
	GenerateAreaStateNames()
EndEvent

Function GenerateAreaStateNames()
	; Dynamically generates AreaStateNames (all areas with a special handler script) via the alias names. 
	; This forms the 1:1 AreaStateName to Alias index handler map. See GetHandler()	
	AreaStateNames = _MGF_Util.GetAliasNames(Self, 1, False)
	
	; Last alias should always be the default (base) handler. Not an actual area - Pluck.
	AreaStateNames = ArrayStringPluck(AreaStateNames, AreaStateNames.Length - 1)
EndFunction

Function BeginPoiUpdate(String AreaStateName)
	; Fills the PointsOfInterest quest with AreaStateName's POIs. 
	; Or clears them if AreaStateName is not mapped. 
	If AreaStateName == "" ; unknown
		ClearPointsOfInterest()
	Else
		FillPointsOfInterest(AreaStateName)
	EndIf
EndFunction

Function FillPointsOfInterest(String AreaStateName)
	_MGF_LocOpsArea_Base Handler = GetHandlerOrDefault(AreaStateName)

	FillAlias(Store.PoiAliasesArea[0], Handler.GetLeader(AreaStateName))
	FillAlias(Store.PoiAliasesArea[1], Handler.GetSteward(AreaStateName))
	FillAlias(Store.PoiAliasesArea[2], Handler.GetCourtWizard(AreaStateName))
	FillAlias(Store.PoiAliasesArea[3], Handler.GetHousecarl(AreaStateName))
	FillAlias(Store.PoiAliasesArea[4], Handler.GetHostler(AreaStateName))
	FillAlias(Store.PoiAliasesArea[5], Store.ForkLicence.GetLicenceOfficerForArea(AreaStateName))

	; GetInnkeepers/Blacksmiths/GeneralTraders/Alchemists/Priests already return Actor[] (filtered for disabled/dead). 
	FillAliasArray(Store.PoiAliasesArea[6], ActorsToRefs(Handler.GetInnkeepers(AreaStateName)))
	FillAliasArray(Store.PoiAliasesArea[7], ActorsToRefs(Handler.GetBlacksmiths(AreaStateName)))
	FillAliasArray(Store.PoiAliasesArea[8], ActorsToRefs(Handler.GetGeneralTraders(AreaStateName)))
	FillAliasArray(Store.PoiAliasesArea[9], ActorsToRefs(Handler.GetAlchemists(AreaStateName)))
	FillAliasArray(Store.PoiAliasesArea[10], ActorsToRefs(Handler.GetPriests(AreaStateName)))
EndFunction

; =======================================================================================
; Helpers ===============================================================================
; =======================================================================================

Function ClearPointsOfInterest()
	Int i = 0
	While i < Store.PoiAliasesArea.Length
		Store.PoiAliasesArea[i].Clear()
		i += 1
	EndWhile
EndFunction

Function FillAlias(ReferenceAlias akAlias, ObjectReference ObjRef)
	If ObjRef
		akAlias.ForceRefTo(ObjRef)
	Else
		akAlias.Clear()
	EndIf
EndFunction

Function FillAliasArray(ReferenceAlias akAlias, ObjectReference[] POIs)
	If POIs.Length
		akAlias.ForceRefTo(POIs[POIs.Length - 1]) ; Prefer last element - Primary location POI
	Else
		akAlias.Clear()
	EndIf
EndFunction

String[] Function GetSpecialAreas()
	; Every AreaStateName with a dedicated handler script. 
	; i.e. exactly what GetHandler() can resolve to something other than DefaultHandler.
	Return AreaStateNames
EndFunction

String[] Function GetBuiltInRoles()
	; The fixed set of built-in role names. 
	; See the property comment on _MGF_LocOpsArea_PropertyStore.BuiltInRoles for the canonical list.
	Return Store.BuiltInRoles
EndFunction

; =======================================================================================
; Debug =================================================================================
; =======================================================================================

Function DumpHandlers()
	; Debug utility - walks AreaStateNames and prints what each alias index actually resolves to. 
	
	Debug.Trace("[_MGF_LocOpsArea] ==== DumpHandlers: " + AreaStateNames.Length + " entries ====")

	Int i = 0
	While i < AreaStateNames.Length
		_MGF_LocOpsArea_Base Handler = GetNthAlias(i) as _MGF_LocOpsArea_Base
		String Status = ""
		If !Handler
			Status = " <<< NONE - no script attached to this alias, or wrong script type"
		EndIf
		Debug.Trace("[_MGF_LocOpsArea] [" + i + "] " + AreaStateNames[i] + " -> " + Handler + Status)
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsArea] DefaultHandler -> " + DefaultHandler)
	Debug.Trace("[_MGF_LocOpsArea] ==== DumpHandlers: done ====")
EndFunction


Function DumpDirectoryFull()
	; Debug utility - cycles through every AreaStateName and calls the real accessor functions 
	; (GetLeaderForArea, GetInnkeepersForArea, etc.) - the same path an actual caller takes. 

	Debug.Trace("[_MGF_LocOpsArea] ==== DumpDirectoryFull: " + AreaStateNames.Length + " entries ====")

	Int i = 0
	While i < AreaStateNames.Length
		String AreaStateName = AreaStateNames[i]
		_MGF_LocOpsArea_Base Handler = GetNthAlias(i) as _MGF_LocOpsArea_Base

		If !Handler
			Debug.Trace("[_MGF_LocOpsArea] [" + i + "] " + AreaStateName + " <<< NONE - no script attached to this alias, or wrong script type")
		Else
			Debug.Trace("[_MGF_LocOpsArea] [" + i + "] " + AreaStateName + " (" + Handler + ")")
			Debug.Trace("[_MGF_LocOpsArea]	IsSlaverunFreeTown: " + GetIsSlaverunFreeTownByString(AreaStateName))
			Debug.Trace("[_MGF_LocOpsArea]	OwnsVanillaHome: " + GetOwnsVanillaHomeInArea(AreaStateName))
			Debug.Trace("[_MGF_LocOpsArea]	Leader: " + _MGF_Util.GetActorName(GetLeaderForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	Steward: " + _MGF_Util.GetActorName(GetStewardForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	CourtWizard: " + _MGF_Util.GetActorName(GetCourtWizardForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	Housecarl: " + _MGF_Util.GetActorName(GetHousecarlForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	Hostler: " + _MGF_Util.GetActorName(GetHostlerForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	Driver: " + _MGF_Util.GetActorName(GetDriverForArea(AreaStateName)))
			Debug.Trace("[_MGF_LocOpsArea]	LicenceOfficer: " + _MGF_Util.GetActorName(GetLicenceOfficerForArea(AreaStateName)))
			TraceActorList("Innkeepers", GetInnkeepersForArea(AreaStateName))
			TraceActorList("Blacksmiths", GetBlacksmithsForArea(AreaStateName))
			TraceActorList("GeneralTraders", GetGeneralTradersForArea(AreaStateName))
			TraceActorList("Alchemists", GetAlchemistsForArea(AreaStateName))
			TraceActorList("Priests", GetPriestsForArea(AreaStateName))
		EndIf
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsArea] DefaultHandler -> " + DefaultHandler)
	Debug.Trace("[_MGF_LocOpsArea] ==== DumpDirectoryFull: done ====")
EndFunction


Function DumpDirectoryFromJson()
	; Debug utility - reads the FULL location list straight from the authoritative LocationMap.json "locationstates" array 
	; ( every settlement/camp/cabin the game tracks), rather than the AreaStateNames property (only the locations with a
	; dedicated handler script). 
	; Exercises GetHandlerOrDefault() and the default-fallback StorageUtil path for EVERY known area, not just the
	; ones with a handler

	String JsonPath = "Data/SKSE/Plugins/StorageUtilData/Mcgufin/Internal/LocationMap.json"
	String[] AllAreaStates = _MGF_File.JsonStringListGet(JsonPath, "locationstates")

	Debug.Trace("[_MGF_LocOpsArea] ==== DumpDirectoryFromJson: " + AllAreaStates.Length + " entries from LocationMap.json ====")

	Int i = 0
	While i < AllAreaStates.Length
		String AreaStateName = AllAreaStates[i]
		Debug.Trace("[_MGF_LocOpsArea] [" + i + "] " + AreaStateName)
		Debug.Trace("[_MGF_LocOpsArea]     IsSlaverunFreeTown: " + GetIsSlaverunFreeTownByString(AreaStateName))
		Debug.Trace("[_MGF_LocOpsArea]     OwnsVanillaHome: " + GetOwnsVanillaHomeInArea(AreaStateName))
		Debug.Trace("[_MGF_LocOpsArea]     Leader: " + _MGF_Util.GetActorName(GetLeaderForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     Steward: " + _MGF_Util.GetActorName(GetStewardForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     CourtWizard: " + _MGF_Util.GetActorName(GetCourtWizardForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     Housecarl: " + _MGF_Util.GetActorName(GetHousecarlForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     Hostler: " + _MGF_Util.GetActorName(GetHostlerForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     Driver: " + _MGF_Util.GetActorName(GetDriverForArea(AreaStateName)))
		Debug.Trace("[_MGF_LocOpsArea]     LicenceOfficer: " + _MGF_Util.GetActorName(GetLicenceOfficerForArea(AreaStateName)))
		TraceActorList("Innkeepers", GetInnkeepersForArea(AreaStateName))
		TraceActorList("Blacksmiths", GetBlacksmithsForArea(AreaStateName))
		TraceActorList("GeneralTraders", GetGeneralTradersForArea(AreaStateName))
		TraceActorList("Alchemists", GetAlchemistsForArea(AreaStateName))
		TraceActorList("Priests", GetPriestsForArea(AreaStateName))
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsArea] ==== DumpDirectoryFromJson: done ====")
EndFunction

Function TraceActorList(String Label, Actor[] akActors)
	; Helper for DumpDirectoryFull()/DumpDirectoryFromJson(). 
	; Joins an Actor[] into one readable, comma-separated log line via
	; _MGF_Util.GetActorNames() + _MGF_ArrayCreate.ArrayStringJoin().
	String[] Names = _MGF_Util.GetActorNames(akActors)
	If Names.Length == 0
		Debug.Trace("[_MGF_LocOpsArea]     " + Label + ": (none)")
	Else
		Debug.Trace("[_MGF_LocOpsArea]     " + Label + ": " + ArrayStringJoin(Names, ", "))
	EndIf
EndFunction

;==================================================================================================
; Properties =======================================================================================
;==================================================================================================

_MGF_LocOpsArea_PropertyStore Property Store Auto
_MGF_LocOpsHold Property LocOpsHold Auto

; Attached to its own alias, OUTSIDE the 0-25 indexed range below (e.g.
; alias 26) - just the plain _MGF_LocOpsArea_Base script, no subclass.
; Used for any AreaStateName with no dedicated handler (mostly "Wilderness"),
; and as the fallback when a real handler's alias is wired wrong.
_MGF_LocOpsArea_Base Property DefaultHandler Auto

; AreaStateNames is computed automatically at OnInit from the alias
; names themselves (see GenerateAreaStateNames() above) - nothing to
; fill here directly. What actually needs doing in the CK is naming
; each alias to match the list below exactly, in order. Walled-city
; pairs (e.g. Windhelm / WindhelmOutskirts) get two ALIASES both
; hosting the same handler script - see the earlier conversation on
; why duplicate aliases beat a parallel Int[] index-mapping array here.
; 26 area-state aliases total (indices 0-25), mapping to 21 distinct
; handler scripts:
;
;   0  WindhelmLocation
;   1  WindhelmOutskirts
;   2  WinterholdLocation
;   3  SolitudeLocation
;   4  SolitudeOutskirts
;   5  MarkarthLocation
;   6  MarkarthOutskirts
;   7  RiftenLocation
;   8  RiftenOutskirts
;   9  WhiterunLocation
;   10 WhiterunOutskirts
;   11 FalkreathLocation
;   12 BYOHHouse1Location   (Lakeview Manor)
;   13 MorthalLocation
;   14 BYOHHouse2Location   (Windstad Manor)
;   15 DawnstarLocation
;   16 BYOHHouse3Location   (Heljarchen Hall)
;   17 NarzulburLocation
;   18 DushnikhYalLocation
;   19 MorKhazgurLocation
;   20 LargashburLocation
;   21 OldHroldanLocation
;   22 IvarsteadLocation
;   23 ShorsStoneLocation
;   24 RiverwoodLocation
;   25 RoriksteadLocation
;
; Alias 26 (DefaultHandler) needs no name matching anything here - it's
; excluded from this array entirely (see GenerateAreaStateNames()).
;
; Adding a new area later: attach its handler script to a new alias at
; the next free index and name that alias to match - nothing else to
; update, GenerateAreaStateNames() picks it up automatically on next load.

String[] Property AreaStateNames Auto Hidden

 

 

LocOpsHold:
 

Spoiler
Scriptname _MGF_LocOpsHold extends Quest

; The yellow pages for Skyrim (Hold-wide edition)

;==================================================================================================
; User Functions ==================================================================================
;==================================================================================================

; Should probably stick to calling 'user functions' only.

; HoldStateName = The name of the hold with any whitespace removed. See LocationMap.json for details.
; Egs:
; 'Whiterun'
; 'ThePale'
; 'TheRift'

Import _MGF_ArrayCreate
Import _MGF_ArrayEdit
Import _MGF_ArrayQuery

Function UpdatePointsOfInterest()
	BeginPoiUpdate(Store.LocTrack.HoldStateName)
EndFunction

Bool Function GetIsSlaverunFreeHoldByString(String HoldStateName)
	; True - free
	; False - enslaved
	Return GetHandlerOrDefault(HoldStateName).IsSlaverunFreeHold()
EndFunction

Bool Function GetIsSlaverunFreeHoldHere()
	Return GetIsSlaverunFreeHoldByString(Store.LocTrack.HoldStateName)
EndFunction

Bool Function GetOwnsVanillaHomeInHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).OwnsVanillaHomeInHold()
EndFunction

Bool Function GetOwnsVanillaHomeHere()
	Return GetOwnsVanillaHomeInHold(Store.LocTrack.HoldStateName)
EndFunction

Bool Function GetIsThaneOfHold(String HoldStateName)
	; Is the player a thane of this hold
	Return GetHandlerOrDefault(HoldStateName).IsThaneOfHold()
EndFunction

Bool Function GetIsThaneOfHoldHere()
	; Should work anywhere within the hold borders
	Return GetIsThaneOfHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetJarlForHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).GetJarl()
EndFunction

Actor Function GetJarlHere()
	Return GetJarlForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetStewardForHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).GetSteward()
EndFunction

Actor Function GetStewardHere()
	Return GetStewardForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetCourtWizardForHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).GetCourtWizard()
EndFunction

Actor Function GetCourtWizardHere()
	Return GetCourtWizardForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetHousecarlForHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).GetHousecarl()
EndFunction

Actor Function GetHousecarlHere()
	Return GetHousecarlForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetHostlerForHold(String HoldStateName)
	Return GetHandlerOrDefault(HoldStateName).GetHostler()
EndFunction

Actor Function GetHostlerHere()
	Return GetHostlerForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor Function GetLicenceOfficerForHold(String HoldStateName)
	; Never went through the State/GoToState system in the original - not
	; hold-dependent handler logic, just a direct delegation.
	Return Store.ForkLicence.GetLicenceOfficerForHold(HoldStateName)
EndFunction

Actor Function GetLicenceOfficerHere()
	Return GetLicenceOfficerForHold(Store.LocTrack.HoldStateName)
EndFunction

; -- Hold-level aggregation --
; Aggregates the plural POI categories across every area within a hold
; (e.g. GetBlacksmithsForHold("Whiterun") merges Whiterun + Riverwood +
; Rorikstead + every other area GetAreaStateNamesForHold returns).
; Deliberately NOT applied to the singular leadership roles above
; (Jarl/Steward/CourtWizard/Housecarl/Hostler) - those are tied
; specifically to the hold's capital, and "aggregate all jarls in the
; hold" doesn't mean anything when only the capital has one.
;
; The "primary POI last in the array" convention within a single area's
; list doesn't carry any special meaning here - each area's own primary
; just ends up wherever that area's results land in the merge order.

Actor[] Function GetInnkeepersForHold(String HoldStateName)
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetInnkeepersForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetInnkeepersHere()
	Return GetInnkeepersForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor[] Function GetBlacksmithsForHold(String HoldStateName)
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetBlacksmithsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetBlacksmithsHere()
	Return GetBlacksmithsForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor[] Function GetGeneralTradersForHold(String HoldStateName)
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetGeneralTradersForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetGeneralTradersHere()
	Return GetGeneralTradersForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor[] Function GetAlchemistsForHold(String HoldStateName)
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetAlchemistsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAlchemistsHere()
	Return GetAlchemistsForHold(Store.LocTrack.HoldStateName)
EndFunction

Actor[] Function GetPriestsForHold(String HoldStateName)
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetPriestsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetPriestsHere()
	Return GetPriestsForHold(Store.LocTrack.HoldStateName)
EndFunction

; Custom role aggregation - same pattern, ObjectReference[] rather than
; Actor[] since GetCustomRoleForArea can return non-Actor entries
; (shrines, etc.).
ObjectReference[] Function GetCustomRoleForHold(String HoldStateName, String RoleName)
	ObjectReference[] Result
	String[] Areas = Store.LocTrack.GetAreaStateNamesForHold(HoldStateName)
	Int i = 0
	While i < Areas.Length
		Result = ArrayRefMerge(Result, LocOpsArea.GetCustomRoleForArea(Areas[i], RoleName))
		i += 1
	EndWhile
	Return Result
EndFunction

ObjectReference[] Function GetCustomRoleHere(String RoleName)
	Return GetCustomRoleForHold(Store.LocTrack.HoldStateName, RoleName)
EndFunction

;==================================================================================================
; Handler Dispatch ================================================================================
;==================================================================================================

; Same design as _MGF_LocOpsArea - alias order on this quest IS the
; lookup table. GetNthAlias(i) returns the alias, cast to
; _MGF_LocOpsHold_Base resolves to whichever specific handler script is
; attached to it. No registration, no array property, nothing to
; bootstrap at runtime - only the CK-side alias ordering to keep correct
; (see the property doc comment below).
;
; Returns None if HoldStateName isn't in the table, or if the alias at
; that index has no matching script attached. Most callers should use
; GetHandlerOrDefault() below.
_MGF_LocOpsHold_Base Function GetHandler(String HoldStateName)
	Int Index = HoldStateNames.Find(HoldStateName)
	If Index >= 0
		Return GetNthAlias(Index) as _MGF_LocOpsHold_Base
	EndIf
	Return None
EndFunction

_MGF_LocOpsHold_Base Function GetHandlerOrDefault(String HoldStateName)
	_MGF_LocOpsHold_Base Handler = GetHandler(HoldStateName)
	If Handler
		Return Handler
	EndIf
	Return DefaultHandler
EndFunction

; Every HoldStateName with a dedicated handler script - i.e. exactly
; what GetHandler() can resolve to something other than DefaultHandler.
String[] Function GetSpecialHolds()
	Return HoldStateNames
EndFunction

; Debug utility - walks HoldStateNames and prints what each alias index
; actually resolves to, so a CK wiring mistake (wrong script on an
; alias, or an alias left empty) shows up as a visible None in the log
; rather than a silent wrong-handler bug later. Call from console via
; "cgf _MGF_LocOpsHold DumpDirectory" (adjust to your actual quest
; editor ID) or from another debug script holding a reference to this
; quest.
; Debug utility - walks HoldStateNames and prints what each alias index
; actually resolves to, so a CK wiring mistake (wrong script on an
; alias, or an alias left empty) shows up as a visible None in the log
; rather than a silent wrong-handler bug later. Cheap - just resolves
; the handler, doesn't touch StorageUtil/aliases. See DumpDirectoryFull()
; below for a full data dump. Call from console via
; "cgf _MGF_LocOpsHold DumpHandlers" (adjust to your actual quest editor
; ID) or from another debug script holding a reference to this quest.
Function DumpHandlers()
	Debug.Trace("[_MGF_LocOpsHold] ==== DumpHandlers: " + HoldStateNames.Length + " entries ====")

	Int i = 0
	While i < HoldStateNames.Length
		_MGF_LocOpsHold_Base Handler = GetNthAlias(i) as _MGF_LocOpsHold_Base
		String Status = ""
		If !Handler
			Status = " <<< NONE - no script attached to this alias, or wrong script type"
		EndIf
		Debug.Trace("[_MGF_LocOpsHold] [" + i + "] " + HoldStateNames[i] + " -> " + Handler + Status)
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsHold] DefaultHandler -> " + DefaultHandler)
	Debug.Trace("[_MGF_LocOpsHold] ==== DumpHandlers: done ====")
EndFunction

; Debug utility - cycles through every HoldStateName and calls the real
; accessor functions (GetJarlForHold, GetStewardForHold, etc.) - the
; same path an actual caller takes, not just alias resolution - printing
; readable names via _MGF_Util.GetActorName instead of raw object
; references. Call from console via "cgf _MGF_LocOpsHold DumpDirectoryFull".
Function DumpDirectoryFull()
	Debug.Trace("[_MGF_LocOpsHold] ==== DumpDirectoryFull: " + HoldStateNames.Length + " entries ====")

	Int i = 0
	While i < HoldStateNames.Length
		String HoldStateName = HoldStateNames[i]
		_MGF_LocOpsHold_Base Handler = GetNthAlias(i) as _MGF_LocOpsHold_Base

		If !Handler
			Debug.Trace("[_MGF_LocOpsHold] [" + i + "] " + HoldStateName + " <<< NONE - no script attached to this alias, or wrong script type")
		Else
			Debug.Trace("[_MGF_LocOpsHold] [" + i + "] " + HoldStateName + " (" + Handler + ")")
			Debug.Trace("[_MGF_LocOpsHold]     IsSlaverunFreeHold: " + GetIsSlaverunFreeHoldByString(HoldStateName))
			Debug.Trace("[_MGF_LocOpsHold]     OwnsVanillaHome: " + GetOwnsVanillaHomeInHold(HoldStateName))
			Debug.Trace("[_MGF_LocOpsHold]     IsThaneOfHold: " + GetIsThaneOfHold(HoldStateName))
			Debug.Trace("[_MGF_LocOpsHold]     Jarl: " + _MGF_Util.GetActorName(GetJarlForHold(HoldStateName)))
			Debug.Trace("[_MGF_LocOpsHold]     Steward: " + _MGF_Util.GetActorName(GetStewardForHold(HoldStateName)))
			Debug.Trace("[_MGF_LocOpsHold]     CourtWizard: " + _MGF_Util.GetActorName(GetCourtWizardForHold(HoldStateName)))
			Debug.Trace("[_MGF_LocOpsHold]     Housecarl: " + _MGF_Util.GetActorName(GetHousecarlForHold(HoldStateName)))
			Debug.Trace("[_MGF_LocOpsHold]     Hostler: " + _MGF_Util.GetActorName(GetHostlerForHold(HoldStateName)))
			Debug.Trace("[_MGF_LocOpsHold]     LicenceOfficer: " + _MGF_Util.GetActorName(GetLicenceOfficerForHold(HoldStateName)))
			TraceActorList("Innkeepers (aggregate)", GetInnkeepersForHold(HoldStateName))
			TraceActorList("Blacksmiths (aggregate)", GetBlacksmithsForHold(HoldStateName))
			TraceActorList("GeneralTraders (aggregate)", GetGeneralTradersForHold(HoldStateName))
			TraceActorList("Alchemists (aggregate)", GetAlchemistsForHold(HoldStateName))
			TraceActorList("Priests (aggregate)", GetPriestsForHold(HoldStateName))
		EndIf
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsHold] DefaultHandler -> " + DefaultHandler)
	Debug.Trace("[_MGF_LocOpsHold] ==== DumpDirectoryFull: done ====")
EndFunction

; Helper for DumpDirectoryFull()/DumpDirectoryFromJson() - joins an
; Actor[] into one readable, comma-separated log line via
; _MGF_Util.GetActorNames() + _MGF_ArrayQuery.ArrayStringJoin().
Function TraceActorList(String Label, Actor[] akActors)
	String[] Names = _MGF_Util.GetActorNames(akActors)
	If Names.Length == 0
		Debug.Trace("[_MGF_LocOpsHold]     " + Label + ": (none)")
	Else
		Debug.Trace("[_MGF_LocOpsHold]     " + Label + ": " + ArrayStringJoin(Names, ", "))
	EndIf
EndFunction

; Debug utility - reads the full hold list straight from the
; authoritative LocationMap.json "holdstates" array (10 entries -
; "Wilderness" plus the 9 real holds) rather than our own HoldStateNames
; property. For Hold this covers the same ground either way (no holds
; are missing a dedicated handler the way DragonBridgeLocation was on
; the Area side), but keeps this dump automatically in sync with the
; JSON source if a hold is ever added, rather than needing a hardcoded
; list update. Note the aggregate categories can get large for
; "Wilderness" specifically - GetAreaStateNamesForHold("Wilderness")
; returns every area not assigned to one of the 9 real holds, so the
; merged Innkeepers/Blacksmiths/etc lists here cover a lot more ground
; than a real hold's would. Call from console via
; "cgf _MGF_LocOpsHold DumpDirectoryFromJson".
Function DumpDirectoryFromJson()
	String JsonPath = "Data/SKSE/Plugins/StorageUtilData/Mcgufin/Internal/LocationMap.json"
	String[] AllHoldStates = _MGF_File.JsonStringListGet(JsonPath, "holdstates")

	Debug.Trace("[_MGF_LocOpsHold] ==== DumpDirectoryFromJson: " + AllHoldStates.Length + " entries from LocationMap.json ====")

	Int i = 0
	While i < AllHoldStates.Length
		String HoldStateName = AllHoldStates[i]
		Debug.Trace("[_MGF_LocOpsHold] [" + i + "] " + HoldStateName)
		Debug.Trace("[_MGF_LocOpsHold]     IsSlaverunFreeHold: " + GetIsSlaverunFreeHoldByString(HoldStateName))
		Debug.Trace("[_MGF_LocOpsHold]     OwnsVanillaHome: " + GetOwnsVanillaHomeInHold(HoldStateName))
		Debug.Trace("[_MGF_LocOpsHold]     IsThaneOfHold: " + GetIsThaneOfHold(HoldStateName))
		Debug.Trace("[_MGF_LocOpsHold]     Jarl: " + _MGF_Util.GetActorName(GetJarlForHold(HoldStateName)))
		Debug.Trace("[_MGF_LocOpsHold]     Steward: " + _MGF_Util.GetActorName(GetStewardForHold(HoldStateName)))
		Debug.Trace("[_MGF_LocOpsHold]     CourtWizard: " + _MGF_Util.GetActorName(GetCourtWizardForHold(HoldStateName)))
		Debug.Trace("[_MGF_LocOpsHold]     Housecarl: " + _MGF_Util.GetActorName(GetHousecarlForHold(HoldStateName)))
		Debug.Trace("[_MGF_LocOpsHold]     Hostler: " + _MGF_Util.GetActorName(GetHostlerForHold(HoldStateName)))
		Debug.Trace("[_MGF_LocOpsHold]     LicenceOfficer: " + _MGF_Util.GetActorName(GetLicenceOfficerForHold(HoldStateName)))
		TraceActorList("Innkeepers (aggregate)", GetInnkeepersForHold(HoldStateName))
		TraceActorList("Blacksmiths (aggregate)", GetBlacksmithsForHold(HoldStateName))
		TraceActorList("GeneralTraders (aggregate)", GetGeneralTradersForHold(HoldStateName))
		TraceActorList("Alchemists (aggregate)", GetAlchemistsForHold(HoldStateName))
		TraceActorList("Priests (aggregate)", GetPriestsForHold(HoldStateName))
		i += 1
	EndWhile

	Debug.Trace("[_MGF_LocOpsHold] ==== DumpDirectoryFromJson: done ====")
EndFunction

;==================================================================================================
; Helper Functions ================================================================================
;==================================================================================================

Event OnInit()
	GenerateHoldStateNames()
EndEvent

; Same reasoning as _MGF_LocOpsArea's GenerateAreaStateNames() - see
; that comment for the full explanation of GetAliasNames' parameters
; and why SkipUnfilled=False matters. Alias 9 (DefaultHandler) has no
; corresponding HoldStateName and is excluded the same way.
Function GenerateHoldStateNames()
	HoldStateNames = _MGF_Util.GetAliasNames(Self, 1, False)
	HoldStateNames = ArrayStringPluck(HoldStateNames, HoldStateNames.Length - 1)
EndFunction

Function BeginPoiUpdate(String HoldStateName)
	If HoldStateName == "" ; unknown
		ClearPointsOfInterest()
	Else
		FillPointsOfInterest(HoldStateName)
	EndIf
EndFunction

Function FillPointsOfInterest(String HoldStateName)
	; Single GetHandler() resolution for the whole update, same reasoning
	; as _MGF_LocOpsArea's version - one lookup instead of five separate
	; ForHold() calls each re-resolving the handler independently.
	_MGF_LocOpsHold_Base Handler = GetHandlerOrDefault(HoldStateName)

	FillAlias(Store.PoiAliasesHold[0], Handler.GetJarl())
	FillAlias(Store.PoiAliasesHold[1], Handler.GetSteward())
	FillAlias(Store.PoiAliasesHold[2], Handler.GetCourtWizard())
	FillAlias(Store.PoiAliasesHold[3], Handler.GetHousecarl())
	FillAlias(Store.PoiAliasesHold[4], Handler.GetHostler())
	FillAlias(Store.PoiAliasesHold[5], Store.ForkLicence.GetLicenceOfficerForHold(HoldStateName))
EndFunction

Function ClearPointsOfInterest()
	Int i = 0
	While i < Store.PoiAliasesHold.Length
		Store.PoiAliasesHold[i].Clear()
		i += 1
	EndWhile
EndFunction

Function FillAlias(ReferenceAlias akAlias, ObjectReference ObjRef)
	If ObjRef
		akAlias.ForceRefTo(ObjRef)
	Else
		akAlias.Clear()
	EndIf
EndFunction

;==================================================================================================
; Properties =======================================================================================
;==================================================================================================

_MGF_LocOpsArea_PropertyStore Property Store Auto

; Needed for Hold-level POI aggregation - Hold calls Area's own
; GetXForArea() accessors for every area within a hold and merges the
; results.
_MGF_LocOpsArea Property LocOpsArea Auto

; Attached to its own alias, OUTSIDE the 0-8 indexed range below (e.g.
; alias 9) - just the plain _MGF_LocOpsHold_Base script, no subclass.
; Used for any HoldStateName with no dedicated handler, and as the
; fallback when a real handler's alias is wired wrong.
_MGF_LocOpsHold_Base Property DefaultHandler Auto

; HoldStateNames is computed automatically at OnInit from the alias
; names themselves (see GenerateHoldStateNames() above) - nothing to
; fill here directly. What actually needs doing in the CK is naming
; each alias to match the list below exactly, in order. No
; Outskirts-style pairing here - every hold gets exactly one alias:
;
;   0  Eastmarch
;   1  Falkreath
;   2  Haafingar
;   3  Hjaalmarch
;   4  ThePale
;   5  TheReach
;   6  TheRift
;   7  Whiterun
;   8  Winterhold
;
; Alias 9 (DefaultHandler) needs no name matching anything here - it's
; excluded from this array entirely (see GenerateHoldStateNames()).
;
; Adding a new hold later: attach its handler script to a new alias at
; the next free index and name that alias to match - nothing else to
; update, GenerateHoldStateNames() picks it up automatically on next load.

String[] Property HoldStateNames Auto Hidden

 

 

LocOpsWorld:
 

Spoiler
Scriptname _MGF_LocOpsWorld extends Quest

; The yellow pages for Skyrim (world edition)

Import _MGF_ArrayCreate
Import _MGF_ArrayEdit
Import _MGF_ArrayQuery

Actor[] Function GetAllJarls()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor Jarl = LocOpsHold.GetJarlForHold(Holds[i])
		If Jarl
			Result = ArrayActorAppend(Result, Jarl)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllJarlsByProximity()
	Actor[] akActors = GetAllJarls()
	ObjectReference[] akRefs = _MGF_Util.SortRefsByAnchoredDistance(Store.PlayerRef, ActorsToRefs(akActors), DropUnknowns = true)
	Return RefsToActors(akRefs)
EndFunction

Actor[] Function GetAllStewards()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor Steward = LocOpsHold.GetStewardForHold(Holds[i])
		If Steward
			Result = ArrayActorAppend(Result, Steward)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllCourtWizards()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor CourtWizard = LocOpsHold.GetCourtWizardForHold(Holds[i])
		If CourtWizard
			Result = ArrayActorAppend(Result, CourtWizard)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllHousecarls()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor Housecarl = LocOpsHold.GetHousecarlForHold(Holds[i])
		If Housecarl
			Result = ArrayActorAppend(Result, Housecarl)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllHostlers()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor Hostler = LocOpsHold.GetHostlerForHold(Holds[i])
		If Hostler
			Result = ArrayActorAppend(Result, Hostler)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllLicenceOfficers()
	Actor[] Result
	String[] Holds = Store.LocTrack.GetAllHolds()
	Int i = 0
	While i < Holds.Length
		Actor LicenceOfficer = LocOpsHold.GetLicenceOfficerForHold(Holds[i])
		If LicenceOfficer
			Result = ArrayActorAppend(Result, LicenceOfficer)
		EndIf
		i += 1
	EndWhile
	Return Result
EndFunction

; -- Flat (world) aggregation --
; Aggregates the plural POI categories across EVERY area in the game,
; not just one hold - GetAllBlacksmiths() is the union of every
; GetBlacksmithsForHold() call across GetAllHolds(). Goes straight
; through LocTrack.GetAllAreas() rather than nesting through
; GetAllHolds()/GetAreaStateNamesForHold() - the Hold grouping means
; nothing to a "give me everything" caller, so there's no reason to
; pay for the extra tier of dispatch.
;
; Deliberately NOT cached. Several _MGF_LocOpsArea_Base subclasses
; override the plural getters with alias reads that swap on actor
; death or civil war status (see that script's GetCustomRole() warning
; about per-town alias overrides) - a cache only rebuilt on
; LocTrack.ReloadJsons() would go stale exactly when those swaps
; happen, which defeats the point of the alias-dispatch system. If a
; real caller later proves this walk is too expensive to run live,
; invalidate on whatever event/state-change the alias swaps themselves
; react to - not on JSON reload, which is the wrong signal for this
; data.
;
; Scope note: GetAllAreas() is backed by LocationMap.json's
; "locationstates", which today only covers the Skyrim worldspace (9
; holds + Wilderness). Sovngarde/The Soul Cairn/Solstheim exist one
; tier up in "worldstates" but have no area-level breakdown yet, so
; there is currently no meaningfully different "every worldspace"
; variant to build - it would return identical results to these. Only
; add one if/when those worlds get their own area entries in the JSON.

Actor[] Function GetAllInnkeepers()
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetInnkeepersForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllBlacksmiths()
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetBlacksmithsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllGeneralTraders()
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetGeneralTradersForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllAlchemists()
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetAlchemistsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

Actor[] Function GetAllPriests()
	Actor[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayActorMerge(Result, LocOpsArea.GetPriestsForArea(Areas[i]))
		i += 1
	EndWhile
	Return Result
EndFunction

ObjectReference[] Function GetAllCustomRole(String RoleName)
	ObjectReference[] Result
	String[] Areas = Store.LocTrack.GetAllAreas()
	Int i = 0
	While i < Areas.Length
		Result = ArrayRefMerge(Result, LocOpsArea.GetCustomRoleForArea(Areas[i], RoleName))
		i += 1
	EndWhile
	Return Result
EndFunction

;==================================================================================================
; Properties =======================================================================================
;==================================================================================================

_MGF_LocOpsArea_PropertyStore Property Store Auto
_MGF_LocOpsArea Property LocOpsArea Auto
_MGF_LocOpsHold Property LocOpsHold Auto

 

 

Edited by Monoman1

0 Comments


Recommended Comments

There are no comments to display.

×
×
  • Create New...