| 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.y && 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 |0, pi*0.25|?3:(this.angle in |pi*0.25, pi*0.75|?0:(this.angle in |pi*0.75, pi*1.25|?1:(this.angle in |pi*1.25, pi*1.75|?2:3)))); //PLAYERS, NPCS, AND WALLS for (temp.i=0; i<this.speed*16*2; i++) { //PLAYERS for (temp.pl : findNearestPlayers(this.x, this.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+1 <= this.y+temp.movey*(i/16) && temp.pl.y+3 >= this.y+temp.movey*(i/16)+this.height) || (temp.pl.y+1 >= this.y+temp.movey*(i/16) && temp.pl.y+3 <= this.y+temp.movey*(i/16)+this.height) || (temp.pl.y+1 <= this.y+temp.movey*(i/16) && temp.pl.y+3 >= this.y+temp.movey*(i/16)) || (temp.pl.y+1 <= this.y+temp.movey*(i/16)+this.height && temp.pl.y+3 >= 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.playercatch, this.params); } } } } //NPCS for (temp.npc : findAreaNpcs(this.x+temp.movex*(i/16), this.y+temp.movey*(i/16), this.width, this.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.npccatch, this.params); } } //WALLS if (onWall2(this.x+temp.movex*(i/16), this.y+temp.movey*(i/16), this.width, this.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.movex, temp.movey, temp.time, 0); //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(sx, sy, ag, sp, ra, ow, ai, aip, pa) { temp.npc = putNpc2(sx, sy, ""); 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.x, this.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 {0, 2}?1.5:0); //x temp.sy = player.y+2+vecy(player.dir)-(player.dir == 0?3:0)-(player.dir in {1, 3}?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.sx, temp.sy, temp.ag, temp.sp, temp.ra, temp.ow, temp.ai, temp.aip, temp.pa); } //#CLIENTSIDE function onWeaponFired() { triggerServer("gui", this.name, null); }
---
//KEEPING THE PROJECTILE IN SCOPE function onActionServerSide() { temp.sx = player.x+1.5+vecx(player.dir)-(player.dir == 1?3:0)-(player.dir in {0, 2}?1.5:0); //x temp.sy = player.y+2+vecy(player.dir)-(player.dir == 0?3:0)-(player.dir in {1, 3}?1.5:0); //y temp.npc = putnpc2(temp.sx, temp.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.name, null); }
Attached is a testing image and gani you can use with the example weapon NPCs I provided.
|