ID:163118
 
Heya guys :)


Me again XP
Well, I was wondering how I would go about making monsters attack back and move. I got the character to attack, but I am not sure how to get another mob to attack or move. I like to stray away from librarys lest I not know what the coding is. If you could either direct me towards the area to learn it or give me some code that would be greatly appreciatted :)

My goals for sunday is that I need to get the level system in, I need to have the display ready, then i need to have mobs attack and move then i need them to respawn. Thanks for the help in advance :D
do you want the monsters to randomly move around in 1 area or wherever they want to go?
In response to VolksBlade
I want them to move around randomly when you enter their vision and then move to attack you when you enter within 5 spaces of it.
In response to Blanke
Think about it. You are going to have to find some way of detecting when a player moves into range of a monster. You could have the monster check every 3-5 seconds if a player is near it. This, however, is exceptionally ineffective, because it involves a delay, and if every monster does this, you end up with useless functions eating up CPU.

Or, you could have the player send their location to all monsters in a certain range every time they move. The monster then turns on a detection function that determines if the monster has been baited.

If the monster is baited, it moves them into a new processing loop, making the monster try to follow their selected target until they are out of range.

If the player is out of range, the monster will then check the area for more potential targets, using the detection function we called before the monster was baited, and if no target is found, we switch off the monster again, returning it to the original state.


Let's break this down into simple functions.

1) Player moves->pings nearby monsters.
2) Monster pinged->tests if baited. If no bait, go to 3.
2a) Monster baited->Follow player. If in range, go to 2b, else, repeat 2a.
2b) Monster in range->Attack player. If still in range and alive, repeat 2b, else, go to 2a.
3) No bait->If more bait in range, go to 2a. If none, switch off.

This is a simple three step process, ping, pursue, reset. By breaking it down, we can see that we have three basic elements to our problem in order to reach the solution.

Let's have a look at one possible solution (note, there is no THE solution, just many possible solutions, and I am only showing one of them.)



Step 1) Ping

First, we have to make the player's movement ping other monsters.

mob
being
player //this typepath can be changed, but I always use this one.
Move()
. = ..() //call the parent proc
if(.) //if parent proc is true (move succeeds)
if(src.client) //is actually a player.
for(var/mob/NPC/monster/M in range(12,src))
M.pinged(src) //pings every monster in a range of 12 of the player's new location.
return . //returns the parent proc's return value.


Now that we have called Monster.pinged, we should actually define it. All it needs to do is determine if the monster is in range to attack the player, if the monster is preoccupied by another player, or if the monster is even interested in attacking the player.

mob
NPC
monster
var
mob/being/player/target //holds the current target, or null if turned off.
chase_range = 6 //set at what range the monster begins pursuit.
move_delay = 10 //moves once per second
attack_delay = 7 //attacks every 0.7 seconds
proc
pinged(var/mob/being/pinger)
if(!src.target) //if the monster isn't distracted already.
if(get_dist(src,pinger)<=src.chase_range) //if the target is close enough to the monster.
if(pinger.client) //if the pinger is a player.
if(pinger in view(src,src.chase_range)) //if the monster can 'see' the player.
src.target = pinger //set the target, so that the monster doesn't chase anybody else.
src.pursue() //commence chase!
return 1
return 0




Step 2) Pursue

Now, we actually have to make the monster chase the player. This is pretty easy using built-in procs, but your monster isn't going to dodge obstacles. You'll have to do that on your own.

mob
NPC
monster
proc
pursue()
var/rng = get_dist(src,src.target) //store the distance in a local variable.
if(rng<=src.chase_range&&src.target in view(src,src.chase_range)) //stop chasing if the player is hiding, or has gotten out of range.
if(rng>1)
step_towards(src,src.target) //take one step to the target.
spawn(src.move_delay)
src.pursue() //after X milliseconds, keep chasing.
else
src.attack() //call the attack function. You are going to have to define this.
spawn(src.attack_delay)
src.pursue() //after X milliseconds, keep chasing or attacking.
else //if out of range, or out of view.
src.target = null //give up on target
src.find_target() //try looking for another target.



Step 3) Switch off

Step 3 is sort of the beginning and the end at the same time. If there is a secondary target, it will keep hunting, if there isn't, it goes back to not processing anything until it is pinged by another player.

mob
NPC
monster
proc
find_target()
var/mob/being/player/found //store the next target locally.
var/fdst = src.chase_range+1 //next target's distance (starts higher than potential range for ease of use)
for(var/mob/being/player/tgt in view(src,src.chase_range))
if(tgt.client) //if is a player.
if(get_dist(src,tgt)<fdst) //compare distances to find the closest potential target.
found = tgt
fdst = get_dist(src,tgt)
if(found) //if there is another target.
src.target = found //set next target
src.pursue() //being pursuit!
return 1
return 0 //switch off


Note that the player will be pinging monsters all the time, even when they can't start chasing him. These calls will however, contain MUCH less overhead than all the monsters in the world searching for targets all the time.

I like this method better, and think that this method is vastly superior to the monsters constantly walking around, looking for targets, or for monsters constantly standing still, trying to figure out if they should be walking around, looking for players.

Optionally, you could start a switch cycle, that checks if a player can see the monster, and if the monster is not engaged with another player, to randomly walk around, so that they don't just stand there until you make them angry.

It's yours to toy with.
In response to Ter13
Thank you, VERY MUCH. This is like perfect. =D I want to have a working battle system by sunday, and this way I can have it =D

I agree with your hypothesis. It causes inexorable lag if every monster is moving all at once and checking for players. I will probably do what you said, have an annual check and movement every like 10 minutes or something to have them move a bit. (Once I get more adroit with coding of course :p)

Thanks again for the code. Oh, and nice //notes =D


Oh, ummm. I am getting an error =/

Attacking.dm:37:error: if: missing comma ',' or right-paren ')'

mob
NPC
monster
proc
pursue()
var/rng = get_dist(src,src.target) //store the distance in a local variable.
if(rng<=src.chase_range&&src.target in view(src,src.chase_range) //stop chasing if the player is hiding, or has gotten out of range.
if(rng>1) <----That is the line in question.

Other than that I don't have any errors, think you can shed any light on it? ~Update~ I also indented everything instead of copying and pasting it. So it is all in the right order. I also tried adding commas in all sorts places and the ) as well.
In response to Blanke
Use the < /dm > tags.

Your code:
mob
NPC
monster
proc
pursue()
var/rng = get_dist(src,src.target) //store the distance in a local variable.
if(rng<=src.chase_range&&src.target in view(src,src.chase_range) //stop chasing if the player is hiding, or has gotten out of range.
if(rng>1) <----That is the line in question.


The line above it only has one closing parenthesis. You need two, one to close the view(), and the other to close the if().
In response to Ter13
i_n_t_e_r_e_s_t_i_n_g i might just use this! *hears the Dundundun!* thats awsome, ill deffinitly give you credit if i do
In response to Evre
My bad! I always miss something tiny like that. Then again, that's what I get for just pouring ideas out of my skull as they occur to me.
In response to VolksBlade
I don't need credit. It's no more an idea of mine as it is common sense and basic programming logic.

Just try to learn something from it, that's all I'm here for, to help out.
In response to Blanke
I will probably do what you said, have an annual check and movement every like 10 minutes or something to have them move a bit.

Well, I'd say do it more intelligently than that, for instance, if they get pinged, and cannot find a target within the chase range, but there is still a player within say, 12 tiles, they move around every 2*move_delay milliseconds.

That way, the world seems to have life, and players might just incur the wrath of a wandering monster. Just by adding to this theory, you could do it with little modification. You'd have to modify the monster a little bit, and modify the pinged, and find_target procs a little, and possibly add a new proc to handle the wandering. Other than that, though, not much work besides tweaking.

There are infinite things you can do to improve this system, like allowing for ranged combat, and using a pathfinding library to help in chasing the player. Or even tracking last known locations, so that when the player ducks behind cover, it will go to that location, and try to find them using line of sight.


I've used this method very often. I've implemented numerous secondary systems into this, pathfinding, scent trails, AI wants/needs, instant away-generation, situationally aware enemies, squad-based movement, etc. I haven't given you some simple solution, I've given you the bare bones of a good-ish solution. All that's left is to keep adding to it!

Now, if I could just release a game that actually meets my standards, and I don't get tired of moderating! >_<
In response to Ter13
Attacking.dm:9:error:M.pinged:undefined var
Attacking.dm:37:error:if :invalid proc definition
Attacking.dm:38:error::invalid proc definition
Attacking.dm:39:error:src.move_delay:undefined var
Attacking.dm:40:error:src.pursue:undefined proc
Attacking.dm:38:error:step_towards :invalid proc name: reserved word
Attacking.dm:41:error:else :invalid proc definition
Attacking.dm:34:error:pursue :previous definition
Attacking.dm:45:error:else :invalid proc definition
Attacking.dm:62:error:= :invalid proc definition
Attacking.dm:63:error:src :invalid proc definition
Attacking.dm:65:error:return 0:instruction not allowed here
Attacking.dm:9:error::invalid expression
Attacking.dm:37:error::invalid expression
basics.dm:118:error:attack :previous definition
Attacking.dm:53:error:find_target :duplicate definition
Attacking.dm:62:error::invalid expression
Attacking.dm:64:error:return 1:expected proc definition

is what happens when I add that extra parenthesis =/
In response to Ter13
lol, thanks I will keep that in mind. I just need it to work :( *Points to butt load of errors when adding the parenthesis*
In response to Blanke
Did you replace my spaces with tabs?

If you just copied and pasted it, it isn't going to work. You have to retab it properly so that Dream Maker will know what to do with it.

Plus, you might want to unsegment the mob/NPC/monster code. It's separated into three sections when there only really needs to be one segment.
In response to Ter13
~Update~

Attacking.dm:9:error:M.pinged:undefined var
Attacking.dm:9:error::invalid expression


M.pinged(src) //pings every monster in a range of 12 of the player's new location.

is what it is down to now.
In response to Blanke
Alright, Well, take this entire snippet here:

Now, open Notepad. Hit Tab, hold shift, and hit left of the keyboard. Now hit Ctrl+X. Paste the code snippet I'm showing you below these instructions. Hit Ctrl+H, and in the first line type two spaces. Now, in the second line, click on the text area. Hit Ctrl+V to paste your copied tab. Now, hit the "Replace All" button. Exit the Replace dialog box, and copy all the code from Notepad, and paste it into Dream Maker.

If you compile this, the only error you should be getting is "error:src.attack:undefined proc". If you define your own attack proc, you should be set. If you are getting other errors, it's something you did wrong.

I have officially tested this snippet, and it does compile fine.

mob
being
player //this typepath can be changed, but I always use this one.
Move()
. = ..() //call the parent proc
if(.) //if parent proc is true (move succeeds)
if(src.client) //is actually a player.
for(var/mob/NPC/monster/M in range(12,src))
M.pinged(src) //pings every monster in a range of 12 of the player's new location.
return . //returns the parent proc's return value.
mob
NPC
monster
var
mob/being/player/target //holds the current target, or null if turned off.
chase_range = 6 //set at what range the monster begins pursuit.
move_delay = 10 //moves once per second
attack_delay = 7 //attacks every 0.7 seconds
proc
pinged(var/mob/being/pinger)
if(!src.target) //if the monster is not already distracted.
if(get_dist(src,pinger)<=src.chase_range) //if the target is close enough to the monster.
if(pinger.client) //if the pinger is a player.
if(pinger in view(src,src.chase_range)) //if the monster can 'see' the player.
src.target = pinger //set the target, so that the monster doesn't chase anybody else.
src.pursue() //commence chase!
return 1
return 0
pursue()
var/rng = get_dist(src,src.target) //store the distance in a local variable.
if(rng<=src.chase_range&&src.target in view(src,src.chase_range)) //stop chasing if the player is hiding, or has gotten out of range.
if(rng>1)
step_towards(src,src.target) //take one step to the target.
spawn(src.move_delay)
src.pursue() //after X milliseconds, keep chasing.
else
src.attack() //call the attack function. You are going to have to define this.
spawn(src.attack_delay)
src.pursue() //after X milliseconds, keep chasing or attacking.
else //if out of range, or out of view.
src.target = null //give up on target
src.find_target() //try looking for another target.
find_target()
var/mob/being/player/found //store the next target locally.
var/fdst = src.chase_range+1 //next target's distance (starts higher than potential range for ease of use)
for(var/mob/being/player/tgt in view(src,src.chase_range))
if(tgt.client) //if is a player.
if(get_dist(src,tgt)<fdst) //compare distances to find the closest potential target.
found = tgt
fdst = get_dist(src,tgt)
if(found) //if there is another target.
src.target = found //set next target
src.pursue() //being pursuit!
return 1
return 0 //switch off
In response to Ter13
Okedokey. I will do that now. I will get back to ya once I get it in and compile. Thanks for the patience!! :D

~Update~ I got it in and it compiles fine. I just need to find out if they are actually attacking or not XD I don't have a death thing for my guy, or a respawn or anything. so i don't know if they are attacking me.
In response to Blanke
Debug text is your friend!

proc/attack()
src.target << "[src] pokes you in the [pick("eye","ribs","nose","ear","stomach","potato")]!"


=D
In response to Ter13
But it clashes with


    verb
attack(mob/M as mob in oview(1))
if (M.HP <= 0) //if M's HP are at or below 0...
usr << "[M] is already dead!"
else //otherwise...
usr << "You attack [M]!"
oview() << "[usr] attacks [M]!"
var/damage = rand(1,10)
world << "[damage] damage!"
M.HP -= damage
M.DeathCheck()


(I hope i did the coding thing right there) I know that code is probably bad.. but I get errors whenever I put in the thing you mentioned.
In response to Blanke
The problem is, rather than merging my code with yours, you are trying to just slap my snippet in there.

You've got to insert the functional portion of my snippet into your snippet. Try putting the "src <<" line at the top of the attack proc.

Plus, my monster loop isn't going to work right for you, because you already have a verb defined under /mob as attack... Mine depends on you making your own attack proc under the mob/NPC/monster chain... You're gonna have issues with what you have now.
In response to Ter13
ooo I get most of that. one sec lemme edit this post :p
Page: 1 2