ID:173308
 
I need an example of a bump proc for a certain game I am making. The kind of example I am looking for is the kind where you bump an obj and it goes a certain distance. Please help me out if you can!

Thank you,
Jeff Talscor
Bump(obj/o as obj)
o.loc = locate(*,* - 2,*)

i think thats how its done but i dont use bump that much you will prob need to edit it a bit and add a check in (is new to byond to)

In response to Madpeter
Thanks, but, that didn't work. It came up with 4 error messages which I didn't know how to fix. What I need is something like this:

obj/bump(o as obj)
o(puck).loc = (*,* - 2,*)

something like that, that actually tells what the obj is. Still, thanks for the coding anyway!

Thank you,
Jeff Talscor
In response to TerminatorX13
mob//a mob
Bump(obj/WHATEVER/O)
if(istype(O,/obj/Whatever) O.step(src.dir)
else ..()
In response to Airjoe
Airjoe wrote:
mob//a mob
Bump(obj/WHATEVER/O)
if(istype(O,/obj/Whatever) O.step(src.dir)
else ..()


I am sorry but what does this actually do ??
In response to Madpeter
As usual, this should be handled by writing your own Bumped() proc. Follow along, now:
atom //Everything that you can place on the map is an atom (except for image objects)
proc
Bumped(atom/movable/A) //Bumped() declaration here. The argument is an atom/movable

atom/movable
//Obviously, an atom/movable is the type of atom that can move.
//Turfs can't move, and areas can't move. Thus, objs and mobs are atom/movables.
Bump(atom/A) //It's already been declared, so no "proc" before it.
A.Bumped(src) //Obviously, we want to call A.Bumped().
//A is an atom type, and we defined the Bumped() proc for atoms. If it's not mappable, you can't bump into it.
//This is all this proc will do for now.

var/tmp/speed = 0 //How much strength is behind something's movements.
//Technically, we should have both mass and speed, but we combine them into one variable for simplicity.
//Notice it's var/tmp/speed? That means "Don't save this variable!". This is an important thing to remember if you save anything.
//If, for example, you have a mob proc that will set their invincibility to 1, wait 10 seconds, then set it to 0, what happens if they log out and it saves at 1?
//Easy: they're invincible forever!

proc/adjustSpeed(var/N) //We'll use this to slow things down / speed things up.
speed = max(speed+N, 0)
return 1 //Let the caller know that we've adjusted our speed. This proc could be changed to return 0 if the object is frictionless, and thus can't slow down.

obj/ball //This is what we'll be bumping into.
//Technically we need a variable for mass, to provide a more accurate physics simulation
//However, that's a bit excessive.
proc
RollinRollinRollin()
//Here we will tell the ball to roll along its merry way.
while(speed > 0) //We'll move until we've slowed down too much
step(src,dir) //Step in the direction of movement
sleep(min(1, 11 - speed))
//We'll sleep for at least one tenth of a second (1 tick). Terminal velocity.
//However, the lower speed is, the longer the wait. At most, it's a bit over 1 second.
Bumped(atom/movable/A) //No proc beforehand, because we already defined it for all atoms, and an obj/ball is an atom.
//Now, when we get bumped by something, what do we want to do?
//Simple! Steal their speed! Just like if you ever played marbles, marble A hits marble B, B goes flying off, A stops (not completely, if it's not a perfect hit)

var/D = get_dir(A, src) //Get the direction we were bumped from. This is not always the same as A.dir.
if(D == dir) //If it's in the same direction, then we want to ADD the speeds.
speed += A.speed
else //Otherwise...
dir = D //Align in correct direction. Again, a simplification.
speed = A.speed //This time, our speed is simply set to their speed.
A.speed = 0 //Now they should stop moving.
spawn()
RollinRollinRollin() //Now, start our movement.
//Notice I used spawn(). That means that the computer is doing two things at once now:
//What's indented past spawn(), and the rest of the proc. Of course, this proc is done. So what's the point, then?
//Bumped() is called from Bump(), so it allows Bump() to finish without having to wait for RollinRollinRollin(), which allows Move() to finish without waiting, and so on.

Bump(atom/A) //Now, what if we bump into something?
if(isturf(A))
//If we bump into a turf, it's a reasonable assumption that it's a wall of some sort
//And what do balls do when they hit walls? BOUNCE!
//This next part determines whether the direction is cardinal (NSEW) or diagnol.
if(dir in list(NORTH, SOUTH, EAST, WEST))
//Then we can simply turn around
dir = turn(dir,180)
else
/*Then we have a more difficult task. Here's a diagram, ball is moving northwest in all examples:
XX OR X
XO O New dir: southeast

XX
O New dir: southwest

X
XO New dir: northeast

So, we first get the two cardinal directions we're moving in (diagnol is a combination of two directions): */

var/NSdir = (dir & (NORTH|SOUTH))
var/EWdir = (dir & (EAST|WEST))
//Make sense? Didn't think so.
//Now, we find if there are dense turfs (or no turfs) in these two directions.
var/NSturf = get_step(src, NSdir)
var/EWturf = get_step(src, EWdir)
var/newdir = dir
if(!NSturf || NSturf.density == 1)
//If it is null, or if it's dense. Though you can't check the density of something that's null, the program realizes
//"Well, the first is true, so even if the second is false, I will still do whatever, so I won't even bother checking the second one."
//That's called short-circut evaluation.
//Anyway, we'll want to flip around the north/south direction here.
newdir^=(NORTH|SOUTH)
//This too is confusing.
if(!EWturf || EWturf.density = 1)
newdir^=(EAST|WEST)
//If there's nothing to bounce off of except for what was Bump()ed into, then newdir is still the same as dir, so we should flip the direction.
if(dir == newdir)
newdir = turn(newdir, 180)
dir = newdir

//And that covers all four cases. The ball now bounces off of walls.

else //If it's not a turf
speed = 0 //Then we'll just assume it was stopped. A player could block it, and other things have shapes more complex than a flat surface.

turf
var/friction = 0.5 //This slows down things moving over it.
Entered(atom/movable/A) //Called once something steps on the turf
//Obviously, we want to slow the thing down, depending on friction.
//However, we have to take some things into consideration. What if it's actually flying, floating, hovering, or just plain in the air?
//So, we'll call a proc to tell it HOW MUCH it should slow down, and let it decide whether it should or not.
A.adjustSpeed(-friction)

mob
//Players act differently from balls, obviously. The friction of a surface doesn't affect them, except in the case where it's completely frictionless.
var/tmp/canMove = 1

var/baseSpeed = 10
var/accel = 2
var/maxSpeed = 10

//These three variables are strange, so I'll explain them.
//baseSpeed is how many ticks there will be between moves. speed is subtracted from it, though.
//accel is how much speed increases every time you move.
//maxSpeed is REALLY strange. It's how many ticks the bonus from accel will remain. So, you move, accel gets added to speed, then maxSpeed ticks later, accel gets SUBTRACTED from speed.

proc/Slide(var/Dir) //Called in order to slide the player along when they hit a frictionless surface. D is the direction to slide.
var/S = speed //speed will drop as we slide, but we should move at a constant rate, so we have a variable for our speed when we started.
if(Dir == 0)
Dir = dir
var/turf/T = loc
while(isturf(T) && T.density == 0)
sleep(baseSpeed - S)
T = get_step(src,Dir)
Move(T, 0, 1) //Move no matter what. The second argument to Move() is Dir. If it's 0, then change dir to match the movement. Otherwise, set dir to whatever it is.

proc/accelerate() //Called by Move() to increase speed, then decrease it soon after.
var/A = accel //In case accel changes mid-proc.
speed += A
sleep(maxSpeed)
speed -= A
speed = max(speed, 0) //Make sure it's not negative.

proc/moveDelay() //Handles the delay between moves.
canMove = 0
sleep( min( (baseSpeed - speed) , 1 ) ) //Wait at least 1/10 of a second.
canMove = 1

Move(atom/newloc, var/Dir = 0, var/override = 0)
//Remember, we can move into any atom, but without calling Move() in the code to move us into something else, we can only Move() into turfs.
if(override == 1 || !loc) //The override argument allows us to just go ahead and move no matter what.
//Of course, if loc is null, then Login() is probably calling move, so we should let it place us on the map.
return ..()
if(isturf(newloc))
if(canMove) //If we can move...
if(loc.friction == 0)
return 0 //No friction, no movement.
var/turf/T = newloc
if(newloc.friction == 0)
if(..()) //If we managed to move onto the frictionless turf
spawn() Slide(Dir) //Start sliding
return 1
else //Otherwise, we get to move normally.
. = ..() //Do what the default Move() action is, then set . to what is returned. . is a variable that gets returned automatically when a proc ends.
spawn() accelerate()
spawn() moveDelay()
else //If we can't move
return 0
else //If we're not moving into a turf, then just do what we normally do.
return ..()

adjustSpeed(var/N)
//Players don't get slowed down by friction, except in extreme cases. Anything less than 10 friction is simply ignored.
//10+ friction is reserved for surfaces that are sticky, rather than simply rough. Like glue.
if(N < 0 && N > -10)
return 0 //Do nothing
else
return ..() //Otherwise, just do what we would normally do.

/* Below are some examples of cool things that you can do */

turf
ice
friction = 0 //That's it! That's all you need to do to make ice that things slide on!
mud
friction = 1 //Mud gets things stuck.
glue
friction = 10 //Quite sticky, stops most things.

ramp
var/steepness = 2
Entered(atom/movable/A)
if(A.dir == src.dir)
A.adjustSpeed(steepness)
..()
else if(turn(A.dir, 180) == src.dir)
var/V = A.adjustSpeed(-steepness) //V will be 1 if adjustSpeed() lets whatever entered to slow it down.
..()
if(V && A.speed == 0) //If we've stopped it, and it's allowing us to slow it down...
A.dir = src.dir //Turn it to match our direction
A.adjustSpeed(steepness) //And then send it off again.
northRamp
dir = NORTH
southRamp
dir = SOUTH
eastRamp
dir = EAST
westRamp
dir = WEST
//etc.


And that is your rather robust ball-kicking system. It also implements a movement delay system, because they're linked together rather importantly.

Also, notice that I don't use usr anywhere there. That's because usr is not to be used outside of procs.

Any errors in that code snippet I apologize for, and I hope someone will correct me.

I've spent a while typing this up, so I expect a simple favor from you:

If you don't understand ANY portion of this, ANY PORTION AT ALL, then you MUST ask me what it does. Do NOT use this if you do not understand ALL of it. This goes for anyone reading this, too. If ANYONE wants ANYTHING in here clarified, just ask..

Finally, I'll explain the parts that are rather confusing.

newdir^=(NORTH|SOUTH)

Now, I'm using ^=, which is a contraction. What it would read, if it were expanded, is this:

newdir = newdir ^ ( NORTH | SOUTH )

Still doesn't make much sense though, so I'll explain.

First of all, you must realize that all data is stored in binary format. 1's and 0's. It seems confusing, but it really isn't. With the decimal system, we start at 0, and when we reach 9, we add 1 to the next digit, and start back at 0. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, etc. Makes sense. Binary is the same, but instead of counting to 9 before adding 1 to the next digit, we only count to 1. So: 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1100, 1101, 1110, etc.

Now, the operators: | is the binary OR operator. That means we take the number on the left, put it on top of the number on the right, and the result is a combination of the two, where for each digit that's 1, that same digit in the final answer will be one. So, 11010 | 01100 would be:
11010
01100
-----
11110
Imagine the 1's being lasers shining down, and the 0's being darkness. Lasers on the bottom block lasers on the top, but darkness doesn't block the lasers. Silly analogy.

Anyway, so now we have NORTH | SOUTH. Now, I'll point out how directions work. NORTH is 1, SOUTH is 2, EAST is 4, WEST is 8. 1, 10, 100, 1000 in binary. The other directions, NORTHWEST, SOUTHEAST, etc. are just a combination of the two cardinal directions. NORTHWEST is the same as NORTH | WEST. Specifically, it's 9, binary 1001.

So, what is NORTH | SOUTH? It's 3. That's not a direction. How can you be facing both north and south at the same time? Still, it's important for our next step.

newdir ^ (NORTH|SOUTH)

^ is the XOR, or EXCLUSIVE OR operator. It is similar to |. The difference, however, is that two 1's on top of each other cancel each other out. So, 11010 ^ 01100 would be:
11010
01100
-----
10110
See the difference? Essentially, it acts like a switch. You take the first number, and for every 1 in the second number, you toggle that digit in the first number, for every 0, you leave that digit the same. So, 1^0 = 1, 0^1 = 1, 0^0 = 0, 1^1 = 0.

So, what does that have to do with our invalid direction of NORTHSOUTH (3)? Well, 3 is binary 11. ^ will toggle the binary digit (bit is short for Binary digIT) whenever there is a 1. The dir var is 4 bits, WESN. If N is set, then it will be unset. Since both N and S won't be set under normal circumstances, that means S is unset, and will now be set. Essentially, we're swapping the north / south direction.

Here's another confusing line:

var/NSdir = (dir & (NORTH|SOUTH))

Looks similar, doesn't it? This time, instead of ^, we have &. Can you guess what & does? We already have an operator which takes either or both bits being 1, one that takes either but not both bits being 1, so logically, this one needs BOTH bits to be one.

Essentially, what this line is doing is determining if we're moving north or south. We already know we're moving diagnolly, so we want to ditch the E and W bits (remember, NSEW). So, WESN & 0011:
WESN
0011
----
00SN
So, we're left with either 0001 (NORTH) or 0010 (SOUTH). We could be left with 0011 (NORTHSOUTH), but that would be in a weird case, and you would have to do it quite intentionally.

And that covers the silly little bitwise operators I was using.

I might add that there are two more commonly used bitwise operators: << and >>. Yes, the same things used for input/output. They have a simple purpose: A >> B shifts A's bits B spaces to the right, A << B shifts them to the left. These have the same effect as A / 2 ** B (** being the power operator, so 2 to the Bth power), and A * 2 ** B, respectively. The one difference is that when dividing, decimals get truncated. That means you lose the decimal. No rounding up because it's .5, it's just chopped off.

Remember, if there's anything you don't understand, just ask. Don't use this code snippet if you don't get it what it does. Help me help you help everyone else. That is, help me by telling me you don't understand so I can help you understand so you don't make ignorant mistakes due to not understand it so that you help everyone else not have to deal with your problems which wouldn't be there if you understood your program in the beginning.

Oh, and sorry for breaking the H-scroll.