Jump to content

subhuman0100

Members
  • Posts

    11
  • Joined

  • Last visited

Nexus Mods Profile

About subhuman0100

Profile Fields

  • Discord ID
    ask
  • Country
    United States
  • Favourite Game
    very mood dependent

Recent Profile Visitors

30972 profile views

subhuman0100's Achievements

Rookie

Rookie (2/14)

0

Reputation

  1. It's an old one, but someone just referred me to it, so it's apparently still coming up in searches.... So posting a clarification seems like it might end up being useful to others. Caveat: I work with Skyrim. I don't do FO4. That being said, I know why this wouldn't work in Skyrim, and see no reason in this instance to believe the two aren't behaving the same. from OP's post: FormLists contain base forms only, hence their name. The condition also is looking at base forms. The base form of an Actor is an ActorBase. i.e. when the condition checks if an actor IsInList, it's looking for that actor's actorbase- not an objectreference! However, the papyrus snippet used to add forms to the formList, cast John (an actor) as Johnny (an objectReference) before adding him. I'm not sure exactly what ends up in the FL in this instance, but it won't be an actorBase. Actor John = NPCJohn.GetReference() as Actor; this is needed for other functions in my script ObjectReference Johnny = John.GetbaseObject() as ObjectReference AliasFills.AddForm(Johnny) Utility.Wait(0.1) if AliasFills.HasForm(Johnny) Debug.Notification("Johnny added to FormList") endIf AliasFills.AddForm(John) probably would have worked, AliasFills.AddForm(John.GetBaseObject() as actorBase) definitely would have
  2. Yep, this I got. You're doing the same thing I did. I kinda touched on it in my earlier reply, when I said a multiplication or division by a power of 2 is a shift. We, all of us "shift" dozens or hundred of times in our daily lives, but we do it without thinking about what we're doing. If you have to figure out "89 * 100" probably nobody gets out a paper and pencil and calculates it. Any power of ten, for division or multiplication you just look at it and shift the digits over to get the answer. In this case, 100=10^2 so it's the base-10 version of a LeftShift 2, or shift the digits "89" two places to the left, and backfill with two zeroes to get "8900" It's the same in binary, if you're multiplying or dividing by a power of two, you just shift the digits and get the answer. So what you wrote as "n2 * 0x800" is shifting 11 bits, where I wrote it as shifting 10 bits "Math.LeftShift(n2, 10)" They're basicly the same thing. The only question comes down to: That can be RightShift(n2, 11) but the difference is I don't know how well Papyrus is optimized. If you do that in GCC or Java, the compiler bytecode output will be a shift. I don't know if Papyrus will do that, and the time difference can be considerable. An x86 does well with addition, subtraction and multiplication- but comparatively, division is very slow whereas a shift is one of the most basic functions a digital device can do. It's a single clock cycle, even the room-sized early computers from 70 years ago could do a shift, the Intel 4004 from 50 years ago only had a total instruction set of 40-some instructions, and shifts were part of it. It's one of the simplest and most basic things a computer can do. Tip: this "shifting" shortcut everyone already knows applies elsewhere, not just base-10(decimal). 0xDEADBEEF * 0x100 = DEADBEEF00 So you're doing what I did, but using different instructions. Thank you for the modulus explanation! I'm gonna chalk that up to "different people think differently" because what you called simpler, I see see as the opposite. But if it helps someone else, it's good!
  3. ReDragon2013, the tone of your post seems to be that you're disagreeing with me, however the contents of your post seem to agree with what I said: That the original thread's premise that you cannot return multiple values from a function is incorrect. You've pointed out another way it can be done. I think we're both saying the same thing: this original thread, from five years ago, that for some reason is still high up on google's search results, will mislead people if it's what they come to. I showed one way it can be done, you've shown another. I think this is a good thing, so maybe the next person searching for this answer will see what we posted instead of "it can't be done" As for this: I'd need to see some profiling data on the claim of faster. Note that I did specify Setting a variable is great, if they're on the same script. What if they're not?cross-script value returns, so there is an overhead in accessing a variable from another script, as well as added complexity. Maybe I should be clearer, I'm talking about scripts that don't included each other, or extend each other- cases where one isn't the parent or child of the other. Unrelated scripts, for example if one extended ObjectReference and the other extended Quest. For the shifts, you definitely can. Multiplying and dividing by powers of two is a shift, and that's what any decently-optimized compiler will have in its output. However I don't like to make assumptions about what may or may not be optimized in Papyrus. i.e. * 2^n becomes SHL (left shift) n bits and /2^n is SHR (right shift) n bits For me, it's easier to visualize it shifting ten bits, than it is to multiply or divide by 2^10. But you bringing that up is a good point, it may make more sense for others to see it as division and multiplication instead of a shift. But I'm not sure where you're going with the modulus. Could you explain that? Because right now, how I'm trying to figure it out, you'd need at least one of the return values before you could use modulus to get the next. Edit: RL called me AFK while I was posting, and dylbill posted in the interim. This is what I was hoping for. Answers to this question, for the next time someone stumbles upon it looking for answers. Edit 2: I'd really like the example using modulus, because as I think about it more, it seems to me that using modulus would fail catastrophically if one or more of the returned values was zero.
  4. Yep, an old post. But it came up as the first search result for me, so others are probably stumbling on it. You can. Just not natively. With a few caveats and conditions. First, this method requires SKSE. Setting a variable is great, if they're on the same script. What if they're not? If you're returning an int, that's 32 bits. Consider it 31 because we don't want to screw with the sign bit because then you can be bringing Two's Complement into play if you want to look at your returned value. But whatever you're returning probably doesn't actually utilize all those 31 readily-used bits. We're going to push three unsigned 10-bit ints into that 32-bit int, then extract them on the far side. So your function calculated your three ints for you. Let's call them int1, int2 and int3. Remember they're limited in this example to ten bits, so that means a max value of 1023 each. But we're gonna check that. int1 = Math.LogicalAnd(int1, 0x000003FF) ; ensure it\'s not more than 10 bits in length - the above mentioned check int2 = Math.LogicalAnd(int2, 0x000003FF) ; or else it will (in later steps) overflow into int3 = Math.LogicalAnd(int3, 0x000003FF) ; the next int\'s space int returnInt = Math.ShiftLeft(int1, 10) ; bits 1-10 of int1 are now in position 11-20 on returnInt returnInt = Math.LogicalOr(returnInt, int2) ; the previous line is still true, but now also bits 1-10 of int2 are in returnInt\'s bits 1-10 returnInt = Math.ShiftLeft(returnInt, 10) ; now int1 is bits 21-30 of returnInt, and int2 is bits 11-20 returnInt = Math.LogicanOr(returnInt, int3) ; our send value is done, the previous line is still true, but we\'ve now stuffed int3 into positions 1-10 return returnIntGetting it out is easier. int returnedInt = TheFunctionAbove() int newInt3 = Math.LogicalAnd(returnedInt, 0x000003FF) ; pull out ten bits returnedInt = Math.ShiftRight(returnedInt, 10) ; shift right 10 bits int newInt2 = Math.LogicalAnd(returnedInt, 0x000003FF) ; pull out next ten bits int newInt1 = Math.ShiftRight(returnedInt, 10) ; shift right ten again for yes, this is my final answer ; voilaJust note that it is LIFO, reverse order from what they went in. If you're dealing with smaller values you can stuff more in (can anyone say 31 bools?) and if larger values then you're limited to less. They don't have to be equal length. You can shoehorn an int20 and an int8 in there. Or add in a couple of bools for good measure.
  5. Perk applies spell spell applies magic effect magic effect applies perk Make all three, like that, and it should persist. Edit: to clarify, it's the same perk in the first and last steps.
  6. I would interpret it to be the minimum values. I.e. you can't assign 0.00009 to it- that would result in either an error or getting rounded to 0.0
  7. Using that method, I get Utility.RandomInt: 0.000458, PO3: 0.000023 and FetchRandom: 0.000084 in milliseconds per single iteration, that's: 16.48, 0.83, 3.02 It looks like the machine you ran that on isn't hitting 60FPS, which makes a delayed function like RandomInt even worse, your average is 32mSec per. "100 iterations in only 0.00089" looks fast until you realize that's in hours So far, RandomInt cons: glacial. Even moreso at lower framerates Pros: no dependencies PO3's GenerateRandomInt cons: more dependencies Pros: fastest FetchRandom Cons: slower than P03 Pros: less dependencies than PO3, much faster than RandomInt But what you've shown here agrees with what I said in the opening post, the problem with RandomInt is it's a delayed function.
  8. There's a %A0 at the end of that link that's causing it to break. I've looked at his Papryus Extender in the past, for other purposes. For some reason, it never came up when I spent many, MANY hours over several days of searching for faster alternatives to RandomInt. Maybe because I always searched for "papyrus" in the terms, and it's not a papyrus implementation? It actually adds multiple additional dependencies, since Papyrus Extender has dependencies itself (Address Library for SKSE plugins & VC Redistributables), but granted most people will already have (or should have) the VC Redistributables. I'll have to profile some runs with it and see what sort of times I get.
  9. Ok, let's get the rant out of the way first. I'm aware of the difference between delayed and non-delayed functions. What I don't get is why RandomInt (or RandomFloat) is the former. Why does it have to interact with the game world and get a lock on "some object"?? It shouldn't have to. The Math functions aren't delayed. I can do Math.Pow(14.7, 8.3) numerous times before RandomInt(0, 1) gives me the result of a virtual coin-flip. Ok, I'm old. I'm allowed to rant sometimes. Now that's out of the way, let's move on... So, profiling a script I'm working with and count how many times RandomInt appears in the profile log with just one iteration of the script: 972. Ok, there's a push and a pop for each time, so the number of times I asked for the RNG was half that, or 486. That's 8 seconds (rock-steady 60fps) spent waiting for results from random numbers. Unacceptable. Another solution is needed. Thought about it a while. Slept on it. Thought about it some more. Knocked around some ideas. Step 1: Create an array. And pre-populate it with random numbers. ; up top int[] Property RandomIntArray Auto ; pre-existing crap goes here ; --snip-- if (RandomIntArray.Length != 1024) Debug.Notification("Initializing Random Array") RandomIntArray = Utility.ResizeIntArray(RandomIntArray, 1024, -1) int n = 1024 while (n) n -= 1 RandomIntArray[n] = Utility.RandomInt(0, 99) endWhile Debug.Notification("Array Created.") endIfOk, let's make an array with length 1024 and stuff randomly generated values from 0-99 into it. I'm already married to SKSE, so the array length isn't an issue. With an array of 1024, it's very likely (but not guaranteed) that every possible value is in it at least once. With the "vanilla" limitation of 128 entries on an array, the same can't be said. There, it would be likely that at least one value would be missed. But it's not foolproof. It's possible, albiet unlikely, that one or more values would never appear in the array. This also takes about 17 seconds to populate (at 60FPS) but luckily it only runs once, unless you make a new character. That bit-o-code is called as part of the OnPlayerLoadGame event. Step 2: This, is a replacement for Utility.GetRandomInt Call it with no parameters, or with an offset to the seed, and it returns a value in the range of 0-99, same as GetRandomInt would. But a lot faster. More on that later. ; up top: int _seed = 42 ; because... why not? ; --snip-- int function FetchRandom(int modifier = 1) {tries to return a pre-generated random from our array, if that fails generate one} ; if there\'s something wrong with the array, GTFO if (RandomIntArray.Length != 1024) return Utility.RandomInt(0, 99) endIf ; limit check the modifier if (modifier == 0) modifier = 1 elseIf (modifier < -511) modifier = 1 elseIf (modifier > 511) modifier = 1 endIf int newseed = (_seed + modifier) while (newseed > 1023) newseed -= 1024 endWhile while (newseed < 0) newseed += 1024 endWhile int retInt = RandomIntArray[newseed] if ((retInt > -1) && (retInt < 100)) ; paranoid check to ensure our response is in bounds _seed = newseed return retInt else return Utility.RandomInt(0, 99) endIf endFunctionThere's two fallbacks, if either the array hasn't been initialized yet or if the returned value is out of bounds it goes to the slow Utility.GetRandomInt. Based on profiling timings, this isn't happening. It's working. And occurrances that only trigger with a 1% chance are occasionally happening, so it seems my array has an acceptable distribution of numbers in it. However, there's no guarantee that it would for anyone else. So far it's looking great. Profiling shows this spitting up values in 0-4mSec, much much better than GetRandomInt with it's 16-17 mSec. So it's considerably faster. But as I said, no guarantee that any *other* array would have a good distribution of values in it. So... Step 3: This is called by the OnLocationChange event. function RefreshRandoms() {when called, re-rolls 20 of the values stored in the random array} ; ensures the randomintarray get fresh values periodically. ; of course, replacing 20 at a time in an array with 1,000 ; entries takes a few iterations... if RandomIntArray.Length != 1024 debug.notification("lengthcheck failed!") return endIf int replacements = _seed while (replacements > 1023) replacements -= 1024 endWhile while (replacements < 0) replacements += 1024 endWhile int iterations = 20 ; debug.notification("replacing 20 randoms, " + replacements) while (iterations) if (replacements > 1023) replacements = 0 endIf RandomIntArray[replacements] = Utility.RandomInt(0, 99) iterations -= 1 replacements += 1 endWhile _seed = replacements ; debug.notification("randoms replaced, " + replacements) endFunction When the player changes location, 20 of the values in the array are replaced with other random values. The stored values are ever-changing, so if at any point in time the array doesn't contain at least every possible value, it will correct itself shortly as the player moves around. It might occasionally break itself, too. But it will self-correct over time. That's the beauty of randomness. The _seed variable is just the starting index to retrieve. The modifier variable that may be passed to FetchRandom is added to the _seed to determine which index to retrieve, then _seed is replaced by that index. If the calling function has some int stored in a var, I pass that along. Maybe it's GetCurrentRealTime as int, or maybe it's something negative. Just some different value to encourage jumping all over the array to grab values. _seed is initially 42, but the OnPlayerLoadGame event does "_seed += Utility.GetCurrentRealTime() as int" so it ends up being something else before it's ever utilized. tldr: seems to be working, much faster than GetRandomInt. If anyone has thoughts, critiques or wants to put it to the test I'd love feedback. Try to break it. If you do, let me know how so I can fix it.
  10. Yep, two year old post. But it caught my eye on a late-night meandering... Event OnSpellCast(Form akSpell) if akSpell == SFS string racket = "MSFS" if PlayerRef.GetActorBase().GetSex() racket = "FSFS" endIf racket += Utility.Randomint(1, 3) as string racket.Play(PlayerRef) endIf endEvent
×
×
  • Create New...