That'd work too, but I hate those pseudoverbs.

They encourage all kinds of badness.
Would this also work with a Click to Move function instead?
Zasif wrote:
Would this also work with a Click to Move function instead?

Let's add a function:

#define MAIN_MAP "map1" //you only want clicks to work on the main map element. You should change this define to the id of your main map
#define TARGET_LOSE_THRESHOLD 16 //don't bother chasing something if it is more than 16 tiles away.

#define

client
var
atom/move_target
Click(atom/movable/object,location,control,params)
. = object.Click(location,control,params // by default, atom.Click() doesn't return anything. If you want the atom's Click() function to not trigger a click-to-move and instead consume the client's Click, return 1 in that atom's Click() function.
if(!.&&control==MAIN_MAP&&object.z&&!move_keys) //ensure object is on the map, and not in the screen or in a statpanel/grid. Also ensure the client isn't currently attempting to move using the movement keys.
if(istype(object))
move_target = object
else
var/turf/t = object
if(!istype(t))
var/list/l = params2list(params)
l = screen_loc2num(l["screen-loc"]) //from DM.StdLib
t = locate((screen_x+l[1]-1)/TILE_WIDTH+1,(screen_y+l[2]-1)/TILE_HEIGHT+1,eye:z) //get the turf location; uses BYOND 509 feature that is currently broken.
if(!t) return
move_target = t
if(!move_loop)
MoveLoop()


Now let's change the tile-based MoveLoop function:

    proc
//this drives movement behavior.
MoveLoop()
if(move_loop) return //only call once while active
var/x,y
move_loop = world.time
//repeat while move_keys are being held down
var/turf/t
var/tdist
var/tdir
while(move_keys||move_target) //add a clause for click movement
if(move_dir)
t = get_step(mob,move_dir)
if(t)
Move(t,move_dir)
if(move_target)
move_target = null
else if(move_target) //this branch handles click movement
tdist = get_dist(src,target)
if(!tdist||tdist>TARGET_LOSE_THRESHOLD||target.z!=mob.z) //if the target is already in range, out of range or z is not equal to mob's
move_target = null //remove the move_target
else //otherwise, chase it like a dummy
tdir = get_dir(src,target)
if(!Move(get_step(src,tdir),tdir)) //stop chasing when an obstacle is encountered
move_target = null
sleep(TICK_LAG)
move_loop = 0


Lastly, we need to modify one other function ever so slightly:

verb
//called when a movement key has been pressed.
MoveKey(dir as num,state as num)
set hidden = 1
var/opp = turn(dir,180)
var/pos = log(2,dir)+1
if(move_target) //if we currently have a move target, pressing any move keys will stop chasing that target
move_target = null
if(state&&++key_state[pos]==1)
//key track of the keytap state
if(world.time-key_time[pos]>TAP_THRESHOLD)
key_taps[pos] = 1
else if(last_key==pos)
++key_taps[pos]
else
key_taps[pos] = 1
last_key = pos

//keep track of the move keys and direction
move_keys |= dir
if(move_keys&opp)
move_dir &= ~opp
else
move_dir |= dir
//store the time of the event
key_time[pos] = world.time
//this is an example of handling double-taps for movement
/*if(move_keys==dir&&key_taps[pos]==2)
mob.Dash(dir,0,pos)*/

else if(!state&&--key_state[pos]==0)
//this is a key release
move_keys &= ~dir
if(move_keys&opp)
move_dir |= opp
else
move_dir &= ~dir
//store the time of the event
key_time[pos] = world.time
//attempt to call MoveLoop if there are keys being held down
if(move_keys&&!move_looping)
MoveLoop()



As for making it work with pixel movement, that's a whole 'nother ball of wax.
In response to Ter13
If a game would be based around Click to Move and Do most stuff with Mouse Click and also reach over 100+ players at once doing stuff, what would you recommand Pixel movement or Tiled movement?
okay thank you ter!
its going to take me a while to learn your code fully so I can learn off it. As there is quite a few variables and what not. Like I understand most of it, just got to think about some of the details.

I also had no idea you could set parameters inside of macros through the dmf file. That was a shocker for me and probably a tad limiting. Of course an idea like this would have been limited from the get go.
also this

~opp

what does that signal mean
If a game would be based around Click to Move and Do most stuff with Mouse Click and also reach over 100+ players at once doing stuff, what would you recommand Pixel movement or Tiled movement?

Tiled movement is obviously going to be more robust in that case.
In response to Ter13
Thank you!
I also had no idea you could set parameters inside of macros through the dmf file. That was a shocker for me and probably a tad limiting. Of course an idea like this would have been limited from the get go.

What? I don't understand, what's limited?

~opp. what does that signal mean?

It's a binary operator that's sort of similar to "!".

"!" means logical not. Logical not will take a true value and return false, or a false value and return true.

"~" is binary not. It takes the binary value of opp, and flips all the bits. BYOND only has access to the bottom 16 bits of a number, so a Binary not would look like this:

Before: 0010010001101011 (value: 9323)
After:  1101101110010100 (value: 56212)


For more information:

http://www.wordsmuggler.com/Learn/Binary
In response to Ter13
Ter13 wrote:
> #define TICK_LAG 0.25 //set this to 10 divided by world.FPS
>
> //this is where you would define your binds. Simply define them as numbers starting at 1, then update the BindKey() verb as necessary to handle behavior.
> #define BIND_JUMP 1
> #define BIND_INVENTORY 2
>
> #define TAP_THRESHOLD 2.5
>
> client
> var/tmp
> move_keys = 0 //stores the actual state of the movement keys
> move_dir = 0 //stores the direction the client is trying to move the mob in
> move_loop = 0 //stores the current move_loop's starting time
>
> last_key = 0 //stores the last key that was actually pressed
> //these lists must be as long as 4+ the number of binds you define.
> list/key_state = list(0,0,0,0,0,0) //stores the on-off state of each registered key
> list/key_time = list(-1#INF,-1#INF,-1#INF,-1#INF,-1#INF,-1#INF) //stores the time that the registered key was pressed or released
> list/key_taps = list(0,0,0,0,0,0) //stores the number of times a key has been pressed in sequence. This allows for dealing with double and triple taps.
> proc
> //this drives movement behavior.
> MoveLoop()
> if(move_loop) return //only call once while active
> var/x,y
> move_loop = world.time
> //repeat while move_keys are being held down
> var/turf/t
> while(move_keys)
> if(move_dir)
> t = get_step(mob,move_dir)
> if(t)
> Move(t,move_dir)
> sleep(TICK_LAG)
> move_loop = 0
> verb
> //called when a movement key has been pressed.
> MoveKey(dir as num,state as num)
> set hidden = 1
> var/opp = turn(dir,180)
> var/pos = log(2,dir)+1
> if(state&&++key_state[pos]==1)
> //key track of the keytap state
> if(world.time-key_time[pos]>TAP_THRESHOLD)
> key_taps[pos] = 1
> else if(last_key==pos)
> ++key_taps[pos]
> else
> key_taps[pos] = 1
> last_key = pos
>
> //keep track of the move keys and direction
> move_keys |= dir
> if(move_keys&opp)
> move_dir &= ~opp
> else
> move_dir |= dir
> //store the time of the event
> key_time[pos] = world.time
> //this is an example of handling double-taps for movement
> /*if(move_keys==dir&&key_taps[pos]==2)
> mob.Dash(dir,0,pos)*/

> else if(!state&&--key_state[pos]==0)
> //this is a key release
> move_keys &= ~dir
> if(move_keys&opp)
> move_dir |= opp
> else
> move_dir &= ~dir
> //store the time of the event
> key_time[pos] = world.time
> //attempt to call MoveLoop if there are keys being held down
> if(move_keys)
> MoveLoop()
>
> //called when a registered bind has been pressed
> BindKey(key as num,state as num)
> set hidden = 1
> var/pos = key+4
> key_state[pos] = state
> //this is a keypress
> if(state)
> last_key = pos
> //keep track of double, triple, quadruple, etc taps
> if(world.time-key_time[pos]>TAP_THRESHOLD)
> key_taps[pos] = 1
> else
> ++key_taps[pos]
> switch(key)
> //these are just examples, use your game's binds and functions here.
> if(BIND_JUMP)
> if(state)
> switch(move_dir&(EAST|WEST))
> if(EAST)
> mob.Jump(60)
> if(WEST)
> mob.Jump(120)
> else
> mob.Jump(90)
> if(BIND_INVENTORY)
> if(state)
> if(interface.inventory.showing)
> interface.inventory.Hide()
> interface.equipment.Hide()
> else
> interface.inventory.Show()
> interface.equipment.Show()
>
> North()
> South()
> East()
> West()
> Northeast()
> Southeast()
> Southwest()
> Northwest()
>

This is how you define macros:
> W        MoveKey 1 1
> W+UP MoveKey 1 0
> S MoveKey 2 1
> S+UP MoveKey 2 0
> D MoveKey 4 1
> D+UP MoveKey 4 0
> A MoveKey 8 1
> A+UP MoveKey 8 0
>
> SPACE BindKey 1 1
> SPACE+UP BindKey 1 0
>
> I BindKey 2 1
> I+UP BindKey 2 0
>

This setup will keep track of several things for you:

1) The number of taps in a sequence. (client.key_taps)
2) The time that the key was last pressed/released (client.key_time)
3) Whether a key is currently held down. (client.key_state)

client.MoveKey() and client.BindKey() is where most of the magic happens. By default, it modifies the current state of the key lists, then the functions are all about being overridden and changed.

Pay close attention to the lists in the client variables. If you add bindkeys, you need to change the length of the list to match the maximum number of keys that it can track.

Moving diagonaly seems to have a skipping effect and not smooth. Wondering if this is a bug!
Was wondering if there is a way to disable diagonal movement for the Click to move function!
Page: 1 2