ID:161665
 
I have just written a bunch of code that pretty much does the following. each mob/creature type is given a text variable called scent that is at the moment either "meat" or "wolf". Then I set up an Entered proc on the turf that adds that scent to a list also named scent. Then I have my preds looking at turf in oview(1) and checking that turf's scent for "meat". It then deletes that scent(so that it doesn't loop back) and moves to that turf before looking in oview(3) for a creature to attack.

will this theory work? will my predators hunt all the "meat"? is there a flaw in my basic theory, or in my execution? I'm just starting the testing, but I wanted all of your input.
I'd just make every type of creature leave an invisible scent trail as an object that only certain people can see and then follow.
In response to Dragonn
after some testing with original idea, I think you're on the right path. you can't look at turf by using view() as far as I can see, so I should just make the scent an object with a blank icon! thanks a lot.
In response to Jehubuddaka
Or you could just give it a "stink trail" icon and set the objects invisibility var to 1, and give the people who are capable of tracking scents a see_invisibility var of 1, that way only they can see the stink trail. Maybe.
In response to Dragonn
no I'm actually using it so that my non-player werewolves can hunt and kill smaller creatures that count as food for them. I'm trying to get them to follow scent trails.
In response to Jehubuddaka
My suggestion is to have a scent object (invisible of course) which the "meat" leaves behind whenever it moves from a tile. This object should have an automatic decay, which you could then use to tell which direction the source of the scent is in (the less decayed it is, the closer you are). That way, you can use ohearers() to locate the nearby scents, and also you could have multiple scents overlapping a single tile, if for instance 2 or 3 creatures passed by that space before the predator smelled it.

You could distinguish between the creatures using a variable in the scent object like "source" which contains some form of ID, possibly a \ref text, so predators could pick which scent to track.

EDIT: I forgot to mention that the decay could be used to automatically remove the scents after a certain amount of time, thus ensuring that they don't get cluttered up.
once I get the basics set up right, I will definitely be using your ideas. :)
In response to Jehubuddaka
Well, it took me an hour to figure out that I cant use view when the proc is being called by a non-player mob, but darn it, I got it working anyway. I used get_dist instead.

Also, I had a problem with setting the scent during the new command.
I had:
new /obj/odor(locate(src.x, src.y, src.z), scent=src.scent)

It didn't work EVER no matter how I tried it. I ended up having to make the mob change every odor object within a distance of 1 to have its scent.
here's the current code

layscent(mob/M as mob)
var/obj/odor/O
new /obj/odor (locate(M.x, M.y, M.z))
for(O)
if(get_dist(M.loc, O.loc)<1)
O.scent = M.scent

Now I had my werewolf track me across the entire map by giving myself the "food" scent, but once again I think I'm doing this the hard way. How should I be doing this?
In response to Jehubuddaka
You can use the view proc when it's being called by a non-player mob. You probably didn't set the center parameter to anything. It defaults to usr, which probably isn't set in this case.
In response to Jehubuddaka
I know everybody suggested this to you, but it's pretty bad implementation. If you're going to have a couple of these mobs around, say 10 or so, they're going to be leaving /objs all over the place and it's just messy. Also I don't think your code is entirely correct either.

Just have the wolf (or w/e) detect animals in the vicinity by using the built-in range() proc to return a list of atoms in the wanted range. Then if there is a juicy rabbit in that list, make it the wolf's target and make it step towards it's general location (or just outright make the wolf automatically know exactly where it is, if you want. maybe you could make it different between monsters depending on their smelling ability).
So you're going to end up with something like this for your AI:
monster
var/at0m/movable/target /*make a target var to decide the current target. /atom/movable type means either obj or mob.
if you think outside the box you'll see an /obj target makes
sense too, and features outside the box are very good to have. I'm not going to touch on objs in the code, but-
what if the wolf smells some nice obj/STEAK off a trash can/player/ground? though this is more of an idea I
mention - you could have certain /turfs be targets too.*/

AI()
...
if(!target) //if we don't have a target...
//check if there is a target in sight:
var/list/L = oview(src) //call the oview() proc and store what it returns to L
if(locate(/atom/movable) in L) //if there is any obj or mob in the list
if(!src.PickTarget(L)) //call our PickTarget() proc. Scroll down to it NOW, quick!

...

PickTarget(list/L)
if(src.target) //if we already have a target when this proc is called
if(src.target in L) //if it's in our list, then just stay with that target
return src.target //stop this proc and return "target" to the caller for convenience.
else src.target = null //clear the target

//you should use this proc to make the monster choose what target it wants to attack out of possible targets. ie just the best (weakest, or tastiest...) target
//this is just a very rough sample:
for(var/atom/movable/A in L) //loop threw the objs and mobs in the list
if(istype(A,/monster/bunny)) //if A is a tasty BUNNY
src.target = A //set the target to it
break //stop the loop
retorn target //return the target for convenience.

//now we continue the AI() proc and actually get to the smelling part:
if(!src.PickTarget(L)) //the ! operator checks if PickTarget() returned a false value
//so, if it couldn't pick a target from sight, this will run.
//now store what our Smell() proc return (scroll down here too)
L = src.Smell()- L //but remove the previous list we have from the new Smell() one, because we already know there are no good targets in the old one.
src.PickTarget(L)) //try to find a target in the new list
//TODO : now, if we have a target, then pursue and attack it.

mob/Smell()
var/list/smells = orange(src,src.smellsense) //lets say smellsense var is the amount of tiles this particular creature can smell. so get the atoms in that range
if(src.key) //player races should be able to smell to some extent too, and each have their own smellsense. so lets code it in (no, this doesn't have to do in particular with AI)
for(var/atom/movable/A in smells,) //loop threw the movables in the list
//let the player know about them
var/_dir = dd_dir2text(get_dir(src,A)) //this requires Deadron's TextHandling library. this gets the direction from the player to the smelled movable and converts it to text
src << "You smell a [A] to the [_dir]"
return smells //let the caller know
//scroll up back to the AI() proc now


This is an example and isn't perfect, or necessarily even good. It is just here to generally show you how to do this stuff. Things you'll want to do is a more throughout PickTarget() and possibly Smell(), a complete AI() proc and more. It also contains things you didn't ask for because I thought them nice, and since this code isn't meant to be copied and used anyway (and it will not compile if you do).
Also, you could try using the apple() proc instead of orange(). hahahaha, get it, get it?
In response to Kaioken
The reason I really like the scent idea is that the wolf could get to its objective even if the path the other critter took is a path through a maze. Maybe I can make each mob have its own scent string like "meat" or "veggie", then whenever they step onto a turf, using entered, I can add that string to a list contained in the turf, and then the predator could search for things by looking for turfs withing 2 steps of it and pick a turf to move towards by the scent. My only problem with that is I think the scent should definitely fade. Is there a way to attach an age to a string in a list? and if so, is there a way to slowly decrement that age until it reaches zero, and then delete that string? It just seems more realistic to me for a hunter to look around, and if it sees nothing, to scent around and follow the food smell until it can see something.
In response to Nickr5
so you're saying that I should be doing something like

for(M in view(3,src))
yadda yadda yadda

I really should have just read the DM reference. Once you said that, I looked it up and it explains it nicely.
In response to Jehubuddaka
Jehubuddaka wrote:
The reason I really like the scent idea is that the wolf could get to its objective even if the path the other critter took is a path through a maze.

You can just use a path-finding walk proc so the monster finds it's way to the 'victim'.

My only problem with that is I think the scent should definitely fade. Is there a way to attach an age to a string in a list?

Look up associative lists. You could use world.time and later check if it expired. I don't know if it's necessarily realistic though since animals generally seem to have a good enough smelling sense to smell stuff from away even if they haven't passed threw where the animal currently is.
In response to Jehubuddaka
yup, always read the reference
In response to Kaioken
See, this is why I said to use an object with an auto decay. The object itself would be declared like this:

obj
scent
density = 0
var
scent // You could set this to a default eg "food" if you wanted
decaytimeleft = 100 // I will explain this in a sec
owner // This var is used to reference whose scent it is. It's very important you set this, else all "food" will smell the same

proc
ScentDecay()
spawn while(1) // Infinite background loop
sleep(5) // Pause for half a second
src.decaytimeleft-- // Reduce the decay time
if(src.decaytimeleft < 1)
// Destroy the scent once it's decayed far enough as well as end the loop
del src


New()
.=..() // Do the default new proc first
src.ScentDecay()


This creates a scent which decays every half second for 50 seconds. If you reduce the sleep() time, you create a faster decay, which means that the creatures can move faster and leave a more accurate scent trail, but you'd then need to increase the decaytimer else the scents will decay that much faster.

Also, if you choose to make scent an object, then you can't use hearers. That's ok though, just make sure you make the scents have an invisibility, and the predators have a see_invisible of at least that value. That way, you won't run into any issues with scents not being, uh, smelled.

Also, you can check the decay timer of a scent to know how old it is. Here's an example:

mob
predator
proc
CheckScentAge(obj/scent/s)
// Pass a scent object into this function. It will return how old the scent is. The higher the number,
// the older the scent is.
var
initialdecay = initial(s.decaytimeleft)
// This gets the original value of the decay timer, so you can compare it to the
// current value to see how old it is
return(initialdecay - s.decaytimeleft)


The above example can be used in conjunction with your view(<insert range here>,src) call to check the age of each scent. You should also make a function to check the owner and the type of each scent (modularity) so that you can actually track individual mobs with them.
Perpetr8r the Perpetu8r wrote:
See, this is why I said to use an object with an auto decay. The object itself would be declared like this:

There's no reason to use an /obj for this though, you don't need any atom for this. Also, you don't even want the scent to be visible or even quite tangible, so it shouldn't be on the map. You'd also get the scent object needlessly included in lists like range(), etc.
If you really wanted this method you could just use a list of recent scents on the turf, that's all you need.

This creates a scent which decays every half second for 50 seconds.

Is there much point in this? No need for a loop, just wait X time until it needs to be gone then delete it. You can still measure the scent's age this way if you use a list but I don't see why you really need to. If a scent is still there, it can be smelled. If it can't be anymore, just get rid of it.
In response to Kaioken
Kaioken wrote:
There's no reason to use an /obj for this though, you don't need any atom for this. Also, you don't even want the scent to be visible or even quite tangible, so it shouldn't be on the map. You'd also get the scent object needlessly included in lists like range(), etc.
If you really wanted this method you could just use a list of recent scents on the turf, that's all you need.

Eh well the reason I'd use an object is because we need to store more information about the scent than just its location. Unless he wants to code in a jillion annoying extra variables for each turf (or a list with a jillion annoying variables in it), he needs some kind of container for all his information; A bigger one, might I add, than a simple associative list. Objects quite simply make that job MUCH easier and also provide something for a coder to actually look at and debug with (just turn see_invisible to 1 or whatever), instead of a bunch of hidden variables where anything can go wrong and you'll be unable to tell what it is without outputting all of your saved variables.

Kaioken wrote:
Is there much point in this? No need for a loop, just wait X time until it needs to be gone then delete it. You can still measure the scent's age this way if you use a list but I don't see why you really need to. If a scent is still there, it can be smelled. If it can't be anymore, just get rid of it.

The reason for the loop is because if, for example, we just called sleep(500), then we'd have no idea how long the scent would have been there originally, without storing extra data. Personally, I think that storing lists about every single variable associated with a scent is a lot more clunky than placing an object on the map. Besides, it makes it easier to override if say the prey crosses back over the same tile. You can have it reset the scent's decay timer without creating or destroying any scents in the process.

Remember, a scent needs to have a specific source, a type, an age, and a location in order to be usable. I suppose if you did a 2D list with 4 elements per row you could store all of those pieces of info separately for each scent, but why do it the hard way when BYOND provides you with objs for free? Besides, if you use lists, you need to write your own code for reading them and storing info in them, as well as any calcuations that need to be done (for example checking how long the scent has been there, or making a decay timer for it).

Perpetr8r the Perpetu8r wrote:
Eh well the reason I'd use an object is because we need to store more information about the scent than just its location. Unless he wants to code in a jillion annoying extra variables for each turf (or a list with a jillion annoying variables in it), he needs some kind of container for all his information; A bigger one, might I add, than a simple associative list.

What more would you need to store than the time the scent was created/left?

Objects quite simply make that job MUCH easier

Indeed, but you don't need to use atoms. And like I said in my opinion this doesn't require an object but everyone and whatever floats his boat.

and also provide something for a coder to actually look at and debug with (just turn see_invisible to 1 or whatever), instead of a bunch of hidden variables where anything can go wrong and you'll be unable to tell what it is without outputting all of your saved variables.

Well, thats not really a good reason. Normally you'd never need all that extra atom information and it's wasted. You can handle debug however you want afterwards you get the system working in whichever way you've chosen.

The reason for the loop is because if, for example, we just called sleep(500), then we'd have no idea how long the scent would have been there originally, without storing extra data.

I was thinking about storing the time the scent was "created" as the associated value, and that should be enough for this stuff you want, and is also more useful than just knowing the ticks since it was created.

Personally, I think that storing lists about every single variable associated with a scent is a lot more clunky than placing an object on the map.

Well, your way you're creating a new /obj object per any scent in a turf. If there were multiple creatures passing threw a single turf you could have multiple /objs inside there this way.
My way you only create a single new /list per turf, and either way a /list is cleaner than an /obj, and /obj even contains lists itself.

Besides, it makes it easier to override if say the prey crosses back over the same tile. You can have it reset the scent's decay timer without creating or destroying any scents in the process.

I don't see any problem with doing this in a list either. I think it's actually possible that it's easier with a list, since all you'll need to do is
src.scents[M] = world.time

and not anything else.

Remember, a scent needs to have a specific source, a type, an age, and a location in order to be usable.

You're absolutely right and it should be added you're approaching designs well. Here's how I'll have all this data by using lists:
sample list:
// list(object_reference = time_scent_was_created)
turf/var/list/scents = list(mob/rabbit/R = 214120)

The source: the rabbit.
The type: simply the rabbit's type (variable: R.type), and if you need any more specific type you could likely get it from this type(=type path).
The age: The associated value holds the time the scent was created, so the age would be world.time - scents[R].
The location: well, this would be 'src' and not particularly difficult to get a hold of.

I suppose if you did a 2D list with 4 elements per row

So no, this is done with a simple associative list.

why do it the hard way when BYOND provides you with objs for free?

Well, the above way isn't too hard IMO. If one already knows how to use associative lists it'd be more or less the same as using an obj.

Besides, if you use lists, you need to write your own code for reading them and storing info in them,

? I don't understand. You need to write code for everything. Getting the above information from a list is still easy like I explained above.

Anyway, you can definitely use basic objects for implementations in DM without using atoms needlessly (sorry, but-duh!). They're called "datums", look them up, and also read this useful link.
http://www.byond.com/members/ DreamMakers?command=view_post&post=35530
In response to Jehubuddaka
Another thing to consider is wind. If you implement wind, you could have the scent objects move to it.

But one thing about predators is they want to be downwind of their prey. Otherwise the wolf's prey would become alert when they sense wolf scent. Smart wolves want to move cross-wind until they pick up the freshest scent, then stalk upwind to the prey.

And what erases scent besides time? How about rain? If you implement a weather system, you could have rain erase scent objects. An animal swimming across a stream shouldn't be easy to follow. Have them stop dropping scent when they are wet. Also when they are rained on, they would be wet and still wet for a while when the rain stops.

Another thing to consider is animal sign. An animal marks territory with sign (musk/urine/pheromones/etc). A player might be able to collect sign and use it for various purposes. For example, in the movie "Sniper" the hero covered himself with manure so the bad guys couldn't find him with hounds.