Graal Forums  

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

Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old 05-13-2010, 01:00 AM
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
Custom Projectile System

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.
Attached Images
 
Attached Files
File Type: gani switch_projectiles.gani (461 Bytes, 218 views)
__________________
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.

Last edited by Switch; 05-13-2010 at 11:24 PM..
Reply With Quote
  #2  
Old 05-13-2010, 04:41 PM
Chompy Chompy is offline
¯\(º_o)/¯
Chompy's Avatar
Join Date: Sep 2006
Location: Norway
Posts: 2,815
Chompy is just really niceChompy is just really niceChompy is just really nice
Send a message via MSN to Chompy
Have you done any stress testing on it? (:

Looking good though!
__________________
Reply With Quote
  #3  
Old 05-13-2010, 11:33 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
Quote:
Originally Posted by Chompy View Post
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.
__________________
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
  #4  
Old 05-15-2010, 08:05 AM
elite_master elite_master is offline
Registered User
Join Date: Apr 2007
Posts: 107
elite_master is an unknown quantity at this point
Bravo, bravo :]
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 10:46 AM.


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