ID:2369895
 
Code:
mob
monster
New()
spawn()
if(get_dist(src,usr) > 2)
step(src,cardinal_dirs[rand(1,4)])
sleep(15)
else
usr << "I see you."
sleep(15)
Slime
icon = 'Slime.dmi'


Problem description:
When I load up the game, nothing happens. The slime just stands there, stock-still. And I also don't get the "I see you" message. ;_;
Sorry, I mean to write:
mob
monster
New()
spawn()
for()
if(get_dist(src,usr) > 2)
step(src,cardinal_dirs[rand(1,4)])
sleep(15)
else
usr << "I see you."
sleep(15)
Slime
icon = 'Slime.dmi'
Okay, so taking away the if case structure and just having
step(src,cardinal_dirs[rand(1,4)]) at least makes the slime move. So maybe my understanding of the get_dist instruction is wrong.
mob
monster
New()
spawn for()
var/distance = get_dist(src,usr)
world << distance
step(src,cardinal_dirs[rand(1,4)])
sleep(5)
Slime
icon = 'Slime.dmi'


Okay, this has a value of -1 constantly being output to the world. I tried replacing world with usr and no output occurred. So many questions.
usr doesn't mean what you think it means. All BYOND games are multiplayer worlds. There is no single "usr" that refers to the player's mob, because there can be multiple players in the world at the same time. If you're actually making a singleplayer game, then you might benefit from having a global Player variable that is set when the player logs in.

In a proc or verb, usr is simply the player who triggered the proc/verb. For example, when clicking an object or verb, usr refers to the mob belonging to the player who clicked the object or verb.

So, unless the monster was created in a context where usr exists (for example, the player created the monster using a verb), usr won't refer to anything (it'll be null) in the monster's New() proc.
I was afraid of that. The more I tinkered with it, the more I was starting to suspect that I couldn't use usr in this context, or that it didn't mean what I thought it meant. Thank you for verifying that for me.

mob
monster
Slime
icon = 'Slime.dmi'
var/mob/M
New()
spawn(5) for()
if(!M.client) return
else
var/distance = get_dist(src,M)
world << distance
step(src,cardinal_dirs[rand(1,4)])
sleep(5)


What about something like this?
In response to Matthewkind
M is never assigned, so it stays the default value of null forever. You'll immediately get a runtime error when the slime is created because you're checking for a variable (client) of null.

Also, if M is ever assigned to something other than a player with a client, then the loop stops permanently.

Also, you can use pick(cardinal_dirs) instead of cardinal_dirs[rand(1, 4)].
You're never setting M to anything, so it's not gonna go so well.

You need to set the variable in question to a mob in the world that you want it to follow, most games have some kind of 'activation' process where if a player moves into range of a monster it'll set a target variable to that player and start the AI loop.

Having the loop running all the time regardless of whether a player is around or not is gonna murder your game's performance pretty quickly.
Okay, so the game knows that M is a mob, at least.
But by default it's null, right, I get that.
But how do I tell that M is the player if usr isn't used the way I thought?
Your game might have lots of players, each one has its own set of variables and procs.

A basic (but not the most resource-friendly) method is something like

mob/player
Move()
..()
for(var/mob/monster/M in view(src))
M.Activate(src)


Then you'd have an Activate() proc under your monsters that would set the target and whatnot.

mob/monster
var/mob/target
Activate(mob/trigger)
set waitfor = 0
if(target) return // Already has a target
target = trigger
while(target)
// Do your movement stuff here
sleep(5)
// You'll want something that can BREAK this loop, like checking if the target has been reached, killed, gone out of range, etc.


Just an example written off the top of my head, it's for learning purposes and not copy/pasting.
Right. So to see if I understand:
You're making the default mob
mob/player, so when a new player logs in, their type path is mob/player.
Then you're editing the pre-existing Move proc. You're letting it do what it normally does, as per the ..(), and then
you're saying for any monster M that the...src, in this case player, can see, have the monster...call some proc called Activate?
Yep, that's the gist of it. However, keep in mind that method of activation isn't entirely efficient. Calling view() everytime a player moves is quite costly.

That's just an example of how you'd go about setting the variable, ideally you'd do something like split your world up into regions, which keep a list of the monsters within them, then when a player moves into that region it would trigger those monsters, if needed.

That gets a bit complicated though, so starting basic and moving up to something like that is ideal.
I think I'm starting to get an idea how this all works.
Question, if you create a var under monster, like
mob/monster/var
mob/Player/M, would that reference a player then?
Or at the very least, it would have to reference anything with a client by default. I'm still having lots of trouble and to be quite frank, this feels really mystifying. I read the entire guide and I still feel like completely lost at sea.
That just tells the compiler that you can access variables under /mob/Player from M. DM doesn't actually have any type-safety at runtime, which is why you'd usually want to istype() those variables if you're unsure if they're set to the right value.

You always need to set the variable to SOMETHING, there's no place a default value set at compile-time would work for what you're looking for.
I guess this is the big problem for me. How do you set it to the proper thing? Like, how do you reference a player? I feel like I'm starting to spin myself in circles here, like something is not conceptually clicking.
mob
monster
var/mob/Player/M
proc
MoveAI()
while(src)
for(M in view())
if(get_dist(src,M) < 2)
step_to(src,M)
sleep(10)
else
step(src,pick(NORTH,SOUTH,EAST,WEST))
sleep(10)


I tried something like this, but it produces a tremendous amount of lag. But it's one of the only ways I can think to do it. You tell the program that M is a Player mob, and then you check the list of player mobs within view in the for loop, and at that point any M under the for is a player mob in view of the slime... Right?
In the example I showed above, it was set by the player moving, where 'src' was the player. Setting the monster's target variable to the player is the magic.

The reason we don't have the monsters constantly looking for targets like the code you've posted is because that's a lot of stuff running for each monster in the world ALL THE TIME, which is gonna cause lag and lots of issues.

The trigger when a player moves isn't the most efficient method either, but it's a ton more efficient than a loop, because it lets you trigger the NPCs on-demand without having to have a loop running the entire lifetime of said monster.

The code I posted above would look for monsters within view of a player each time the player moves (given the player's type is set to /mob/player), then call a proc for each one if it finds any, passing itself (the player) through the argument of that proc so it can be utilized within the proc.

Using that information we set a variable on the monster (target) to the player that triggered them, unless they already have a target. Within that activation function we have a loop that runs the entire time the target variable is set, moving the monster towards whatever that variable is set to (the player that triggered them).

What I didn't show was a termination point, which would do something like checking if the player had gotten too far away, died, or killed the NPC, whatever, to end the movement loop.
I GOT IT!
world
mob = /mob/Player
mob/Player
mob
monster
var
mob/Player/target
Eyesight = 4
proc
MoveAI()
while(src)
sleep(10)
if(src.target) // i got you now
if(get_dist(src,target) <= src.Eyesight)
step_to(src,target)
else
src.target = null
world << "WHERE DID YOU GO"
else // doesn't have a target
for(var/mob/Player/M in view(src.Eyesight)) // because it goes through all M's in view
if(!src.target)
src.target = M
else
if(get_dist(src,src.target) > get_dist(src,M))
src.target = M
if(!src.target)
step(src,pick(NORTH,SOUTH,EAST,WEST))
Page: 1 2