ID:2175784
 
(See the best response by Kaiochao.)
Code:
turf
Water
icon = 'Water.dmi'
Enter()
if(!usr.flight)
return 0
else
return 1


Problem description:
Once a player has stopped flying (the variable is changed to 0/untrue), they can still move several steps before stopping. How should I resolve this? Is there a better way to do the above?

Here is a video showing the problem.



usr is not applicable to this proc. The first argument given to Enter() is the object attempting to enter, so use that.
turf
Water
icon = 'Water.dmi'
Enter(atom/movable/M) // Any movable can be given here.
if(ismob(M))
var mob/mob = M // We need to use a mob variable, so we cast M to a new variable with type /mob.
return mob.flight // Allow flying mobs to enter.
// What should you return for non-mobs?
In response to Kaiochao
This is some helpful advice, thanks! I kind've forgot you can use "return" for a user's variables. Though, the issue seems to be related to my step_sizes, which are 8 for obj/mobs.
In response to E52680
Oh, right, pixel movement.

turf.Enter() is called when a mover attempts to overlap a turf, but since turfs are tiles, their edges are always a fixed distance apart. You can move around within a turf without Enter() or Exit() being called at all.

So, what do you want to have happen? Here's one way of telling if you're overlapping water at all:
// Returns a /turf/Water if overlapping one (or more), or null if not.
mob/proc/OverWater() return locate(/turf/Water) in locs

Completely restrict player movement if overlapping water?
mob/Move()
if(OverWater()) return FALSE
return ..()

Maybe you should instead disable landing while overlapping water?
mob/verb/Fly()
if(flight)
if(OverWater()) src << "You can't land while over water!"
else // Land normally
else // Start flying


I kind've forgot
You "kind have" forgot? :P
In response to Kaiochao
You "kind have" forgot?

listen here u lil cunt

Thanks for the help! Though, I want them to be able to land on water for a variety of reasons, so instead I did the following:

mob
Skills
verb
Fly()
set waitfor = 0
set category = "Skills"
usr << output("You have attempted to enter flight.","chat")
if(state != "state_flying")
state = "state_flying"
state2 = null
icon_state = "Flight"
else
if(overWater())
state2 = "state_water"
nullState()
flight = 0
while(state == "state_flying")
if(energy <= 10)
flight = 0
usr << output("You have run out of energy.","chat")
nullState()
else
usr << output("You are flying and have [energy] energy.","chat")
flight = 1
energy -= flightMod * 5
sleep(10)


And the movement stuff.

var/list/moveStates = list("state_flying")
var/list/freezeStates = list("state_training","state_water")
var/list/stopStates = list("state_ko","state_frozen")
mob/var/state = "state"
mob/var/state2
mob
Move()
if(freezeStates.Find(state || state2) || stopStates.Find(state || state2))
return 0
else
..()
proc
nullState()
icon_state = null
state = "state"
overWater()
return locate(/turf/Water) in locs


This is probably not the greatest, but it's a fix I came up with in a few seconds. If you have any suggestions, go ahead.
In response to E52680
Best response
Quick suggestions:
* Return ..() in Move(). It's supposed to return something, according to the Developer Reference, so it's best not to break that assumption.
* Instead of using strings and lists for states, consider using bit flags:
#define NULL_STATE 0
#define STATE_FLYING (1 << 0)
#define STATE_TRAINING (1 << 1)
#define STATE_WATER (1 << 2)
#define STATE_KO (1 << 3)
#define STATE_FROZEN (1 << 4)

// nice and compact combinations
#define MOVE_STATES (STATE_FLYING)
#define FREEZE_STATES (STATE_TRAINING | STATE_WATER)
#define STOP_STATES (STATE_KO | STATE_FROZEN)

mob
var
state = NULL_STATE
state2 = NULL_STATE

Move()
// nice and compact comparisons
if((state || state2) & (FREEZE_STATES | STOP_STATES))
return FALSE
return ..()

proc
nullState()
icon_state = null
state = NULL_STATE

overWater()
return locate(/turf/Water) in locs

mob
Skills
verb
Fly()
set waitfor = FALSE
set category = "Skills"
src << output("You have attempted to enter flight.", "chat")
if(state != STATE_FLYING)
state = STATE_FLYING
state2 = NULL_STATE
icon_state = "Flight"
else
if(overWater())
state2 = STATE_WATER
nullState()
flight = FALSE
while(state == STATE_FLYING)
if(energy <= 10)
flight = FALSE
src << output("You have run out of energy.", "chat")
nullState()
else
src << output("You are flying and have [energy] energy.", "chat")
flight = TRUE
energy -= flightMod * 5
sleep 10

The only caveat with using bit flags is that you can only have up to 16 states, including the null state, and not including the state groups e.g. STOP_STATES.

Also, due to the nature of #define, they only exist in code that comes after their definition, so it's best to put them in a header file that gets compiled first, or manually include the file they're defined in before you need them. For the latter, if the file is called "Movement.dm" and the file that needs to use the states is in a file called "Skills.dm", then at the top of Skills.dm you would write:
#include "Movement.dm"


Aside from the usefulness of the bit-wise operations, giving identifiers to your states lets the compiler know what the valid states are. If you accidentally set state to "state flying", it might be hard to notice where the problem is, but STATE FLYING will give a compiler error.
In response to Kaiochao
I'm wondering, what exactly the numbers are for? So, for example (1 << 3). Also, I'll only need up to 10 states, so that's good.
In response to E52680
Here's a nice interactive tutorial introduction to bitwise operators:

https://www.codecademy.com/courses/ python-intermediate-en-KE1UJ/0/1

Not all of it is relevant to DM (DM doesn't have a bin() or int() or 0bXXX "binary literal" syntax), but the stuff with the operators and masking is.
In response to Kaiochao
Thanks for your help, then! I'll now wait for my other topic to be answered, why the hell there is all this rubberbanding with client-side FPS ;-;.