Graal Forums

Graal Forums (https://forums.graalonline.com/forums/index.php)
-   Code Gallery (https://forums.graalonline.com/forums/forumdisplay.php?f=179)
-   -   Custom Projectile System (https://forums.graalonline.com/forums/showthread.php?t=134259144)

Switch 05-13-2010 01:00 AM

Custom Projectile System
 
2 Attachment(s)
Finished this around December with just plain directions, and recently changed it to angles.
The reason I started this was to allow a change of the speed and range of a projectile. The only thing it really doesn't have now is z manipulation. Dusty style comments alright!


Here's the actual projectile class, which should be named "projectile". I would suggest you not change anything except for this.playercatch and this.npccatch in the onCreated function, and if you want your own defined width/height you could change this.width and this.height in the onMove function to your desired width and height.
PHP Code:

/*
  BEFORE JOINING TO THE CLASS, MAKE SURE THESE ARE SET:
  npc.angle - ANGLE OF THE PROJECTILE IN RADIANS
  npc.speed - TILES PER 0.05 SECONDS
  npc.range - MAX TILES THE PROJECTILE CAN GO WITHOUT HITTING A WALL
  npc.owner - SET TO THE PLAYER THAT YOU DON'T WANT TO BE HIT (PLAYER THAT SHOT THE PROJECTILE?/NULL FOR UNNEEDED)
  npc.showCharacter() - CREATES A WIDTH/HEIGHT
  npc.ani - PROJECTILE'S GANI
  npc.aniparams - PROJECTILE'S GANI PARAMS AS A STRING SEPARATED BY ","'S
  npc.params - PARAMS TO BE READ BY THE OBJECT THAT WAS HIT AS AN ARRAY
*/

function onCreated() {
  
//CHANGE TO THE PLAYER HIT WEAPON NPC (NEEDED FOR PLAYERS)
  
this.playercatch "-System/Projectiles";
  
//CHANGE TO THE HIT EVENT onEventname (NEEDED FOR NPCS)
  
this.npccatch "CustomProjectile";
  
  
//SET ON CREATION
  
this.startx this.x;
  
this.starty this.y;
  
this.oldx this.x;
  
this.oldy this.y;
  
dontBlock();
  
onMove();
}

function 
onMove() {
  
//CHECK IF PROJECTILE IS OVER THE RANGE
  
if (((this.x-this.startx)^2+(this.y-this.starty)^2)^0.5 this.range) {
    
onProjectileDestroy();
  }
  
//CHECK IF THE PROJECTILE HASN'T MOVED
  
if (this.oldx == this.&& this.oldy this.y) {
    
onProjectileDestroy();
  }
  
this.oldx this.x;
  
this.oldy this.y;
  
//FULL TIME TO MOVE
  
temp.time 0.1;
  
//FULL DELTA X AND Y TO MOVE
  
temp.movex cos(this.angle)*this.speed*2;
  
temp.movey = -sin(this.angle)*this.speed*2;
  
//CHANGE DIR IF NECESSARY
  
this.dir = (this.angle in |0pi*0.25|?3:(this.angle in |pi*0.25pi*0.75|?0:(this.angle in |pi*0.75pi*1.25|?1:(this.angle in |pi*1.25pi*1.75|?2:3))));
  
//PLAYERS, NPCS, AND WALLS
  
for (temp.i=0i<this.speed*16*2i++) {
    
//PLAYERS
    
for (temp.pl findNearestPlayers(this.xthis.y)) {
      
//CHECKS IF THE PLAYER IS READY TO BE HIT
      
if (!(temp.pl in this.playerhit) && temp.pl.account != this.owner) {
        
//CHECKS IF THE PLAYER IS IN VALID X/Y COORDINATES
        
if ((temp.pl.x+0.5 <= this.x+temp.movex*(i/16) && temp.pl.x+2.5 >= this.x+temp.movex*(i/16)+this.width) || (temp.pl.x+0.5 >= this.x+temp.movex*(i/16) && temp.pl.x+2.5 <= this.x+temp.movex*(i/16)+this.width) || (temp.pl.x+0.5 <= this.x+temp.movex*(i/16) && temp.pl.x+2.5 >= this.x+temp.movex*(i/16)) || (temp.pl.x+0.5 <= this.x+temp.movex*(i/16)+this.width && temp.pl.x+2.5 >= this.x+temp.movex*(i/16)+this.width)) {
          if ((
temp.pl.y+<= this.y+temp.movey*(i/16) && temp.pl.y+>= this.y+temp.movey*(i/16)+this.height) || (temp.pl.y+>= this.y+temp.movey*(i/16) && temp.pl.y+<= this.y+temp.movey*(i/16)+this.height) || (temp.pl.y+<= this.y+temp.movey*(i/16) && temp.pl.y+>= this.y+temp.movey*(i/16)) || (temp.pl.y+<= this.y+temp.movey*(i/16)+this.height && temp.pl.y+>= this.y+temp.movey*(i/16)+this.height)) {
            
//GIVES TIME FOR THE PLAYER TO NOT RECEIVE EXCESSIVE HITS WHILE PASSING THROUGH
            
this.playerhit.add(temp.pl);
            
scheduleEvent(3"PlayerUnhit"temp.pl);
            
//TRIGGERS THE CONTROL WEAPON
            
temp.pl.triggerClient("gui"this.playercatchthis.params);
          }
        }
      }
    }
    
//NPCS
    
for (temp.npc findAreaNpcs(this.x+temp.movex*(i/16), this.y+temp.movey*(i/16), this.widththis.height)) {
      
//CHECK IF THE NPC IS READY TO BE HIT
      
if (!(temp.npc in this.npchit)) {
        
//GIVE TIME FOR THE NPC TO NOT RECEIVE EXCESSIVE HITS WHILE PASSING THROUGH
        
this.npchit.add(temp.npc);
        
scheduleEvent(3"NPCUnhit"temp.npc);
        
//TRIGGERS THE NPC'S PROJECTILE CATCH
        
temp.npc.trigger(this.npccatchthis.params);
      }
    }
    
//WALLS
    
if (onWall2(this.x+temp.movex*(i/16), this.y+temp.movey*(i/16), this.widththis.height)) {
      
//TIME LEFT TO MOVE
      
temp.time this.speed/i/16;
      
//DELTA X AND Y LEFT TO MOVE
      
temp.movex temp.movex*(i/16);
      
temp.movey temp.movey*(i/16);
      
//SETS TO BLOCKED
      
temp.blocked true;
      break;
    }
  }
  
//SCHEDULES WHEN TO DESTROY THE PROJECTILE IF BLOCKED
  
if (temp.blocked) {
    
//MAKE SURE THERE'S ENOUGH TIME TO DESTROY
    
if (temp.time-0.05 <= 0) {
      
onProjectileDestroy();
    }
    else {
      
scheduleEvent(temp.time-0.05"ProjectileDestroy"null);
    }
  }
  
//DO MOVE
  
move(temp.movextemp.moveytemp.time0);
  
//SCHEDULE NEW MOVE
  
scheduleEvent(0.05"Move");
}

//ALLOWS PLAYER TO GET HIT AGAIN
function onPlayerUnhit(pl) {
  
this.playerhit.remove(pl);
}
//ALLOWS NPC TO GET HIT AGAIN
function onNPCUnhit(npc) {
  
this.npchit.remove(npc);
}

//DESTROYS THE PROJECTILE
function onProjectileDestroy() {
  
this.destroy();


Note: The projectile's direction (animation direction, that is) is always being set as it goes along based on it's angle, so if you decide to change it's angle while it's going it will face the right direction.


Here's the class with a shoot2(x, y, angle, speed, range, owner, ani, aniparams, params) serverside function (explanations on them at the top of the projectile class) for if you don't need to keep the projectile object in scope. I would name it "projectilesmaker", and join an NPC to use shoot2().
PHP Code:

function shoot2(sxsyagspraowaiaippa) {
  
temp.npc putNpc2(sxsy"");
  
temp.npc.showCharacter();
  
temp.npc.ani ai;
  
temp.npc.aniparams aip;
  
temp.npc.speed sp;
  
temp.npc.angle ag;
  
temp.npc.range ra;
  
temp.npc.owner ow;
  
temp.npc.params pa;
  
temp.npc.join("switch_projectiles");



Here's the player's catch (weapon), and all player catches will be done in here. The default name for it is "-System/Projectiles". Just add if checks and whatnot. Use temp.param[0,1,...] instead of params[0,1,...] since the param array has to be sent as an array (given params[0] spot only).
PHP Code:

//#CLIENTSIDE
function onActionClientside() {
  
temp.param params[0];
  
//EXAMPLE CHECK AS {cookies", "yogurt"}
  
if (temp.param[0] == "cookies") {
    
player.chat temp.param[1]; //PLAYER SAYS "yogurt"
  
}



Now, in a level NPC, you need to add your own catch function (serverside). The event name would be the projectile system's NPC catch with "on" stuck before it. An example could be like below. Also, notice that you can use the default params[0,1,...] for this. Not sure why this is, but it is.
PHP Code:

...

//STILL USING {"cookies", "yogurt"}
function onCustomProjectile() {
  if (
params[0] == "cookies") {
    
with (findNearestPlayer(this.xthis.y)) {
      
player.chat params[1]; //NEAREST PLAYER SAYS "yogurt"
    
}
  }
}

... 


So with all that, here's examples of the two ways you can go about doing this (easy weapon NPC example).
PHP Code:

//USING shoot2()
function onCreated() {
  
this.join("projectilesmaker");
}
function 
onActionServerSide() {
  
temp.sx player.x+1.5+vecx(player.dir)-(player.dir == 1?3:0)-(player.dir in {02}?1.5:0); //x
  
temp.sy player.y+2+vecy(player.dir)-(player.dir == 0?3:0)-(player.dir in {13}?1.5:0); //y
  
temp.ag getangle(vecx(player.dir), vecy(player.dir)); //angle
  
temp.sp 1.5//speed
  
temp.ra 64//range
  
temp.ow player.account//owner
  
temp.ai "switch_projectiles"//ani
  
temp.aip "switch_projectiles.png"//aniparams
  
temp.pa = {"cookies""yogurt"}; //params
  
shoot2(temp.sxtemp.sytemp.agtemp.sptemp.ratemp.owtemp.aitemp.aiptemp.pa);
}
//#CLIENTSIDE
function onWeaponFired() {
  
triggerServer("gui"this.namenull);
}

---

//KEEPING THE PROJECTILE IN SCOPE
function onActionServerSide() {
  
temp.sx player.x+1.5+vecx(player.dir)-(player.dir == 1?3:0)-(player.dir in {02}?1.5:0); //x
  
temp.sy player.y+2+vecy(player.dir)-(player.dir == 0?3:0)-(player.dir in {13}?1.5:0); //y
  
temp.npc putnpc2(temp.sxtemp.sy"");
  
temp.npc.angle getangle(vecx(player.dir), vecy(player.dir));
  
temp.npc.speed 1.5;
  
temp.npc.range 64;
  
temp.npc.owner player.account;
  
temp.npc.ani "switch_projectiles";
  
temp.npc.aniparams "switch_projectiles.png";
  
temp.npc.params = {"cookies""yogurt"};
  
temp.npc.join("projectiles");
  
//USE temp.npc OBJECT FOR OTHER THINGS, ETC.
}
//#CLIENTSIDE
function onWeaponFired() {
  
triggerServer("gui"this.namenull);


Attached is a testing image and gani you can use with the example weapon NPCs I provided.

Chompy 05-13-2010 04:41 PM

Have you done any stress testing on it? (:

Looking good though! ^^

Switch 05-13-2010 11:33 PM

Quote:

Originally Posted by Chompy (Post 1575798)
Have you done any stress testing on it? (:

Looking good though! ^^

Yep, quite a bit. There are possibly things I may have overlooked, other than that onwall2() is returning true for spots like bushes, stones, etc. that have been picked up and not replaced yet, players that aren't blocking like paused players, and NPCs even if they're not blocking.

elite_master 05-15-2010 08:05 AM

Bravo, bravo :]


All times are GMT +2. The time now is 10:22 PM.

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