Can any other Devs explain how the hell SMELL is coded?

I am looking through he code, and I see where it is defined, but the commenting is poor, and the documentation doesnt exist, so where is the function for it?

You have MF_SMELLS, which you give to monsters, but where do you adjust it?

I would like to make it so each mob has different levels of senses.

For example, there is no reason a Zombie should be able to smell you as well as a wolf.

The only reference I can FIND is in monmove.cpp about them tracking it.

[i]point monster::scent_move(game *g) { plans.clear(); std::vector<point> smoves; int maxsmell = 1; // Squares with smell 0 are not eligable targets int minsmell = 9999; point pbuff, next(-1, -1); unsigned int smell; for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { smell = g->scent(posx + x, posy + y); int mon = g->mon_at(posx + x, posy + y); if ((mon == -1 || g->z[mon].friendly != 0 || has_flag(MF_ATTACKMON)) && (can_move_to(g->m, posx + x, posy + y) || (posx + x == g->u.posx && posx + y == g->u.posy) || (g->m.has_flag(bashable, posx + x, posy + y) && has_flag(MF_BASHES)))) { if ((!is_fleeing(g->u) && smell > maxsmell) || ( is_fleeing(g->u) && smell < minsmell) ) { smoves.clear(); pbuff.x = posx + x; pbuff.y = posy + y; smoves.push_back(pbuff); maxsmell = smell; minsmell = smell; } else if ((!is_fleeing(g->u) && smell == maxsmell) || ( is_fleeing(g->u) && smell == minsmell) ) { pbuff.x = posx + x; pbuff.y = posy + y; smoves.push_back(pbuff); } } } } if (smoves.size() > 0) { int nextsq = rng(0, smoves.size() - 1); next = smoves[nextsq]; } return next; } [/i]

So, I am guessing what needs to be done is 3 levels of MF_SMELLS
Like say a MF_SMELLSSHARP (High sense of smell) MF_SMELLSNORMAL and MF_SMELLSLOW (Poor sense of smell)

int maxsmell = 1 is Obviously the key, but I need to read through some more of the code and see how it tracks how long a “scent” stays in a square…

Anyone look into this yet? Its more of an issue of figuring out WHERE stuff is located…

I’ve looked into this a little bit, but some of the math is a little funky, so I’m not sure it’s really working as intended.

Probably the simplest thing to do is set some threshold before the loop based on how good the monster is at smelling, and do:
smell = g->scent(blabla);
if( smell < threshold ) continue;

Scent is currently a cloud that disperses very rapidly unless it’s refreshed constantly.
game::update_scent() is responsible for updating the scent cloud, which is maintained in game::grscent[][], a 2D array of scent intensity, this should probably live in the map, not the game structure for one thing.

what it does is fairly simple, first it sets the square the player is on equal to the player’s scent, then for each tile within an 18-square radius* it calculates a pseudo-average** of each square’s adjacent*** scent values, then overwrites the old scent map with the new one.

There’s a proposal to make the player’s scent “stick” to the area where the player has been more strongly, and make it spread less, so it acts more like a trail than a cloud.

*18 looks like a legacy value since the active area is 120 squares across. Additionally it looks like it’s going to leave phantom scent values in some cases if there is any scent left once the player is more than 18 squares away because it stops updating the values then, but they’re still used for monster navigation.
**This pseudo-average is kind of weird, it only counts squares that are stronger or as strong as the current square, but then adds one to the average, which means isolated pockets of scent die off VERY fast.
***Since it equally weights surrounding squares, the cloud would expand in a square as long as the player doesn’t move, but due to the weird averaging thing, the corners of the square will decay faster than the sides (since there aren’t as many adjacent equal-or-higher-strength squares)

BTW, here’s how the algorithm progresses if you cut the max value to 0xF and don’t move:

0000000 0000000 0000000 0000000
0000000 0000000 0000000 0000000
0000000 0011100 0032300 0064600
000F000 001F100 002F200 004F400
0000000 0011100 0032300 0064600
0000000 0000000 0000000 0000000
0000000 0000000 0000000 0000000

0000000 0000000 0000000 0000000
0011100 0023200 0044400 0045400
0175710 0276720 0476740 0476740
015F510 036F630 046F640 056F650
0175710 0266720 0466740 0476740
0011100 0023200 0044400 0045400
0000000 0000000 0000000 0000000

It progresses from left-to-right then top-down. As you can see, it spreads pretty fast (this would only take a minute in game, and would spread even faster if the max value was the default (600) instead of 0xF). I was really wasting my time with this, because with values this small the algorithm gets dominated by rounding errors. For example, it spreads to 0 tiles very slowly because every adjacent tile gets averaged in, which means the adjacent values have to add up to 10 before a 0 transitions to a 1. This isn’t an issue with large values, because that threshold of 10 is more easily met since the values start in the hundreds. You can see several features of the algorithm though, one is that each concentric circle is roughly half the next circle in, and at some point the whole thing reaches stasis because the averaging is so aggressive. As long as the player doesn’t move, that last scentmap will stay stable indefinitely. This points out another feature, which is that an area never develops “memory” of the player’s presence, the scentmap will roughly stabilize within 10 mins or so, after which it’s just a static feature. Another feature this doesn’t highlight is that walls block scent, or more properly they can never have scent. [size=12pt]Something that surprised me is that windows and doors don’t block scent at all, just walls, which means we’ve been giving people bad advice about avoiding being sniffed out by zombies :P[/size] I think I’ll want to do something about this in the short term while I’m still mulling over what to do about the system as a whole, because closed doors and windows should definitely attenuate scent more than that.

I’m guessing you’ve never looked at the in-game scentmap much during testing?
Personally, I refer to it if I need a semi-safe shelter for a few hours, to see just how far out my scent is drifting.
Doors and windows are quite apparent by the bottlenecks and scent spread well beyond the room you’re in.
Also of note is that I use a 5-minute wait when testing different areas of a room to see where my scentcloud actually reaches, so I know what area of the room I’m sheltering in confines my scent best.

Hopefully, scent will be a bit more realistic or understandable, and my referring to the debug scentmap will be a thing of the past.

Kevin, thanks. Wish this stuff was commented better, but I HAVE been going through and reading everything, making my own little comments here and there. (When Im not stuck at work for 14 hours, :stuck_out_tongue: )

I knew about the debug scentmap, but i didnt understand what they were doing with “Sensitivity”? 1-0? What for? Why the different levels?
Plus you cant scroll past the edge of the screen, so I cant really check if I am leaving “trails”

I ended up adding a MF_KEENNOSE to dogs and Wolves(And possibly other things later) and the following before the loop;

369 + if (has_flag(MF_KEENNOSE)) 370 + int maxsmell = 1; // Squares with smell 0 are not eligable targets Oddzball-Scent? 371 + else 372 + int maxsmell = 2; //Oddzball-Correct setup for smell? 373 int minsmell = 9999; 374 point pbuff, next(-1, -1); 375 unsigned int smell;

(Outta my github)

What it does is essentially zombies cant follow that little trail of 1’s anymore, but wolves and other things which SHOULD have a keen nose can.

I was mostly referring to Kevin’s post with my reply.
However, the different levels are supposed to be corresponding to different levels of scent sensitivity, however most mobs all have the same really high level. :-/

The only suggestion I have for checking trails is checking every few steps, or waiting many turns between steps.
A stable scentmap never fills the screen.

All of this code hurts my brain, I need to learn this before I read any more of this page :confused: