Nuascura Posted September 14, 2023 Posted September 14, 2023 (edited) What I have now is this: Spoiler Scriptname BM_LocationCheck extends ReferenceAlias bool Property isInSettlement auto location Property WhiterunLocation auto location Property MarkarthLocation auto location Property SolitudeLocation auto location Property RiftenLocation auto location Property WindhelmLocation auto location Property DawnstarLocation auto location Property FalkreathLocation auto location Property MorthalLocation auto locartion Property WinterholdLocation auto location Property DLC2RavenRockLocation auto Function checkSettlement(location akNewLoc) if (akNewLoc == none) isInSettlement = False else if (akNewLoc.HasKeyWord(LocTypeCity) || akNewLoc.HasKeyWord(LocTypeTown)) isInSettlement = True elseIf (WhiterunLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (MarkarthLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (SolitudeLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (RiftenLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (WindhelmLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (DawnstarLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (FalkreathLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (MorthalLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (WinterholdLocation.IsChild(akNewLoc)) isInSettlement = True elseIf (DLC2RavenRockLocation.IsChild(akNewLoc)) isInSettlement = True else isInSettlement = False endIf endIf EndFunction The idea is that a separate script can call checkSettlement(). I've separated the scripts so that it's just easier to read and parse atm. 1) Will this work, or is there a more efficient method? 2) Can I store locations in an array? Spoiler Event onInit() new settlement = location[10] settlement[0] = WhiterunLocation settlement[1] = MarkarthLocation settlement[2] = SolitudeLocation settlement[3] = RiftenLocation settlement[4] = WindhelmLocation settlement[5] = DawnstarLocation settlement[6] = FalkreathLocation settlement[7] = MorthalLocation settlement[8] = WinterholdLocation settlement[9] = DLC2RavenRockLocation endEvent Function checkSettlement(location akNewLoc) int numCheck = settlement.Length while numCheck if (settlement[numCheck].IsChild(akNewLoc)) isInSettlement = True else isInSettlement = false endIf numCheck -= 1 endWhile endFunction Alternatively, can we also do this: Spoiler Event onInit() new settlement = location[10] settlement[0] = WhiterunLocation settlement[1] = MarkarthLocation settlement[2] = SolitudeLocation settlement[3] = RiftenLocation settlement[4] = WindhelmLocation settlement[5] = DawnstarLocation settlement[6] = FalkreathLocation settlement[7] = MorthalLocation settlement[8] = WinterholdLocation settlement[9] = DLC2RavenRockLocation endEvent Function checkSettlement(location akNewLoc) int numCheck = settlement.Length while numCheck if playerRef.IsInLocation(settlement[numCheck]) Debug.Trace("Player is in" + settlement[numCheck]) endIf numCheck -= 1 endWhile endFunction Edited September 14, 2023 by Gyra
Taki17 Posted September 14, 2023 Posted September 14, 2023 (edited) Provided that the separate script that's supposed to use it is also part of your mod, the simplest might be storing locations in a form list. That way, you can add or remove them without needing to edit the script with new properties, or needing to even add and fill out that long list of location properties in the first place. And the body of your function can be reduced to this: FormList Property Locations Auto Function checkSettlement( location akNewLoc ) isInSettlement = Locations.HasForm( akNewLoc ) EndFunction Edited September 14, 2023 by Taki17
Nuascura Posted September 15, 2023 Author Posted September 15, 2023 (edited) So I added the main city and town names via CK to a formlist, one for cities and one for towns. However, the script refuses to read the formlist. I thought it was the akNewLoc location variable I was providing that was problematic. But if I switched it out to an included Location, the code still can’t read the formlist. I’ve merged the scripts into one script for easier debugging. Calling the function results in a “Can’t use HasForm() on a None object”. Ex. Location Property WhiterunLocation Auto FormList Property BM_Cities Auto … onLocationChange(location akOldLoc, location akNewLoc) isInCity = BM_Cities.HasForm(WhiterunLocation) Any ideas? ChatGPT ain’t helping, maybe cuz I’m not asking the right questions. Edited September 15, 2023 by Gyra
Taki17 Posted September 15, 2023 Posted September 15, 2023 46 minutes ago, Gyra said: Any ideas? ChatGPT ain’t helping, maybe cuz I’m not asking the right questions. That is unlikely to help you since it's been made profoundly retarded with each successive update. Also because what you have is most likely ain't a coding issue. Have you made sure to fill out the property on the script with the form list you want?
Nuascura Posted September 15, 2023 Author Posted September 15, 2023 (edited) 19 minutes ago, Taki17 said: That is unlikely to help you since it's been made profoundly retarded with each successive update. Also because what you have is most likely ain't a coding issue. Have you made sure to fill out the property on the script with the form list you want? Yeah, pretty sure. Feel free to take a look at the script if you'd like. I've been banging my head against a wall for half-a-day on this. BM_Player.psc Unless I'm misunderstanding you and missed a step. Sorry, i'm new to all this. Edited September 15, 2023 by Gyra
Taki17 Posted September 15, 2023 Posted September 15, 2023 (edited) 1 hour ago, Gyra said: Unless I'm misunderstanding you and missed a step. I meant assigning the actual form list you have created in xedit or the Creation kit to the script properties to fill the cites and towns form lists declared in the script. Example is my own mod, however you need to double click the script in your quest to bring up the properties window and fill out the values. Spoiler I have a strong suspicion that you are getting a None object because there is nothing behind the property. Edited September 15, 2023 by Taki17
Nuascura Posted September 15, 2023 Author Posted September 15, 2023 (edited) 2 hours ago, Taki17 said: I have a strong suspicion that you are getting a None object because there is nothing behind the property. My instinct is that you're right, but is there a way to do this outside CK? I'd assumed writing "FormList Property BM_Cities Auto" was enough to assign the formlist to script properties. I've been staring at a small-ass Steam Deck screen for the past weeks, but that's besides the point. I'm getting a bunch of errors when using CK to read or compile scripts whereas an external third-party PCA has no issue compiling my scripts provided I have the correct source files. Hell, CK tells me the main script is missing a variable found in another script, created by and related to the mod, that CK does see. My guess is that my CK environment isn't setup correctly, so I'd like to avoid it since I've already used CK to create my BM_Cities and BM_Towns formlists and populated them with Locations. I opened that window in your screenshot with CK, but with the mod that I'm editing (BM Licenses), that properties window is completely blank even though there are declared properties in the script that function 100% fine. Edited September 15, 2023 by Gyra
Taki17 Posted September 15, 2023 Posted September 15, 2023 12 minutes ago, Gyra said: I'd assumed writing "FormList Property BM_Cities Auto" was enough to assign the formlist to script properties. You'd be right if these were Fallout 3/New Vegas scripts. Alas, they ain't. Think of properties in Skyrim as a sort of handle that's setup to contain a formlist, but this handle can refer to any formlist you assign it to in the script properties. 15 minutes ago, Gyra said: is there a way to do this outside CK? I guess you could xedit, however it displays data in a bit of a raw-er format. Same example but in xedit: Spoiler Though if I were you, I'd look into properly setting up the Creation Kit. Even though xedit is very useful for things like small specific adjustments or batch processing file records, the visual approach of the Creation Kit is sometimes more informative, especially if you are dealing with an aspect that is new to you and don't know what the records do.
Nuascura Posted September 16, 2023 Author Posted September 16, 2023 (edited) Ok so I spent another half-a-day moving all source scripts to .\Data\Source\Scripts from .\Data\Scripts\Source (if seen through MO2) and finding that I'm missing FNIS Sexy Move files (which weren't necessary when compiling through PCA). I enabled the setting to allow CK to load multiple masters, and I can't remember what else I did. So FML and fuck Bethesda for using two different script source directories. CK works, compiles my script, and I can add the formlist as a script property. Edit: oh but now CK removed my patch's masters. Edit 2: adding the formlists to the main plugin, which only has Skyrim.esm as the master, allows it to retain its previous records. Seems using CK to edit a .esp that has another .esp as a master removes the latter as master. Edited September 16, 2023 by Gyra
Nuascura Posted September 16, 2023 Author Posted September 16, 2023 @Taki17 With your help, I've managed to make the above BM_Player track whether player is in town or city, after some script modifications, and to some extent. The problem now is that location IDs such as WhiterunLocation are both too broad and do not cover interior locations (unlike if one were to use the isInLocation() function). For example, with BM_Cities formlist of locations including WhiterunLocation, isInCity == true when Player is in Plains or Clouds district and when Player is wandering around the stables. isInCity == false when player in inside The Bannered Mare. Another example: isInTown == true when Player is in Riverwood exterior. isInTown == false when Player is in Alvor's house. What I want: Whiterun city outskirts shouldn't be counted as part of the city since Whiterun has walls, but Falkreach city outskirts should be counted as such. One option I've thought of is to add all possible settlement locations into the formlists, separated into town and city as like it is currently, but filter certain areas with an IF statement. I don't want areas outside walls to count as part of the city, which means including WhiterunLocation, RiftenLocation, SolitudeLocation, and MarkarthLocation (else city streets won't count as city) and creating a condition for when in WhiterunStables etc. Is a large formlist bad practice? The number of forms will exceed 128. The other option is to remain with WhiterunLocation, FalkreathLocation, RiverwoodLocation etc. the parent forms, and make a loop to check each form for isInLocation (to check child forms). Function checkIsInCity(location currLoc) int numToCheck = licenses.BM_Cities.GetSize() while(numToCheck) if licenses.playerRef.GetActorRef().GetAt(numToCheck) Debug.Notification("Player is in a city.") endIf endWhile endFunction What would you say is better?
Taki17 Posted September 16, 2023 Posted September 16, 2023 (edited) 6 hours ago, Gyra said: Function checkIsInCity(location currLoc) int numToCheck = licenses.BM_Cities.GetSize() while(numToCheck) if licenses.playerRef.GetActorRef().GetAt(numToCheck) Debug.Notification("Player is in a city.") endIf endWhile endFunction Just so that you are aware, this will result in an infinite loop. You need the loop iterator either increased or decreased with each run of the loop, and also specify a proper exit condition that is a comparison with the loop iterator value - which should not exceed the array/list size. Example: while( i < licenses.BM_Cities.Getsize() ) Furthermore, since arrays and form lists start with the 0 index, the actual first index you need to check will be size-1. Example: A form list has 10 elements, getting its size will return 10. However, the last item is stored at the 9th index. The GetAt call on the player will not compile, since that's not getting the player's location, but is used on form lists to fetch an element at a specific index. 6 hours ago, Gyra said: The problem now is that location IDs such as WhiterunLocation are both too broad and do not cover interior locations I believe you can leverage the parent locations for these city house interiors. Every house and such in Whiterun should has WhiterunLocation as their parent location. For that, there is the IsSameLocation function present in the game, which is aware of parent locations on location forms. It looks something like this: Bool Function checkIsInCity( Location currLoc ) ;setup the loop iterator int i = 0 ;loop will go on either until a match is found or we ran out of locations to test While( i < licenses.BM_Cities.GetSize() ) ;assuming currLoc holds the player's current location and BM_Cities holds the city locations: ;the LocTypeCity keyword is present on WhiterunLocation ;WhiterunLocation is a parent to all locations within Whiterun, like WhiterunBanneredMareLocation ;using GetKeyword spares you a keyword property declaration if you type in the exact name of the keyword you want If currLoc.IsSameLocation( licenses.BM_Cities.GetAt(i), Keyword.GetKeyword( "LocTypeCity" ) ) Debug.Notification( "Player is in a city." ) Return True EndIf i += 1 EndWhile ;no match found, player is not in a city, return false Return False EndFunction 9 hours ago, Gyra said: adding the formlists to the main plugin, which only has Skyrim.esm as the master, allows it to retain its previous records. Seems using CK to edit a .esp that has another .esp as a master removes the latter as master. The Creation Kit does not allow esp masters. For that to work, you'll need to temporarily add the ESM flag to your plugins in xedit, and remove it once you are finished working on the mod that has them as masters. Alternatively, move your records around in xedit, since it will allow loading esp masters. Edited September 16, 2023 by Taki17
Nuascura Posted September 17, 2023 Author Posted September 17, 2023 18 hours ago, Taki17 said: The GetAt call on the player will not compile, since that's not getting the player's location, but is used on form lists to fetch an element at a specific index. Oops, I must've been dead tired when I sent that code snippet since I didn't even include the IsInLocation() function, which I also totally misinterpreted. You've pretty much done half of my work for me, but I've learnt a lot, so thank you so much. licenses.BM_Cities.GetAt(i) cannot be detected as type Location, so I simply had to add "as location" to prevent parameter type mismatch. Bool Function checkIsInCity(location currLoc) int i = 0 While(i < licenses.BM_Cities.GetSize()) If currLoc.IsSameLocation(licenses.BM_Cities.GetAt(i) as location, Keyword.GetKeyword("LocTypeCity")) Debug.Notification("Player is in a city.") Return True EndIf i += 1 EndWhile Debug.Notification("Player is NOT in a city.") Return False EndFunction I've also realized that the Stables Locations aren't enough to blacklist walled-city outskirts, based on the cell render preview inside CK. I'll just rationalize the governance of licenses in walled-city outskirts as making sense given that some residents don't live within the city walls. I'll look around for filters or alternatives when I have more time to waste.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now