Jump to content

The critter thread


Sclerocephalus

Recommended Posts

I'm not sure about the most recent one last week, but these were attempts to fix: 14853, 14820, 14814, 14813

 

But mostly we need BlackPete to confirm....

14813 -- May or may not be fixed, I haven't recieved any more cannot call "IsDisabled" or "IsDeleted", but I'm still receiving some other "cannot call" errors on critters since updating to USKP 2.0.3.

14814 -- I'm pretty sure this is fixed, I haven't seen any "cannot call" errors on X(), Y(), Z() as of USKP 2.0.3. In fact, I haven't received any criiterFish errors so far since updating.

14820 -- I'm pretty sure this is fixed as well, I haven't seen any "cannot call" errors on X(), Y(), Z() as of USKP 2.0.3.

14853 -- May not be fixed, as noted in previous posts, I'm still getting "no 3D" errors on critter.psc and critterMoth.psc occasionally. [posts #165 and #175]

Link to comment
Share on other sites

I'm trying to think of faster versions of critter code, having already put Sclero's bird fix in 2.0.3. There are 3 loops, all of which look something like this:

 

; First find a random point within the radius
float flength = fLeashLength * 2.0 ; So that we loop at least once
fTargetX = 0.0
fTargetY = 0.0
; pick a radom point in the disk of fleashlength radius

int max = 10
while (flength > fLeashLength) && (0 < max)
    fTargetX = RandomFloat(-fLeashLength, fLeashLength)
    fTargetY = RandomFloat(-fLeashLength, fLeashLength)
    flength = Math.sqrt(fTargetX * fTargetX + fTargetY * fTargetY)
    max -= 1
endWhile
if max <= 0; [USKP 2.0.1]
    return false
endif
fTargetX += fSpawnerX
fTargetY += fSpawnerY

; Pick random target angle
fTargetAngleZ = RandomFloat(-180.0, 180.0)
 

This is dumb, it's looping to find 2 variables with a combined distance less than fLeashLength from [0.0, 0.0], the spawner.

 

This code is what makes fish look like they are flipping back and forth without moving much. And dragonflies. There's got to be a better design. Any ideas?

Link to comment
Share on other sites

Naively, this should do the trick?

fTargetAngleZ = RandomFloat(-180.0, 180.0)
fTargetAngleX = 0.0

fTargetX = fSpawnerX + RandomFloat(0.0, fLeashLength) * Cos(fTargetAngleZ)
fTargetY = fSpawnerY + RandomFloat(0.0, fLeashLength) * Sin(fTargetAngleZ)
Anybody see anything wrong with this?

 

fTargetAngleZ = RandomFloat(-180.0, 180.0)
fTargetAngleX = 0.0

fTargetX = fSpawnerX + RandomFloat(-fLeashLength, fLeashLength) * Cos(fTargetAngleZ)
fTargetY = fSpawnerY + RandomFloat(-fLeashLength, fLeashLength) * Sin(fTargetAngleZ)
Seems to work, too, but the dragonfly sometimes turns around after travel. OK.

fTargetAngleZ = RandomFloat(-180.0, 180.0)
fTargetAngleX = 0.0

Float travelAngleZ = RandomFloat(-180.0, 180.0)
fTargetX = fSpawnerX + RandomFloat(0.0, fLeashLength) * Cos(travelAngleZ )
fTargetY = fSpawnerY + RandomFloat(0.0, fLeashLength) * Sin(travelAngleZ )
Does the job without the final angle always being in the direction or counter direction traveled.

Hopefully this is much faster than the loop.

Link to comment
Share on other sites

i seem to remember that i discussed this with scleorcephalus a while back but can not find it.

mind you i'm no it expert, but i do understand the very basic math behind these formulas.

 

so here goes: the first function may look stupid and does not produce good results. hence the 10fold loop.

 

the mathematically correct solution however uses two sin functions (sin and cos, basically the same).

 

while the latter finds a solution with one run of the function, the question is:

what is faster?

10 sqrts or 2 sin? (of course this doesn't factor in the other variables/operations within the loop)

 

 

also: is this the function used in vanilla? wasn't that even more stupid simple and fast, but had even more ugly to look at loops?

 

edit: found it

http://www.afkmods.com/index.php?/topic/3759-god-dammit-bethesda-or-why-the-lakeview-manor-weapon-rack-cant-be-fixed/page-2

Link to comment
Share on other sites

the mathematically correct solution however uses two sin functions (sin and cos, basically the same).

 

while the latter finds a solution with one run of the function, the question is:

what is faster?

10 sqrts or 2 sin? (of course this doesn't factor in the other variables/operations within the loop)

Thanks! Had forgotten that Sclero had forked off into another thread.

In that thread, you pointed at a jsPerf test page. But the setup there had Math.atan2(dy, dx), which we don't need, as we only need a random radius. Without the atan2 (test case 2), the cos&sin is 4% faster than sqrt. I added test case 3, with Math.Random(), but that's half as fast.

 

However, the old bad code uses more randoms than mine. So we should be good to go.

 

also: is this the function used in vanilla? wasn't that even more stupid simple and fast, but had even more ugly to look at loops?

The old code posted above was already simplified, as I'd removed constant expressions out of the loop and loop exit speedup in USKP 2.0.1.

Combining Sclero with mine:

Float fLength = RandomFloat(0.0, fLeashLength)
Float fAngle = RandomFloat(-180.0, 180.0)
fTargetX = fSpawnerX + fLength * Math.Cos(fAngle)
fTargetY = fSpawnerY + fLength * Math.Sin(fAngle)

; Now pick a random Z
fTargetZ = fSpawnerZ + RandomFloat(0.0, fHeight)

; Pick random target angle
fTargetAngleZ = RandomFloat(-180.0, 180.0)
fTargetAngleX = 0.0
The main difference is I've already calculated and saved the spawner X Y Z -- a minor difference is the dragonfly doesn't always end moving and start hovering facing away from the spawner, which looked a little odd.
Link to comment
Share on other sites

Good catch with the facing direction.

The formula look correct, and since you did test the speed i have no objections.

Link to comment
Share on other sites

Good catch with the facing direction.

The formula look correct, and since you did test the speed i have no objections.

A slight tweak, watching both dragonflies and fish now, allowing them to turn at the end up to 180 degrees was too much, narrowing to 90 degrees from the path they just took looks better:
Float fLength = RandomFloat(0.0, fLeashLength)
fTargetAngleZ = RandomFloat(-180.0, 180.0)
fTargetX = fSpawnerX + fLength * Math.Cos(fTargetAngleZ)
fTargetY = fSpawnerY + fLength * Math.Sin(fTargetAngleZ)

; Now pick a random Z
fTargetZ = fSpawnerZ + RandomFloat(0.0, fHeight)

; Pick random target angle
fTargetAngleZ += RandomFloat(-90.0, 90.0)
fTargetAngleX = 0.0
Fish without schooling looks fairly smooth, less twitchy; on to schooling code....
Link to comment
Share on other sites

Schooling with much fewer instructions:

bool Function PickRandomPointBehindTargetFish()

	if TargetFish && !TargetFish.IsDisabled() && CheckFor3D(TargetFish)
		; moved unchanged variables outside loop [USKP 2.0.1]
		float ftargetFishX = targetFish.X
		float ftargetFishY = targetFish.Y
		fTargetZ = targetFish.Z; default same as leader [USKP 2.0.4]
		fTargetAngleZ = targetFish.GetAngleZ()
;/// [USKP 2.0.4]
		float ftargetFishHeadingCos = Math.cos(fTargetAngleZ)
		float ftargetFishHeadingSin = Math.sin(fTargetAngleZ)
		float flength = fLeashLength * 2.0
		int max = 10   ; how many times before we give up?

		while ((flength > fLeashLength) && (0 < max))
			; Pick random point around fish, accounting for depth
			float flocalDeltaX = RandomFloat(-fSchoolingDistanceX, fSchoolingDistanceX)
			float flocaldeltaY = RandomFloat(-fSchoolingDistanceY, 0.0) ; To be behind the other fish
			
			; Now turn that into absolute X Y offset
			float fdeltaX = ftargetFishHeadingCos * flocalDeltaX + ftargetFishHeadingSin * flocalDeltaY
			float fdeltaY = ftargetFishHeadingCos * flocalDeltaY - ftargetFishHeadingSin * flocalDeltaX
			
			fTargetX = ftargetFishX + fdeltaX - fSpawnerX
			fTargetY = ftargetFishY + fdeltaY - fSpawnerY
			flength = Math.sqrt(fTargetX * fTargetX + fTargetY * fTargetY)
			
			; decrement our recursion counter [USKP 2.0.1]
			max -= 1
		endWhile
		if max <= 0; [USKP 2.0.1]
			return false
		endif
		fTargetX += fSpawnerX
		fTargetY += fSpawnerY
///;
		; vanilla formula makes no sense, does not result in fish following behind leader.
		; reuse fSchoolingDistance[X|Y] as min and max distance from leader.
		float fDistance = RandomFloat(fSchoolingDistanceX, fSchoolingDistanceY)
		fTargetX = ftargetFishX + fDistance * Math.cos(fTargetAngleZ)
		fTargetY = ftargetFishY + fDistance * Math.sin(fTargetAngleZ)
		float flength = Math.sqrt(fTargetX * fTargetX + fTargetY * fTargetY)

		; Now pick a random Z
		; If flength == fleashLength, then it must be 0
		; If length == 0, then it can be -fWaterDepth
		if flength < fLeashLength; [USKP 2.0.4]
			fTargetZ = fSpawnerZ - RandomFloat(fMinDepth, (fDepth - ((flength * fDepth) / fLeashLength)))
		endIf
		fTargetAngleX = 0.0
		return (PlayerRef != None); check again after lengthy calculations [USKP 2.0.1]
	else
; 		debug.trace("Tried to run PickRandomPointBehindTargetFish, but no valid TargetFish.  "+self)
		return false
	endif
	
endFunction
Needs work (mostly in other parts of code). Currently, in the fairly rare circumstance that a leader is found, the fish sidles slowly in that direction. But after getting near the leader, hurries in all its movements (fFleeTranslationSpeed) until it 5% chance stops following. Since the leader is going at a random slow speed, the fast follower looks spastic.

Oh well, are there any comments about this code?

Link to comment
Share on other sites

Comments? Well i guess i was able to deceiver what the code is supposed to do, but im way to inexperienced to recognize any synthax errors.

I suppose the variables used for the calculation of the z coordinate are defined in earlier sections of the code?

And again the same nonsense applies: calculate rectangles then check distance to see if within circle section. Loop intil success.

It makes me uneasy. Either the programmer sucked at math horribly, or he knew something about issues with the sin/cos function calculation inside the creation engine we do not know. I hope for the first.

Also, why not just accept a rectangular solution? I guess nobody would see the difference between critters spawning within a rectangle or a circle. We shouldnt change that now, might lead to clipping. But from the original programmers pov? Maybe he didnt want to admit to having skipped math in school?

Also the code posted just defines the target position, not the speed to get there, right? The problem would be a separate issue latet in the code?

What about (random guess) slowing the fish down once its inside the max schooling rectangle behind the boss-fish?

Link to comment
Share on other sites

I spoke too soon about the 'critterFish' errors being fixed.  I received this error in the pond below Left Hand Mine;

[03/07/2014 - 10:21:09PM] error: Cannot call bMoving() on a None object, aborting function call
stack:
    [ (FF001AEC)].critterFish.OnUpdate() - "CritterFish.psc" Line 132
[03/07/2014 - 01:21:09AM] warning: Assigning None to a non-object variable named "::temp19"
stack:
    [ (FF001AEC)].critterFish.OnUpdate() - "CritterFish.psc" Line 132

Another CritterMoth error. This one was on the south side of Lake Ilinalta (to the north of North Shriekwind Bastion):

[03/07/2014 - 10:56:20PM] error:  (FF0019C4): does not have any 3d and so cannot be moved to.
stack:
    [ (FF001A8F)].ObjectReference.MoveToNode() - "<native>" Line ?
    [ (FF001817)].critterMoth.PlaceDummyMarker() - "Critter.psc" Line 516
    [ (FF001817)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 770
    [ (FF001817)].critterMoth.BellShapeTranslateToRefNodeAtSpeedAndGotoState() - "Critter.psc" Line 846
    [ (FF001817)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 283
    [ (FF001817)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130

Also, I was wrong about the X(), Y(), and Z() "none" errors being fixed as well. The errors in the spoiler below were generated from butterflies to the southeast of the Riften Stables;

[03/08/2014 - 12:01:38AM] error: Cannot call Z() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 783
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 783
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call X() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 784
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 784
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call Y() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 785
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 785
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call Z() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 786
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 786
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call GetAngleX() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 787
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 787
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call GetAngleY() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 788
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 788
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] error: Cannot call GetAngleZ() on a None object, aborting function call
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 789
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130
[03/08/2014 - 12:01:38AM] warning: Assigning None to a non-object variable named "::temp53"
stack:
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 789
[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833
[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291
[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130

Link to comment
Share on other sites

Maybe there comes a point when we just have to accept that some errors are going to occur no matter what we do? The critter spam is next to non-existent when compared to what it was, so I'd consider it a job well done. I think it's becoming apparent that despite best efforts, the critters can loose 3D at any point. Why keep fighting it when it's rather rare?

 

I like the improved critter movement code :)

Link to comment
Share on other sites

Comments? Well i guess i was able to deceiver what the code is supposed to do, but im way to inexperienced to recognize any synthax errors.

No worries, there are no syntax errors -- I always compile and test before posting code here. I was looking for general feedback, such as your next comments:

 

It makes me uneasy. Either the programmer sucked at math horribly, or he knew something about issues with the sin/cos function calculation inside the creation engine we do not know. I hope for the first.

Since this is done consistently, my guess is s/he did know something we don't, and the loop was a work around. I doubt anybody managed to get hired with no high school geometry.

But we also know something they didn't, based on 2 years of testing: the length of the calculations means the engine keeps dropping the schedule, and after continuation other processes have deleted the references, causing an error. In turn, errors spamming the log slows down the engine, which causes more errors.

Tightening the code should reduce the context switches and scheduling delays, more than making up for any slowness of the math functions.

 

Also, why not just accept a rectangular solution? I guess nobody would see the difference between critters spawning within a rectangle or a circle. We shouldnt change that now, might lead to clipping.

Agreed. The code assumes a circular arrangement. No pools in the game are actually circular, but rectangular are quite rare.

 

Also the code posted just defines the target position, not the speed to get there, right? The problem would be a separate issue latet in the code?

What about (random guess) slowing the fish down once its inside the max schooling rectangle behind the boss-fish?

Yes, that was a one line fix elsewhere in the code. I've changed it to be the defined mean speed. The leader fish will go faster or slower than the mean, but the vanilla design with the follower always going faster than the leader isn't working. That was clearly an error.

 

I spoke too soon about the 'critterFish' errors being fixed.  I received this error in the pond below Left Hand Mine;

[03/07/2014 - 10:21:09PM] error: Cannot call bMoving() on a None object, aborting function call
stack:
    [ (FF001AEC)].critterFish.OnUpdate() - "CritterFish.psc" Line 132
[03/07/2014 - 01:21:09AM] warning: Assigning None to a non-object variable named "::temp19"
stack:
    [ (FF001AEC)].critterFish.OnUpdate() - "CritterFish.psc" Line 132

I'm hoping speeding up the fish code loops as described will help fix this. That code looks like:

if (TargetFish as CritterFish) && TargetFish.bMoving

Again, there's already a test against None (TargetFish as CritterFish), and the engine is blocking and coming back with None on the dot of "TargetFish.bMoving" -- it's just random timing giving us this error.

 

Another CritterMoth error. This one was on the south side of Lake Ilinalta (to the north of North Shriekwind Bastion):

[03/07/2014 - 10:56:20PM] error:  (FF0019C4): does not have any 3d and so cannot be moved to.
stack:
    [ (FF001A8F)].ObjectReference.MoveToNode() - "<native>" Line ?
    [ (FF001817)].critterMoth.PlaceDummyMarker() - "Critter.psc" Line 516
    [ (FF001817)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 770
    [ (FF001817)].critterMoth.BellShapeTranslateToRefNodeAtSpeedAndGotoState() - "Critter.psc" Line 846
    [ (FF001817)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 283
    [ (FF001817)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130

Another GoToNewPlant BellShape calculation.

 

Also, I was wrong about the X(), Y(), and Z() "none" errors being fixed as well. The errors in the spoiler below were generated from butterflies to the southeast of the Riften Stables;

03/08/2014 - 12:01:38AM] error: Cannot call Z() on a None object, aborting function call

stack:

[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line 783

[ (FF002D2F)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line 833

[ (FF002D2F)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line 291

[ (FF002D2F)].critterMoth.OnUpdate() - "CritterMoth.psc" Line 130

...

Another GoToNewPlant BellShape calculation.

As I mentioned earlier, the SplineTranslateTo code seems to be working, but there's a hole somewhere in the BellShapeTranslate code. I'm hoping the new code catches these.

 

Maybe there comes a point when we just have to accept that some errors are going to occur no matter what we do? The critter spam is next to non-existent when compared to what it was, so I'd consider it a job well done. I think it's becoming apparent that despite best efforts, the critters can loose 3D at any point. Why keep fighting it when it's rather rare?

 

I like the improved critter movement code :)

It's not the critters losing 3D now, it's the plants that the critters are traveling toward. There's not much we can do, as they could be in adjacent cells that happen to unload during calculations.

If there's general agreement on the movement code, I'll package it up later today for testing. It tests OK here (no errors). But BlackPete seems to be able to find errors better than anybody.

Link to comment
Share on other sites

Oh well, are there any comments about this code?

This version fixes 2 more bugs. Vanilla could generate positive Depth for fish, because the formula didn't subtract MinDepth in the fractional part, and RandomFloat definition requires min < max. This may be the fish out of water bug mentioned earlier.

bool Function PickRandomPointBehindTargetFish()

    if TargetFish && !TargetFish.IsDisabled() && CheckFor3D(TargetFish)
        ; moved unchanged variables outside loop [USKP 2.0.1]
        float ftargetFishX = targetFish.X - fSpawnerX
        float ftargetFishY = targetFish.Y - fSpawnerY
        fTargetZ = targetFish.Z; default same as leader [USKP 2.0.4]
        fTargetAngleZ = targetFish.GetAngleZ()
;/// [USKP 2.0.4] ***
        float ftargetFishHeadingCos = Math.cos(fTargetAngleZ)
        float ftargetFishHeadingSin = Math.sin(fTargetAngleZ)
        float flength = fLeashLength * 2.0
        int max = 10   ; how many times before we give up?

        while ((flength > fLeashLength) && (0 < max))
            ; Pick random point around fish, accounting for depth
            float flocalDeltaX = RandomFloat(-fSchoolingDistanceX, fSchoolingDistanceX)
            float flocaldeltaY = RandomFloat(-fSchoolingDistanceY, 0.0) ; To be behind the other fish
            
            ; Now turn that into absolute X Y offset
            float fdeltaX = ftargetFishHeadingCos * flocalDeltaX + ftargetFishHeadingSin * flocalDeltaY
            float fdeltaY = ftargetFishHeadingCos * flocalDeltaY - ftargetFishHeadingSin * flocalDeltaX
            
            fTargetX = ftargetFishX + fdeltaX
            fTargetY = ftargetFishY + fdeltaY
            flength = Math.sqrt(fTargetX * fTargetX + fTargetY * fTargetY)
            
            ; decrement our recursion counter [USKP 2.0.1]
            max -= 1
        endWhile
        if max <= 0; [USKP 2.0.1]
            return false
        endif
///;
        ; vanilla formula dithers X and Y directly. Reduce calculations by
        ; dithering angle vector instead.
        ; reuse fSchoolingDistance[X|Y] as min and max distance from leader.
        float fDistance = RandomFloat(fSchoolingDistanceX, fSchoolingDistanceY)
        float fDeltaAngle = fTargetAngleZ + RandomFloat(-5.0, 5.0)
        fTargetX = ftargetFishX - fDistance * Math.cos(fDeltaAngle)
        fTargetY = ftargetFishY - fDistance * Math.sin(fDeltaAngle)
        ; variables above are 0,0 spawner relative
        float flength = Math.sqrt(fTargetX * fTargetX + fTargetY * fTargetY)
        fTargetX += fSpawnerX
        fTargetY += fSpawnerY
;*** [USKP 2.0.4] ***

        ; Now pick a random Z
        ; If flength == fleashLength, then it must be 0
        ; If length == 0, then it can be -fMinDepth to -fDepth, fixed [USKP 2.0.4]
        if flength < fLeashLength && fMinDepth < fDepth
            fTargetZ = fSpawnerZ - RandomFloat(fMinDepth, (fDepth - ((flength * (fDepth - fMinDepth)) / fLeashLength)))
        endIf
        
;!        fTargetAngleZ = targetFish.GetAngleZ()
        fTargetAngleX = 0.0
        return (PlayerRef != None); check again after lengthy calculations [USKP 2.0.1]
    else
;         debug.trace("Tried to run PickRandomPointBehindTargetFish, but no valid TargetFish.  "+self)
        return false
    endif
    
endFunction
Note the old code is commented out, and the new code surrounded by USKP, for easy comparison.

 

As to schooling speed, I've given up and replaced existing bMoving with fMoving, and store the speed there so the follower can match speed. I've now watched two fish circle each other (have each other as leader) without the spasmic twitching in vanilla.

;!			if ((RandomInt(0, 100) < iPercentChanceStopSchooling) || TargetFish == none || TargetFish.IsDisabled() || !TargetFish.bMoving)
			if (closestActor != none); [USKP 2.0.4]
; 				;Debug.Trace(self + " Oh noes! there is an Actor " + closestActor + " nearby, Flee")
				GotoState("RandomSwimming")
				TargetClear()
				GoToNewPoint(fFleeTranslationSpeed)
			elseif (RandomInt(0, 100) >= iPercentChanceStopSchooling) && (TargetFish as CritterFish) && TargetFish.fMoving && PickRandomPointBehindTargetFish()
				; If the target fish is moving, follow it
;!				SchoolWithOtherFish(fFleeTranslationSpeed)
				SchoolWithOtherFish(TargetFish.fMoving); reduce twitching [USKP 2.0.4]
			else
				GotoState("RandomSwimming")
				TargetClear()
				GoToNewPoint(RandomFloat(fTranslationSpeedMean - fTranslationSpeedVariance, fTranslationSpeedMean + fTranslationSpeedVariance))
			endIf
As you can see, vanilla code has SchoolWithOtherFish(fFleeTranslationSpeed) -- this caused the follower fish to rush up to the leader, over and over, as the leader was going the Mean+-Variance speed. It looked like a calmly cruising fish with a twitchy fish darting up and down and left and right next to it. Not amusing, especially as the follower can be much bigger than the leader.

Now, it follows the same logic as random swimming: too close to player, runs away; follow at same speed as leader; or go its own way at Mean+-Variance. Everything is nice and smooth.

Link to comment
Share on other sites

I spoke too soon about the 'critterFish' errors being fixed.

Here's a test script-only (empty .esp) .bsa build. This is designed to be added to your current game without problems. Please see whether it eliminates any more errors?

 

[old code removed]

Link to comment
Share on other sites

Near the Windhelm Docks, was killing salmon with an ice storm spell in order to harvest salmon roe;

[03/09/2014 - 12:47:56AM] error: Cannot call Y() on a None object, aborting function call
stack:
    [ (FF001902)].critterFish.PickRandomPointBehindTargetFish() - "CritterFish.psc" Line 227
    [ (FF001902)].critterFish.OnUpdate() - "CritterFish.psc" Line 132
[03/09/2014 - 12:47:56AM] warning: Assigning None to a non-object variable named "::temp42"
stack:
    [ (FF001902)].critterFish.PickRandomPointBehindTargetFish() - "CritterFish.psc" Line 227
    [ (FF001902)].critterFish.OnUpdate() - "CritterFish.psc" Line 132
[03/09/2014 - 12:47:56AM] error: Cannot call GetAngleZ() on a None object, aborting function call
stack:
    [ (FF001902)].critterFish.PickRandomPointBehindTargetFish() - "CritterFish.psc" Line 228
    [ (FF001902)].critterFish.OnUpdate() - "CritterFish.psc" Line 132
[03/09/2014 - 12:47:56AM] warning: Assigning None to a non-object variable named "::temp42"
stack:
    [ (FF001902)].critterFish.PickRandomPointBehindTargetFish() - "CritterFish.psc" Line 228
    [ (FF001902)].critterFish.OnUpdate() - "CritterFish.psc" Line 132

Here's a test script-only (empty .esp) .bsa build. This is designed to be added to your current game without problems. Please see whether it eliminates any more errors?

I'm currently still testing this and will see what happens. The above errors with the CritterFish occurred prior to putting your crittertest 8.7 files into my data folder, so I don't know yet if it fixes them.

Link to comment
Share on other sites

Near the Windhelm Docks, was killing salmon with an ice storm spell in order to harvest salmon roe;

I'm currently still testing this and will see what happens. The above errors with the CritterFish occurred prior to putting your crittertest 8.7 files into my data folder, so I don't know yet if it fixes them.

That is following a leader and both the leader and follower are killed in the middle of its calculations. I'd never considered the case where a mass attack could hit more than 1 fish at exactly the same time -- and obviously the developers didn't either! (I've used a bow, but never an ice storm or chain lightening.) However, it's a simple fix -- yet another short wait loop.

 

For testing, you'd have to make sure there is a leader. It doesn't happen very often. My guess is the version 8 code will mostly handle things better anyway. But here's the fixed version 9 code.

 

[old code removed]

Link to comment
Share on other sites

At the Raven Rock docks (this happened when using the critter test 9 code);

[03/11/2014 - 10:40:44PM] error: Failed to setup moving reference because it has no parent cell or no 3D
stack:
    [ (FF002D69)].critterdragonfly.SplineTranslateTo() - "<native>" Line ?
    [ (FF002D69)].critterdragonfly.GoToNewPoint() - "CritterDragonFly.psc" Line 174
    [ (FF002D69)].critterdragonfly.OnUpdate() - "CritterDragonFly.psc" Line 78
Link to comment
Share on other sites

 

At the Raven Rock docks (this happened when using the critter test 9 code);

[03/11/2014 - 10:40:44PM] error: Failed to setup moving reference because it has no parent cell or no 3D
stack:
    [ (FF002D69)].critterdragonfly.SplineTranslateTo() - "<native>" Line ?
    [ (FF002D69)].critterdragonfly.GoToNewPoint() - "CritterDragonFly.psc" Line 174
    [ (FF002D69)].critterdragonfly.OnUpdate() - "CritterDragonFly.psc" Line 78

I think we are down to diminishing returns.

elseif !CheckViability(false)
    ; And travel to it
    SplineTranslateTo(fTargetX, ftargetY, ftargetZ, fTargetAngleX, 0.0, fTargetAngleZ, fSplineCurvature, afSpeed, fMaxRotationSpeed)
Bool Function CheckViability(Bool bStart)

; prevent DisableAndDelete during calculations [USKP 2.0.3]
bCalculating = bStart

If PlayerRef && !bKilled && CheckCellAttached(self) && CheckFor3D(self)
    return False
EndIf

Thus, a mere return False ago, it had a parent cell and also 3D! There are no lines of code between the tests and the failure. It's a random timing problem.

 

I could move the bCalculating after the SplineTranslateTo() -- but we don't know that it was an OnUpdate event. I suppose that's worth a try....

Link to comment
Share on other sites

 

At the Raven Rock docks (this happened when using the critter test 9 code);

 

After a few tedious hours ensuring bCalculating is outside TranslateTo scattered about the code, and trying not to have too many duplicate lines setting it true and false over and over, here's test 10.

 

If you have a save around there, try it a few times to see whether the problem is fixed. Otherwise, it probably wasn't an OnUpdate anyway, as that was a very tiny timing window....

 

[old code removed]

Link to comment
Share on other sites

If you have a save around there, try it a few times to see whether the problem is fixed. Otherwise, it probably wasn't an OnUpdate anyway, as that was a very tiny timing window....

I tested with version 10 near the Raven Rock docks and didn't get the 'CritterDragonFly' error again, so it must either be fixed or the previous error was a fluke.

 

As far as the other critters go, I have been testing quite extensively in the areas where I was getting errors before and haven't been getting them. This leads me to believe that the version 9/10 critter test code has probably eliminated very close to all of the remaining problems that the version included in USKP 2.0.3 didn't eliminate.

Link to comment
Share on other sites

This leads me to believe that the version 9/10 critter test code has probably eliminated very close to all of the remaining problems that the version included in USKP 2.0.3 didn't eliminate.

Hallelujah! At least something is going better. These latest fixes were merely narrowing tiny timing windows where things could go wrong. Mostly simply removing looping, reducing the excessive number of lines of code, or speeding up the code path by selective duplication. The usual defensive programming and code optimization techniques.

Because we don't have the tools for proper scheduling synchronization, and the compiler code generation is not optimized at all, it's a bit ad hoc. Some issues will never completely go away (such as disappearing plants).

But couldn't have done it without your excellent reporting. Many thanks!

Link to comment
Share on other sites

  • 3 weeks later...

Another minor problem discovered today during testing. When you harvest a butterfly/moth/etc, they are very likely to be flying -- that is, experiencing TranslateTo(). So the translate is interrupted, triggering OnCritterGoalFailed(), which registers for a single update, which then disables and deletes.

 

But you are already disabled and deleting, called directly from OnHit(). And because of timing, this also can be during new calculations for the next TranslateTo(). There can be several threads working on the same critter at the same time. In any case, sometimes have a log message for a [None] failure.

 

Hopefully fixed by blocking repeated calls to OnUpdate(), using the same bCalculating flag. Wait a limited time (up to 1.1 seconds) for the bCalculating to be done, extending the wait for de-linking follower fish to a maximum of 3.3 seconds. This is a little long, as it delays the harvest sound and message in busy spots.

 

It's an infrequent delay even in the very busy places I've tested. However, this still isn't long enough. It can take 8 seconds to clean up all the butterflies, dragonflies, fish, and who knows what else....  There's a whole lot of scripting going on!

 

UPDATE: subtle logic error introduced above fixed in Critter. Am feeling quite pleased with myself, I awoke suddenly aware of the error and the fix! Although it's unlikely anybody here cares about the detail, note the new wasCalculating flag in CheckViableDistance(). Very clever, but obscure.

 

[old code removed]

Link to comment
Share on other sites

  • 2 weeks later...

I tested out the test 12 code. It seems to working really well just like the test 11 version. I did get several errors though (see below). As can be seen in the errors, papyrus isn't even reporting line numbers anymore, so this might just be engine nonsense, I'm not sure.

[04/10/2014 - 07:14:49PM] error:  (FF002D84): cannot find variable named fTakeOff.
stack:
    [ (FF002D84)].critterMoth.SetAnimationVariableFloat() - "<native>" Line ?
    [ (FF002D84)].critterMoth.DoPathStartStuff() - "CritterMoth.psc" Line ?
    [ (FF002D84)].critterMoth.BellShapeTranslateToRefAtSpeed() - "Critter.psc" Line ?
    [ (FF002D84)].critterMoth.BellShapeTranslateToRefAtSpeedAndGotoState() - "Critter.psc" Line ?
    [ (FF002D84)].critterMoth.GoToNewPlant() - "CritterMoth.psc" Line ?
    [ (FF002D84)].critterMoth.OnUpdate() - "CritterMoth.psc" Line ?
[04/10/2014 - 07:19:03PM] error: Cannot check if the reference has a named node because it has no 3D
stack:
    [ (FF002DA4)].ObjectReference.HasNode() - "<native>" Line ?
    [ (FF002DA9)].critterMoth.WarpToNewPlant() - "CritterMoth.psc" Line ?
    [ (FF002DA9)].critterMoth.OnStart() - "CritterMoth.psc" Line ?
    [ (FF002DA9)].critterMoth.OnUpdate() - "Critter.psc" Line ?

The two above occurred near Riften: [screenshot].

[04/10/2014 - 07:24:45PM] error: Failed to setup moving reference because it has no parent cell or no 3D
stack:
    [ (FF002DE7)].critterFish.SplineTranslateTo() - "<native>" Line ?
    [ (FF002DE7)].critterFish.GoToNewPoint() - "CritterFish.psc" Line ?
    [ (FF002DE7)].critterFish.OnUpdate() - "CritterFish.psc" Line ?

This one occurred at the hatchery, at Windstad Manor.

Link to comment
Share on other sites

All 3 are due to unexpected loss of 3D. All 3 have 3D checks -- the top one passes multiple 3D checks.

 

We're just going to have to live with the fact the game engine unloads 3D at unexplained times without warning.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...