Unintended results in monster spawn range calculations

Discuss coding questions, pull requests, and implementation details.
Post Reply
User avatar
Rand
Posts: 72
Joined: Sat Nov 23, 2019 5:10 am
Location: Canada

Unintended results in monster spawn range calculations

Post by Rand »

Jehuty asked me to look at some code. Something bothered me about it and after thinking for a while, I noticed this:

The code is from RandomEncounters.cs
https://github.com/Interkarma/daggerfal ... s.cs#L1476

First, to those who aren't coders, bear in mind that the first slot on an enemy list is number 0, not 1. (the 20th slot then corresponds to the number 19, not 20)

Okay, so to generate an enemy it essentially "rolls" a number from 1 to 100.

If the rolled number is 1 to 80 (80% chance), it sets a range of numbers from the player level -3 to player level +3.

If the rolled number is from 81 to 95 (15% chance) it sets a range of numbers from 0 to player level +1.
EDIT: This next section is wrong, please ignore it. :oops:
THERE IS (was) A POTENTIAL GLITCH HERE IF THE PLAYER LEVEL IS 19 OR HIGHER: the generated max will be 20 or higher. This would normally be a significant problem, but see below.

If the rolled number is 96 to 100 (5% chance), and if the player level is 5 or less, it sets a range of numbers from 0 to player level +2. If the player level is 6 or higher it sets a range of numbers from 0 to 19. (That means any monster on the list. Hello Vampire Ancients, Ancient Liches, and Daedra Lords. At level 6 :twisted:)

Then it looks at those max and min numbers. If the minimum or maximum number of the range turn out to be less than 0 or over 19, the ranges become fixed: 0 to 5 if the min went below 0, or 14 to 19 if the max went over 19. (It can't possibly do both.)

Lastly, it picks one number from the range and looks on a list specific to that area to see what monster to place.

EDIT: This next section is wrong, please ignore it. :oops:
As to the issue in the 15% chance condition. Normally there would be a serious error with a possible too high max value at player level 19, because the lists are only 20 elements long and so it can't choose one if the generated number ends up being 20 or higher. I have no idea what the result of that would be. A crash, maybe?

But some code added by DFU (for a different purpose!) at line 1517 seems to accidentally catch the excessive value in that case.

EDITED: removed unnecessary explanations

EDIT: This next section is now unnecessary, please ignore it. :oops:
Altering the code can fix this quite easily. One solution is to add some lines between 1495 and 1496:

if (max > 19)
{
max = 19;
}

But this only fixes the level 19+ problem.


The DFU code (lines 1517 to 1524) produces incorrect results to the intended ranges generated by classic code, lines 1477 to 1512.

Now, the DFU code shouldn't normally come into use unless the enemy list is shorter (or possibly: longer) than 20 entries (which, at present, none are).

But in my opinion, at minimum, line 1521 (min = max - 5;) needs to be rewritten anyway, so as to properly set min to reflect the min set by the classic conditions (level -3 (minimum 0) or level 0, depending on which condition between lines 1477 to 1512 was passed).

This naturally increases the frequency of higher level spawns for level 19+ characters when this code comes into use.

Is this a major problem? No, but it is an unintended consequence.

There are several pretty obvious options/solutions, but I won't presume to write the code for you guys. However, if I were doing it, I would re-write the lines from 1477 to 1512 without assuming 20 element lists (both longer and shorter ones) and remove the (now unnecessary) DFU code from lines 1517 to 1524. This would solve all possible issues at present and in future.

My thanks to Jehuty to bringing this to my notice. :)
Last edited by Rand on Sun Dec 08, 2019 7:29 pm, edited 2 times in total.

User avatar
Ralzar
Posts: 2211
Joined: Mon Oct 07, 2019 4:11 pm
Location: Norway

Re: Unintended results in monster spawn range calculations

Post by Ralzar »

And if DFU didn’t have a 20 limit on the lists, we could do more with modding it :)

User avatar
pango
Posts: 3347
Joined: Wed Jul 18, 2018 6:14 pm
Location: France
Contact:

Re: Unintended results in monster spawn range calculations

Post by pango »

Seems to match my own previous analysis, alternate random enemy selection is currently broken in endgame.
I simplified nested ifs by switching to mathematical expectations, which is what those heatmaps are based on.
Mastodon: @pango@fosstodon.org
When a measure becomes a target, it ceases to be a good measure.
-- Charles Goodhart

User avatar
Interkarma
Posts: 7236
Joined: Sun Mar 22, 2015 1:51 am

Re: Unintended results in monster spawn range calculations

Post by Interkarma »

I'm happy for this be reviewed. My main requirement is that any change still generates classic enemy loadout, especially in Privateer's Hold upon starting a new game. My early placeholder enemy selection generated different enemy loadouts than classic, and I received many complaints about it. That all stopped when Allofich implemented the current version, which matches classic other than the flaw mentioned.

If nobody else wants to take this on (Pango seems to have some knowledge and interest of this already), then I can take a chop later on.

I'll move this to developer discussion as that's a more appropriate spot for it.

User avatar
pango
Posts: 3347
Joined: Wed Jul 18, 2018 6:14 pm
Location: France
Contact:

Re: Unintended results in monster spawn range calculations

Post by pango »

Actually I needed to reread your first post more carefully, because you're talking of a somewhat similar issue, but in classic enemy selection code.

Well, test line 1517 is currently never triggered with the classic 20-length encounter tables, because max is already clamped to 19 line 1508, a check you somehow seems to have missed. That's also why I did not take into account this DFU specific code in my own analysis.

It will only become an issue with shorter encounter tables, if we decide to use some. Simplest way to extend the classic algorithm to any other encounter table lengths would be to replace code between lines 1508 and 1524 with

Code: Select all

int maxIndex = EncounterTables[encounterTableIndex].Length - 1;
if (max > maxIndex)
{
  min = maxIndex > 5 ? maxIndex - 5 : 0;
  max = maxIndex;
}
(if maxIndex is not > 5, the whole algorithm degenerates to a random selection over all available enemies though)
Mastodon: @pango@fosstodon.org
When a measure becomes a target, it ceases to be a good measure.
-- Charles Goodhart

User avatar
Rand
Posts: 72
Joined: Sat Nov 23, 2019 5:10 am
Location: Canada

Re: Unintended results in monster spawn range calculations

Post by Rand »

Yes, Pango, you're correct. The clamp code from 1503 to 1512 does catch all excessively high and low values in classic.

My only defense is that I was working very late at night, and consequently my underperforming brain associated lines 1503 to 1512 solely with the else conditional at 1498 to 1502 (the 80% chance -3 to +3 range).

Looking at it now after 10 hours of sleep, I see it immediately. :oops:

So while the potential bug was a sleep-deprivation phantom (and the potential fix I suggested is unnecessary), the rest of the post is essentially correct, and your proposal for the DFU code patch is a good one.

A comment in the encounter tables section that lists must not be shorter than 6 elements (or whatever the code eventually assumes) would be a good idea, too.

Post Reply