ID:164448
 
I'm creating a dungeon crawling RPG like Nethack, Rogue, etc... and I need the enemies to only move when the player moves or takes an action.

I understand how the step_rand(ref) works but putting it into practice in the way described way is bugging me. I tried to put it in an IF to check whether the player is moving - if(usr.Move(1)) - but that comes up with an error involving Enter.null. Anyone got any ideas?

Thank in advance
- Milo
You can make enemies move by directly overwriting the Move() proc... but ony if you know what you are doing (meaning, read about Move() in both DM Reference [F1] and DM Guide... this is very important if you do not want to have a headache such as why an atom is unable to move if you modify Move())
You can probably do something like:

mob
Move()
..()
for(var/mob/monster/M in view())
step_rand(M)


Of course that's the simple version, I leave it up to you to do the fun stuff.
In response to Nadrew
Nadrew wrote:
You can probably do something like:
mob
Move()
..()
for(var/mob/monster/M in view())
step_rand(M)


Probably want to make this for mob/player or something, since when all those monsters step_rand(), they'll also be calling Move(), and you'll have one hell of a loop. I'd definitely set this up through the Move() proc though, like Nadrew has.
In response to Nadrew
Ok, Nadrew, here's how I implemented your code:

    Move()
..()
for(var/mob/enemies in view())
monster_move()


By the way, monster_move() is a proc for testing whether the monster is an NPC. Here it is:

        monster_move()
if(src.key)
return
else
step_rand(src)
mob
proc
Wander()
sleep(10)
step_rand(src)
src.Wander()

When you want certain mobs to walk automatically
New()
..()
Wander()

That's how I got my npcs to move it just takes a little longer coding.
For a more roguelike feel, I'd suggest an action schedule. Each mob will schedule their next action, and the schedule will proceed only when the player schedules something.

Naturally: this is going to be complicated. You should've expected this when setting forth to create a roguelike.

var/list/actionSchedule = list()    //list containing times for actions to be taken
var/list/actors = list() //list containing mobs that will take actions
//actionSchedule and actors are to be maintained in parallel, with actionSchedule[n]
//being the time that actors[n] will take an action
var/worldtime = 0 //the world's time, in arbitrary units

proc
scheduleAction(var/mob/M, var/time) //schedules an action for M in time units of time
time = worldtime+time //we're scheduling it for time units of time LATER, so add it to the current time
var/index = 1
var/length = length(actionSchedule)
while(index < length && time >= actionSchedule[index])
//Here, we're going through actionSchedule to find a spot where we can insert the action, in order
// >= instead of > is used so that actions scheduled first are executed first
index++
actionSchedule.Insert(index, time) //insert the time into the schedule
actors.Insert(index, M) //insert the mob into the schedule

advanceTheClock() //Let mobs take actions until it becomes the player's turn, then stop
if(length(actionSchedule) == 0) //if there are no actions scheduled, then return
return
var/mob/M
do
worldtime = actionSchedule[1] //set the time to when the first mob would take its action
M = actors[1] //grab the lucky mob
actionSchedule.Cut(1,2) //remove the first scheduled action
actors.Cut(1,2) //and the mob
while(!M.takeTurn() && length(actionSchedule) > 0)
//the loop continues until we run out of actions (this is bad)
//or the mob that took its turn is a player, which causes takeTurn()
//to return 1 (this is good)


mob
proc
takeTurn()
//The children of this procedure would contain the AI routines for a mob to take their turn
return 0 //return 0 when not a player
player
takeTurn()
//For the player, we would put something that would unlock their ability to take actions
//because the player should only be able to make a move when it's their turn
return 1 //return 1 when a player


(actually, that wasn't as bad as I was expecting)

And that's the groundwork for what you need. Now: whenever the player takes an action, add the time of their next move to the schedule (if the action takes 20 units, then scheduleAction(usr, 20)). Then, lock the player's actions (to be unlocked later, in takeTurn()), and call advanceTheClock().

For mobs besides the player, takeTurn() will have the AI perform one action, such as moving or attacking, then scheduling the next action. Additioanlly, all non-player mobs should do a scheduleAction() call, so that they're active. If a mob isn't in the action schedule, then they won't do anything. You can, of course, exploit this to keep only the mobs you want to be doing anything (as in, they're on the same level as the player) in the schedule, disabling the AI of any other mobs.

And, other important detail: because worldtime is totally arbitrary, you can decide how to use it. A step could be 1, or it could be 1000. It doesn't have to have anything to do with ticks.