Graal Forums  

Go Back   Graal Forums > Development Forums > NPC Scripting
FAQ Members List Calendar Today's Posts

Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old 03-13-2010, 08:44 PM
DustyPorViva DustyPorViva is offline
Will work for food. Maybe
DustyPorViva's Avatar
Join Date: Sep 2003
Location: Maryland, USA
Posts: 9,589
DustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond repute
Send a message via AIM to DustyPorViva Send a message via MSN to DustyPorViva
[tutorial]How to script a movement system

Well, maybe this isn't quite a tutorial... or at least, I'll try to make it one!

I've posted a few movement systems before, but I have never really went in depth as to how I made them or the methods I use. I just provide a script... and that's not conductive to a learning environment. So here I will provide a script with lots of comments, and also go a little in-depth as to how I did things.

This movement isn't quite as barebones as previous ones I have posted. It does have things like some basic modes(pulling/pushing,swimming...), and then some(functions for replacing ganis, applications of things I've learned over the years).

First, I'm going to explain the method of wall collision I use. I use a simple dual-box check. This means I am checking two boxes in the direction the player is moving with onwall2's. This means it checks the whole distance of the player's size, and provides the necessary data for sliding(hitting one side of the player and not the other). Whenever I do this I sketch out a very easy diagram to understand what I am trying to accomplish.

I create two checks. CheckOne, CheckTwo. Each denote a box check in front of the player. There are important things to know.
1) the player's "true" size is 3x3 tiles, starting at... of course, player.x and player.y
2) However, the player is graphically starting at player.x+.5 and player.y
3) The collision box of the player is at player.x+.5 and player.y+1
4) The 'center' of the player is player.x+1.5 and player.y+2. This is where things like tile detection are done.
The above are all explained more easily in this picture, which also explains the box layout I use:

The white transparent box is where we determine the player's collisions should be. The blue outline shows what Graal consider's the full player's size.
Now each yellow and red box are checks that are determined in the script. However, only two are done at a time: only in the direction provided. This is not fully accurate, though... the boxes are not always 16x16 pixels(a single tile) in size. They stretch themselves outwards from the player to reflect the size of the player's speed. This means if the player is traveling to the left at 3tiles/frame, the box is going to be 3 tiles in width. This means it's checking for a wall between the new position and the last position(a simpler check means it will simply skip over walls found in-between).

Here is the script I use to generate the positions of the two checks:
PHP Code:
  // CREATE CHECK POSITION FOR BOX ONE
  
temp.checkOne = {
    
player..5 vecx(k)*(speed 2),
    
player.vecy(k)*(speed 2)
  };

  
// CREATE CHECK POSITION FOR BOX TWO
  
temp.checkTwo = {
    
player.1.5 vecx(k)*(speed 1),
    
player.vecy(k)*(speed 1)
  }; 
For the first check, I initiate it at the upper-left of the collision box. If the player's direction is less than 2(to the left or up), move it outwards by their size. This is because when moving left or up, you want to displace the onwall2 check to account for the size it is going to be. This image representing a 3 speed movement can better explain:

The red + shows where the checkx/y is, and the red box represents the onwall2 check. Thus, if going to the left, it needs to be displaced 3 tiles to account for the fact onwall2 will be 3 tiles in width.
Anyways, for CheckTwo I start the check at the bottom right corner of the collision check. Then if the player is facing up or left, add a tile, or else subtract a tile(too keep it properly offset to represent the original sketch).

Now onto the wall checks itself:
PHP Code:
  temp.hitwall = {
    
onwall2(checkOne[0],checkOne[1],(k in {1,3} ? speed 1)-1/16,(k in {0,2} ? speed 1)-1/16),
    
onwall2(checkTwo[0],checkTwo[1],(k in {1,3} ? speed 1)-1/16,(k in {0,2} ? speed 1)-1/16),
  }; 
This might seem a little complicated at first, but it's actually very easy. Have an array that keeps track of the wall checks for CheckOne and CheckTwo. The positions, of course, represent the appropriate positions determined just above. The size of the onwall2 is figured out fairly easily. If moving left or right, make the horizontal check the size of the player's speed, otherwise make it the size of a tile. Do the same for up and down and moving vertically. Now one thing you'll notice is I subtract 1/16. 1/16 represents the size of a single pixel. Onwall2(x,y,1,1) is actually 17x17 pixels in size, for whatever reason. That means using just 1 will get the player stuck when they're not supposed to be, because it's checking an extra pixel(you'd get stuck trying to walk between two blocks of tiles with a 2-tile wide opening, because it would check a single pixel out of your collision box). Anyways, that bug is bad so we have to compensate.

Another important thing to do is if you DO hit a wall, account for the space between the player and the wall. If you're moving at 5tiles/frame, that means you can hit a wall way farther than your player actually is. If you just stop the player, chances are they will not be flush against the wall. Thus what we do is we run a simple loop. Now normally this is not fun, but if you take your above wall checks and make a function that will check in a given direction at the given speed, this becomes A LOT easier. Why? Because you're getting rid of redundant checks that you'd normally have to do, like checking for walls, and simplifying them. So what you do is you create a loop that starts at 1/16(explained in the script in comments), runs the length of the player's speed(to account for every pixel between the player and the wall), and increments at... 1/16! Again, this is to account for every pixel. Once you hit a wall, stop the function and use the data found(the length the loop ran), and move the player that much.
PHP Code:
  temp.0// INITIATE 'i' OUTSIDE OF LOOP, SO IT IS ACCESSIBLE AFTERWARDS
  
for (temp.i=1/16;i<player.speed;i+=1/16) {
    
temp.CheckGap CheckWall(k,i);
    
// IF WALL IS DETECTED, STOP THE LOOP TO RETAIN IMPORTANT PIXEL DATA
    
if (@CheckWall(k,i) != @{0,0}) break;
  } 
So just take 'i', and move the player in their direction that amount. What you have to do after though, is clean up the player's position. Normally doing checks like this will leave a player with some huge floating values(player.x = 5.45378234256464). So we do simple rounding for that:
PHP Code:
  // IF DIRECTION IS LEFT OR RIGHT, MAKE CHANGES TO PLAYER'S X
  
if (k in {1,3}) {
    
player.+= vecx(k)*i;  // ADD 'i' TO PLAYER'S X POSITION
    // THEN GET RID OF DIRTY FLOATING VALUES
    // AND ADD .5, SINCE THE PLAYER'S GANI X IS OFFSET .5 FROM POSITION
    
player.int(player.x)+.5;
  
// IF DIRECTION IS UP OR DOWN, MAKE CHANGES TO PLAYER'S Y
  
} else {
    
player.+= vecy(k)*i;       // ADD 'i' TO PLAYER'S X POSITION
    
player.int(player.y+.5); // ROUND OFF PLAYER'S Y TO CLEAN FLOATING VALUE
  

As the first picture established, the player's visual position is actually .5x inwards. Thus, we round the player.x and THEN add .5. For player.y, no such thing is needed so we add .5 inside the integer function to round the player.y up to the nearest whole.

Sliding is quite simple with the two-box set-up. If only ONE box is hitting a wall, then you know your player should be sliding. What you do is simply use some determining factors to decide a direction for the player to slide... then do just what you would when moving the player. Check for a wall in the sliding direction, and if there is no wall slide them! I'll do my best to explain how I calculated the directions.
PHP Code:
  // IF CHECK BOX ONE IS HITTING WALL, AND NOT BOX TWO...
  
if (player.wallcheck[0] == && player.wallcheck[1] == 0slidedir = (+ (k in {1,2} ? : -1))%4;
  
// OTHERWISE ASSUME BOX TWO IS HITTING A WALL, AND NOT BOX ONE
  
else slidedir = (+ (k in {1,2} ? -1))%4
If CheckOne is hitting a wall, and not CheckTwo is the first check we assume. Take player's direction. If player direction is LEFT or DOWN, add 1(turn counter-clockwise), otherwise subtract 1(clockwise). The %4 makes sure the player's direction is looped back around if it exceeds 3.
For the CheckTwo collission, it's somewhat similar except we are doing the opposite. If player's direction is LEFT or DOWN, subtract 1, otherwise add 1. I could have easily took all of this and made it into one big calculation, but for learning purposes I decided to keep it simple.

Then you just take the slide direction and use it!
PHP Code:
  player.+= vecx(slidedir)*player.slidespeed;
  
player.+= vecy(slidedir)*player.slidespeed;
  
player.int(player.x/player.slidespeed)*player.slidespeed;
  
player.int(player.y/player.slidespeed)*player.slidespeed
Now the last two lines are a bit important. What they do is similar to what we had to do after the wall collision. Because large floating values are bad for sliding(it can cause your player to get stuck, or 'stutter' when trying to slide into gaps), we have to get rid of them. What we do is round it to the player's sliding speed(I have it set to .25).

That pretty much accounts for movement and wall collision
However, things only get more complicated from here. You have to do various other checks to maintain the 'mode' of the player. This essentially reflects what the player's character is doing. Whether they're idle, walking, swimming, pulling...
Attached Images
  

Last edited by DustyPorViva; 03-13-2010 at 09:19 PM..
Reply With Quote
  #2  
Old 03-13-2010, 08:45 PM
DustyPorViva DustyPorViva is offline
Will work for food. Maybe
DustyPorViva's Avatar
Join Date: Sep 2003
Location: Maryland, USA
Posts: 9,589
DustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond repute
Send a message via AIM to DustyPorViva Send a message via MSN to DustyPorViva
This pretty much means what you have to do is make your script prioritize checks. Then you also have to make sure your script knows when and when it can not, apply these priorities. For example, you do a check for swimming(is the player in water?). Then you do grabbing. If you don't account for whether the player is swimming or not, your player could end up being able to grab things while in water. Hence, the swim check goes BEFORE the grab check. That way you can say, "is the player swimming? If not, proceed with grabbing! If they are, ignore even considering if the player can grab." This turns into a whooooole mess of moving things around so each check interacts with the other correctly.

There are ways to lessen the burden, though. A while ago I figured out a nifty way to set the player to idle. Normally I'd constantly set the player's mode back to IDLE at the beginning on the timeout. This worked... except eventually problems arose: movement was always taking gani priority over anything else. So if you had a command like, "/setani sit" that set your player's ani to sit while not on a chair, the script would immediately set the player back to idle. Also, if you have various modes of idle, then you had to constantly figure out which to set it back to(regular idle, carrying object idle, riding a horse idle...). So I created a function: ReturnIdle(). Whenever you want to return the player to idle mode, run it. I also made it public so that external scripts could call it(for example, I had a sign NPC that had to set the player back to idle. If I used setani("idle",null)... 1) it didn't account for the custom gani names the movement used, and 2) if the player was say... riding a horse, it would cause the player's horse to disappear until the player was done reading. I also allowed the function to account for a parameter: ReturnIdle(omitsettinggani?). If set to true, it wouldn't force set the player's ani. It would only set the player's MODE to idle. This is used in the actual movement script so loops are not being made(setani("idle",null) > setani("walk",null) > setani("idle",null) > setani("walk",null) > setani("idle",null) > setani("walk",null)). However, for external NPC's I can leave it as null, or false, and it will forcefully set the player's ani along with it(in case the player is frozen, and thus the movement script is not processing said data).

Another cool thing I figured out is the ability to replicate the replaceani() function. How? When the script is created, establish the base gani array(player.ganis). This is an array that the script uses to set the player's gani's. It contains all the arrays that reflect the various modes of the player. This is not a static copy of gani's, however. We create a copy of that array as a static, and call it "default gani's". Then, we can take the player.gani array and change the gani's as we like. When we need to, we can revert back to the back-up default gani's. It's fairly simply
PHP Code:
public function ReplaceGani(defgani,newgani) {
  
temp.ganifound this.defaultganis.index(defgani);
  if (
newgani == nullplayer.ganis[ganifound] = this.defaultganis[ganifound];
  else 
player.ganis[ganifound] = newgani;

Hmm... otherwise I can't think of much to explain. I'm not very good at tutorials. Though I will detail one more thing: enumerators. They are very important for this script(not needed, but they help so much!), as they are used to keep track of the player's mode. What they do, is they take a list of strings/words, and allow you to use them in place of integers(numbers). Prior to GS2 and enumerators, you had to do something similar with numbers. player.movemode = 0, player.movemode = 7... in the end that's very hard to keep track of. You could also do something like player.movemode = "idle", and such, but strings are harder to work with. So enumerators create the best of both worlds!

PHP Code:
enum {
  
IDLE,WALK,GRAB,PULL,PUSH,SIT,SWIM

The above means I can use the defined words in place of numbers. player.movemode = GRAB, for example. But Graal will read GRAB as 2, because it is the 2nd index in the enumerator(much like arrays, they start at 0). So if you do player.chat = player.movemode, it will echo an integer instead of IDLE/WALK and so on. But as far as reading the script goes, they make it so much more easier.

One more thing... you'll notice I use vars like player.speed to store variables. This is strictly to simplify the script. However, they are not secure! You should instead use more secure methods to store important gameplay data like the player's speed(clientr.vars).
Reply With Quote
  #3  
Old 03-13-2010, 08:46 PM
DustyPorViva DustyPorViva is offline
Will work for food. Maybe
DustyPorViva's Avatar
Join Date: Sep 2003
Location: Maryland, USA
Posts: 9,589
DustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond repute
Send a message via AIM to DustyPorViva Send a message via MSN to DustyPorViva
PHP Code:
//#CLIENTSIDE
enum {
  
IDLE,WALK,GRAB,PULL,PUSH,SIT,SWIM
}

function 
onCreated() {
  
/*      CUSTOMIZABLES       */
  // AN ARRAY OF GANIS, REFLECTIVE OF THE ABOVE ENUMERATOR
  
player.ganis = {"idle","walk","grab","pull","push","sit","swim"};
  
player.speed .6;         // THE PLAYER'S MOVEMENT SPEED
  
player.slidespeed .25;   // THE SPEED THE PLAYER MOVES WHEN SLIDING
  
player.actionspeed 1;    // SPEED ACTIONS ARE EXECUTED(PUSH,PULL)
  /* NO MORE CUSTOMIZABLES :( */

  
noplayerkilling();                // NO PLAYER KILLING = PLAYERS NOT BLOCKING!
  
disabledefmovement();             // DISABLE DEFAULT MOVEMENT
  
this.defaultganis player.ganis// MAKE A COPY OF GANIS FOR DEFAULT COMPARISON(replaceGani())
  
player.wallcheck = new[2];        // CREATE WALL CHECK ARRAY
  
player.notpush timevar2;        // KEEP TRACK OF WHEN PLAYER IS NOT PUSHING
      
  
ReturnIdle();                     // RETURN PLAYER TO IDLE POSITION

  
setTimer(0.05);
}

function 
onTimeout() {
  
Main(); // CALL MAIN FUNCTION

  // RECORD LAST FRAME OF DATA, USED FOR NEXT FRAME DATA COMPARISON
  
this.oldData = {player.x,player.y,player.freezetime,player.movemode,player.level.name};

  
setTimer(.05); // ERR, 
}

function 
Main() {
  
// DETERMINE WHETHER THE PLAYER'S GANI SHOULD BE UPDATED
  
temp.updateGani player.!= this.oldData[0] ||
      
player.!= this.oldData[1] ||
      (
player.freezetime == -&& this.oldData[2] >=0);

  
// IF PLAYER IS FROZEN(freezeplayer()), RETURN(STOP THE FUNCTION)
  
if (player.freezetime >= 0) return;
  
  
ReturnIdle(true); // ALWAYS RETURN THE PLAYER TO IDLE, true = DO NOT SET THE GANI
  
Movement();       // DO MOVEMENT FUNCTIONS

  // FIND THE TILE TYPE UNDER THE PLAYER, APPLY MODES APPROPRIATELY
  
temp.undertile tiletype(player.x+1.5,player.y+2);
  if (
undertile == 11player.movemode SWIM;
  else if (
undertile == 3player.movemode SIT;

  
// IF PLAYER IS NOT SWIMMING...
  
if (player.movemode != SWIM) {
    
// IF A WALL IS IN FRONT OF PLAYER...
    
if (@player.wallcheck == @{1,1}) {
      
// IF PLAYER HOLDS 'A'...
      
if (keydown(6)) {
        
// AND PLAYER IS HOLDING OPPOSITE DIRECTION KEY, PULL
        
if (keydown((player.dir+2)%4)) player.movemode PULL;
        
// OTHERWISE THEY ARE GRABBING
        
else player.movemode GRAB;
      }
      
// IF THEY'RE HOLDING THE SAME KEY THEY ARE FACING
      
if (keydown(player.dir)) {
        
// AFTER TIMEVAR2 EXCEEDS THE LAST TIME THE PLAYER WAS NOT PUSHING,
        // PLUS THEIR ACTION SPEED, SET MODE TO PUSH
        
if (timevar2 player.notpush player.actionspeedplayer.movemode PUSH;
      } else 
player.notpush timevar2// WHEN NOT HOLDING KEY, RECORD TIME
    
}
  }

  
Actions();           // PERFORM ACTIONS(GRABBING/PUSHING/PULLING...)
  
Animate(updateGani); // ANIMATE THE PLAYER, SEND WHETHER IT SHOULD BE UPDATED
}

function 
Movement() {
  
// IF PLAYER IS GRABBING OR PULLING, DO NO MOVEMENT(RETURN)
  
if (player.movemode in {GRAB,PULL}) return;

  for (
temp.k=0;k<4;k++) {
    if (
keydown(k)) {
      
// IF PLAYER MOVES, STOP THE PLAYER FROM PUSHING ANYMORE
      
if (!= player.dirplayer.notpush timevar2;

      
player.dir k;
      
// CHECK FOR WALL IN PLAYER'S NEW DIRECTION
      
player.wallcheck CheckWall(k);
     
      
// IF ABSOLUTELY NO WALL, PROCEED WITH MOVEMENT!
      // DO THIS BY COMPARING TWO ARRAYS
      
if (@player.wallcheck == @{0,0}) {
        
player.+= vecx(k)*player.speed;
        
player.+= vecy(k)*player.speed;
      } else {
        
ClearGap(k); // IF PLAYER HITS WALL, RUN ClearGap() TO RUN PRECISION CHECK
        // IF PLAYER IS HITTING WALL ON ONLY ONE SIDE, SLIDE THE PLAYER AROUND
        
if (player.wallcheck[0] + player.wallcheck[1] == 1WallSlide(k);
      }

      
player.movemode WALK// SET MODE TO WALK
    
}
  }
}

// CheckWall(direction[,speed]), SPEED IS OPTIONAL PARAMETER
function CheckWall(k,speed) {
  
// IF SPEED IS NOT GIVEN, DO CHECK AT PLAYER'S DEFAULT SPEED
  
if (temp.speed == nulltemp.speed player.speed;

  
// CREATE CHECK POSITION FOR BOX ONE
  
temp.checkOne = {
    
player..5 vecx(k)*(speed 2),
    
player.vecy(k)*(speed 2)
  };

  
// CREATE CHECK POSITION FOR BOX TWO
  
temp.checkTwo = {
    
player.1.5 vecx(k)*(speed 1),
    
player.vecy(k)*(speed 1)
  };

  
// CREATE AN ARRAY, AND DO WALL CHECKS
  // DETERMINE SIZE OF ONWALL2 IN RELATION TO PLAYER'S SPEED, TO DO ACCURATE CHECKS
  // AT HIGHER SPEEDS(NO WALL HOPPING)
  
temp.hitwall = {
    
onwall2(checkOne[0],checkOne[1],(k in {1,3} ? speed 1)-1/16,(k in {0,2} ? speed 1)-1/16),
    
onwall2(checkTwo[0],checkTwo[1],(k in {1,3} ? speed 1)-1/16,(k in {0,2} ? speed 1)-1/16),
  };

  return 
hitwall// RETURN WALL COLLISION CHECK
}

function 
ClearGap(k) {
  
temp.0// INITIATE 'i' OUTSIDE OF LOOP, SO IT IS ACCESSIBLE AFTERWARDS
  // INITIATE FOR LOOP AT 1/16, WHICH IS THE EQUIVALENT OF A SINGLE PIXEL
  // THIS AVOIDS SENDING WALL CHECK AT SPEED 0, WHICH WOULD MAKE THE FUNCTION
  // CHECK FOR THE PLAYER'S DEFAULT SPEED(0 = NULL)
  // 
  // FOR LOOP IS THE SIZE OF PLAYER'S SPEED, INCREMENTING AT 1/16
  // THIS CHECKS EVERY PIXEL BETWEEN THE PLAYER AND WHERE THEY HIT THE WALL
  // FOR PRECISION ACCURACY
  
for (temp.i=1/16;i<player.speed;i+=1/16) {
    
temp.CheckGap CheckWall(k,i);
    
// IF WALL IS DETECTED, STOP THE LOOP TO RETAIN IMPORTANT PIXEL DATA
    
if (@CheckWall(k,i) != @{0,0}) break;
  }
  
// IF LOOP COMPLETES FULLY, THEN THERE IS NO WALL. STOP CHECKING
  
if (== player.speed) return;

  
// IF DIRECTION IS LEFT OR RIGHT, MAKE CHANGES TO PLAYER'S X
  
if (k in {1,3}) {
    
player.+= vecx(k)*i;  // ADD 'i' TO PLAYER'S X POSITION
    // THEN GET RID OF DIRTY FLOATING VALUES
    // AND ADD .5, SINCE THE PLAYER'S GANI X IS OFFSET .5 FROM POSITION
    
player.int(player.x)+.5;
  
// IF DIRECTION IS UP OR DOWN, MAKE CHANGES TO PLAYER'S Y
  
} else {
    
player.+= vecy(k)*i;       // ADD 'i' TO PLAYER'S X POSITION
    
player.int(player.y+.5); // ROUND OFF PLAYER'S Y TO CLEAN FLOATING VALUE
  
}
}

function 
WallSlide(k) {
  
temp.slidedir 0// INITIATE TEMPORARY SLIDE DIRECTION

  // IF CHECK BOX ONE IS HITTING WALL, AND NOT BOX TWO...
  
if (player.wallcheck[0] == && player.wallcheck[1] == 0slidedir = (+ (k in {1,2} ? : -1))%4;
  
// OTHERWISE ASSUME BOX TWO IS HITTING A WALL, AND NOT BOX ONE
  
else slidedir = (+ (k in {1,2} ? -1))%4;

  
// CHECKK FOR WALL IN SLIDING DIRECTION
  
temp.slidewall CheckWall(slidedir,player.slidespeed);
  if (@
slidewall != @{0,0}) return; // HIT WALL? RETURN AND STOP SLIDING
 
  // MOVE PLAYER IN SLIDING DIRECTION
  
player.+= vecx(slidedir)*player.slidespeed;
  
player.+= vecy(slidedir)*player.slidespeed;
  
// ROUND OFF PLAYER'S POSITION TO NEAREST SLIDING SPEED
  // THIS GETS RID OF LONG FLOATING VALUES
  
player.int(player.x/player.slidespeed)*player.slidespeed;
  
player.int(player.y/player.slidespeed)*player.slidespeed;
}

function 
Actions() {
  
// PERFORM CHECKS TO MAKE SURE ACTIONS ARE NOT BEING PERFORMED
  
if (player.movemode == GRAB) {    // IF GRABBING...
    
if (this.hasgrabbed == false) { // AND NOT KNOWN TO BE GRABBING BEFORE...
        // TRIGGERACTION 'grab' IN FRONT OF PLAYER
        
triggerAction(player.x+1.5+vecx(player.dir)*2,
                      
player.y+2+vecy(player.dir)*2,
                      
"grab",null);
      
this.hasgrabbed true;       // LET SCRIPT KNOW PLAYER IS GRABBING
    
}
  } else 
this.hasgrabbed false;   // IF NOT GRABBING, STOP RECORDING THAT PLAYER IS

  // DO SIMILAR CHECKS FOR PUSH AND PULL, EXCEPT ONLY EVERY N SECONDS(player.actionspeed)
  
if (player.movemode == PULL) {
    if ((
timevar2 this.startpull) > player.actionspeed) {
        
triggerAction(player.x+1.5+vecx(player.dir)*2,
                      
player.y+2+vecy(player.dir)*2,
                      
"pulled",(player.dir+2)%4);
      
this.startpull timevar2;
    }
  } else 
this.startpull timevar2;

  if (
player.movemode == PUSH) {
    if ((
timevar2 this.startpush) > player.actionspeed) {
        
triggerAction(player.x+1.5+vecx(player.dir)*2,
                      
player.y+2+vecy(player.dir)*2,
                      
"pushed",player.dir);
      
this.startpush timevar2;
    }
  } else 
this.startpush timevar2;
}

function 
Animate(update) {
  
// IF PLAYER'S MODE HAS CHANGED, OR FORCED UPDATE IS TRUE, UPDATE GANI
  
if (player.movemode != this.oldData[3] || update == true) {
    
setani(player.ganis[player.movemode],null);
  }
}

// A CONVENIENT FUNCTION TO RUN CHECKS TO RETURN PLAYER TO IDLE POSITION
// MADE PUBLIC SO EXTERNAL SCRIPTS CAN CALL IT.
// INSTEAD OF SETTING GANI TO IDLE, USE THIS FUNCTION INSTEAD
// omitgani PARAMETER TELLS FUNCTION TO NOT SET THE GANI, ONLY SET THE MODE
public function ReturnIdle(omitgani) {
  
// DO NOT CONTINUE TO IDLE IF...
  
if (player.movemode == SWIM) return;                        // SWIMMING
  
if (player.movemode in {GRAB,PULL} && keydown(6)) return;   // GRABBING OR PULLING
  
if (player.movemode == PUSH && keydown(player.dir)) return; // PUSHING

  // SET MODE AS IDLE. HERE YOU CAN DO MORE CHECKS TO PROPERLY ASSIGN IDLE MODE
  // FOR EXAMPLE, IS PLAYER CARRYING SOMETHING? IF SO, SET MODE TO CARRYIDLE 
  // INSTEAD OF IDLE. 
  
player.movemode IDLE;

  if (
omitgani == true) return; // IF OMIT GANI, STOP FUNCTION BEFORE SETTING GANI
  
if (player.movemode == IDLEsetAni(player.ganis[player.movemode],null);

  
// RECORD LAST FRAME DATA IN CASE IT'S NEEDED
  
this.olddata = {player.x,player.y,player.freezetime,player.act};
}

public function 
ReplaceGani(defgani,newgani) {
  
// FIND INSTANCE OF DEFAULT GANI IN LIST OF CUSTOM GANI'S
  
temp.ganifound this.defaultganis.index(defgani);

  
// IF newgani IS NOT PROVIDED, RESET GANI TO DEFAULT
  
if (newgani == nullplayer.ganis[ganifound] = this.defaultganis[ganifound];
  
// OTHERWISE REPLACE THE GANI WITH NEW GANI
  
else player.ganis[ganifound] = newgani;


Last edited by DustyPorViva; 03-13-2010 at 10:15 PM..
Reply With Quote
  #4  
Old 03-13-2010, 09:05 PM
Clockwork Clockwork is offline
ᶘ ᵒᴥᵒᶅ...ᶘ ಠᴥಠᶅ❤...ℳℴℯ
Clockwork's Avatar
Join Date: Feb 2007
Location: Pennsylvania
Posts: 2,071
Clockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant futureClockwork has a brilliant future
Very nice :o...
Reply With Quote
  #5  
Old 03-13-2010, 09:15 PM
Crow Crow is offline
ǝɔɐɹq ʎןɹnɔ
Crow's Avatar
Join Date: Dec 2006
Location: Germany
Posts: 5,153
Crow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond reputeCrow has a reputation beyond repute
That box approach is nice, I think I will give that a try in the future.

Edit: I think when rounding the coordinates, though, you are better off using shared.roundto(), and rounding towards 1/16:
PHP Code:
player.shared.roundto(player.x16);
player.shared.roundto(player.y16); 
Reply With Quote
  #6  
Old 03-13-2010, 09:23 PM
Deas_Voice Deas_Voice is offline
Deas
Deas_Voice's Avatar
Join Date: Jun 2007
Location: Sweden
Posts: 2,264
Deas_Voice is a jewel in the roughDeas_Voice is a jewel in the rough
Send a message via AIM to Deas_Voice Send a message via MSN to Deas_Voice Send a message via Yahoo to Deas_Voice
very nice, but you kinda lost me, i guess i didn't read it too well.
__________________
.
WTF is real life, and where do I Download it?
There is no Real Life, just AFK!
since 2003~
I Support~
ღAeonღ | ღTestbedღ | ღDelteriaღ

if you are going to rep me, don't be an idiot, leave your name!
I got nothing but love for you
Reply With Quote
  #7  
Old 03-13-2010, 09:31 PM
DustyPorViva DustyPorViva is offline
Will work for food. Maybe
DustyPorViva's Avatar
Join Date: Sep 2003
Location: Maryland, USA
Posts: 9,589
DustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond reputeDustyPorViva has a reputation beyond repute
Send a message via AIM to DustyPorViva Send a message via MSN to DustyPorViva
Quote:
Originally Posted by Crow View Post
That box approach is nice, I think I will give that a try in the future.

Edit: I think when rounding the coordinates, though, you are better off using shared.roundto(), and rounding towards 1/16:
PHP Code:
player.shared.roundto(player.x16);
player.shared.roundto(player.y16); 
I had tried rounding to the nearest pixel. However, it doesn't really matter. Rounding to the speed of the player's sliding speed will align them just as properly when sliding around
Reply With Quote
  #8  
Old 03-13-2010, 09:32 PM
xAndrewx xAndrewx is offline
Registered User
xAndrewx's Avatar
Join Date: Sep 2004
Posts: 5,260
xAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud of
wow- you're amazing! Good job, never once not been amazed by your work!
__________________
Reply With Quote
  #9  
Old 03-13-2010, 10:13 PM
Crono Crono is offline
:pluffy:
Join Date: Feb 2002
Location: Sweden
Posts: 20,000
Crono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond reputeCrono has a reputation beyond repute
most helpful NAT ever.
__________________
Reply With Quote
  #10  
Old 03-13-2010, 10:49 PM
Imperialistic Imperialistic is offline
graal player lord
Imperialistic's Avatar
Join Date: Apr 2007
Location: Florida
Posts: 1,094
Imperialistic is a jewel in the roughImperialistic is a jewel in the rough
wow, this is an outstanding job.
Good Job Dusty!
__________________
" It's been swell, but the swelling's gone down. "
Reply With Quote
  #11  
Old 03-13-2010, 11:52 PM
Cubical Cubical is offline
Banned
Join Date: Feb 2007
Posts: 1,348
Cubical has a brilliant futureCubical has a brilliant futureCubical has a brilliant futureCubical has a brilliant futureCubical has a brilliant futureCubical has a brilliant futureCubical has a brilliant future
Good job.
Reply With Quote
  #12  
Old 03-14-2010, 12:28 AM
adam adam is offline
http://wiki.graal.us/
adam's Avatar
Join Date: Nov 2001
Posts: 2,247
adam has a spectacular aura aboutadam has a spectacular aura about
Send a message via AIM to adam
Good work!
__________________
Rogue Shadow (TCN)(NAT)(Global Development Team)

For development help, contact the patrons of the #graaldt irc channel below, I am usually there.
Click Here to Join IRC Chat Now! -- irc.freenode.net Channel: #graaldt
Quote:
<Dustyshouri> no, RogueShadow is always talking about scripts lol
<Dustyshouri> in fact, he pretty much brought Graal back as a topic single-handedly
Reply With Quote
  #13  
Old 03-14-2010, 12:43 AM
papajchris papajchris is offline
Zeus Condero
papajchris's Avatar
Join Date: Jan 2006
Location: Michigan
Posts: 1,600
papajchris is a splendid one to beholdpapajchris is a splendid one to beholdpapajchris is a splendid one to beholdpapajchris is a splendid one to behold
If you are in the GDT Dusty then ignore this, but if your not; This is the type of work they should be doing imo. Teaching others how to script instead of just making scripts
__________________
Reply With Quote
  #14  
Old 03-14-2010, 03:53 AM
xAndrewx xAndrewx is offline
Registered User
xAndrewx's Avatar
Join Date: Sep 2004
Posts: 5,260
xAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud ofxAndrewx has much to be proud of
Quote:
Originally Posted by papajchris View Post
If you are in the GDT Dusty then ignore this, but if your not; This is the type of work they should be doing imo. Teaching others how to script instead of just making scripts
Exactly- Hurrah for DUSTY!!
__________________
Reply With Quote
  #15  
Old 03-14-2010, 11:26 PM
Switch Switch is offline
o.o
Switch's Avatar
Join Date: Jan 2007
Location: Philadelphia
Posts: 3,038
Switch has a spectacular aura about
Send a message via MSN to Switch
I forget how I made mine, but this seems like a much better approach.
__________________
Oh squiggly line in my eye fluid. I see you lurking there on the peripheral of my vision.
But when I try to look at you, you scurry away.
Are you shy, squiggly line?
Why only when I ignore you, do you return to the center of my eye?
Oh, squiggly line, it's alright, you are forgiven.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 09:57 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2025, vBulletin Solutions Inc.
Copyright (C) 1998-2019 Toonslab All Rights Reserved.