Scripts:Fire projectile
The following script launches a slice in an arc according to gravity.
There's also a simple "jump hero to" (and "jump npc to") script which makes this easy to use for heroes and NPCs.
To use the scripts, you create a slice sl (such as an arrow sprite), place it at the initial position (e.g. the player's position), call fire projectile(sl, speed, x, y, z) (z is optional) which is similar to move slice by(sl, x, y, distance/speed), and then call projectile eachtick(sl) every tick after that. For example,
fire projectile(sl, 5, 20, 40) # Move sl right 20 pixels, down 40 pixels, at 5 horizonal pixels per tick while (projectile eachtick(sl)) do (wait(1))
There are more complete examples below.
- Here is an example .rpg file and scripts. (See recording to the right.)
The basic scripts for this example .rpg are included at the bottom of this page, showing how to have multiple projectiles at once.
The scripts[edit]
# Strength of gravity, in hundredths of a pixel per tick per tick define constant(150, gravity 100) # Unique slice lookup code used by projectiles define constant(12345, sli:dataslice) ################################################################################ # Utility scripts # These are temporary variables used by the projectile scripts. They are saved # and loaded to a hidden data slice, so that you can have multiple projectiles at once. global variable(16001, prj:time) # Ticks remaining global variable(16002, prj:x100) # Current x position * 100 global variable(16003, prj:y100) # Current y position * 100 global variable(16004, prj:z100) # Current z position (relative to start) * 100 global variable(16005, prj:v_x100) # x velocity * 100 global variable(16006, prj:v_y100) # y velocity * 100 global variable(16007, prj:v_z100) # z velocity * 100 # Load the prj:* globals from a slice script, load projectile data, dataslice, begin prj:time := slicewidth(dataslice) prj:x100 := sliceheight(dataslice) prj:y100 := slicex(dataslice) prj:z100 := slicey(dataslice) prj:v_x100 := gettoppadding(dataslice) prj:v_y100 := getbottompadding(dataslice) prj:v_z100 := getleftpadding(dataslice) end # Save the prj:* globals to a slice script, save projectile data, dataslice, begin setslicewidth (dataslice, prj:time) setsliceheight (dataslice, prj:x100) setslicex (dataslice, prj:y100) setslicey (dataslice, prj:z100) settoppadding (dataslice, prj:v_x100) setbottompadding (dataslice, prj:v_y100) setleftpadding (dataslice, prj:v_z100) end # Create a hidden child slice that can be used to store data script, create data slice, parent, begin variable(dataslice) dataslice := lookup slice(sli:dataslice, parent) if (dataslice) then (exit returning(dataslice)) # Just in case you call this twice dataslice := createcontainer setparent(dataslice, parent) setslicevisible(dataslice, false) setslicelookup(dataslice, sli:dataslice) return(dataslice) end ################################################################################ # Projectiles # Start a new projectile on an arcing path. # Speed is the ground speed in pixels per tick. # x distance and y distance is how far you want to shoot the projectile, relative # to the initial position (like "move slice by"). # z distance is used when shooting at a target with a different height, e.g. aiming # at the top of a tree or up a cliff. Positive z distance means shooting upwards script, fire projectile, slice, speed, x distance, y distance, z distance = 0, begin variable(dist, time) dist := sqrt(xdistance ^ 2 + ydistance ^ 2) # Calculate time-in-air, in ticks time := (dist + speed / 2) / speed if (time <= 0) then (time := 1) # Initial vertical velocity, in hundredths of a pixel per tick prj:v_z100 := gravity100 * time / 2 + 100 * zdistance / time # Save some other necessary data prj:x100 := slicex(slice) * 100 prj:y100 := slicey(slice) * 100 prj:z100 := 0 prj:v_x100 := xdistance * 100 / time prj:v_y100 := ydistance * 100 / time prj:time := time save projectile data(create data slice(slice)) end # Update the position of a projectile slice. Needs to be called every tick. # Returns true if still in flight, or false if the projectile has reached the end. script, projectile eachtick, slice, begin variable(dataslice) dataslice := lookup slice(sli:dataslice, slice) if (dataslice == false) then (script error($99="Couldn't find projectile data slice")) load projectile data(dataslice) if (prj:time <= 0) then (exit returning(false)) # Already finished prj:time -= 1 prj:x100 += prj:v_x100 prj:y100 += prj:v_y100 prj:z100 += prj:v_z100 -- gravity100 / 2 # Add half of gravity to perform leapfrog integration prj:v_z100 -= gravity100 save projectile data(dataslice) # Add 50 to round to nearest pixel put slice(slice, (prj:x100 + 50) / 100, (prj:y100 -- prj:z100 + 50) / 100) return (prj:time > 0) end
jump hero to script[edit]
This is a very simple way to use the script to make a hero jump in an arc.
This script works for NPCs too, if you just search-and-replace "hero" with "npc".
You might want to change the hero speed before calling the script, and don't forget that you can customise the value of the gravity100 constant.
Note: You need to call suspend player before calling this script, or set npc moves(npc, false) or suspend npcs before using it on an NPC.
# Make a hero to jump to a certain tile position, at their normal walk speed, # and wait for the jump to finish. # This is a drop-in replacement for "walk hero to X(who, X), walk hero to Y(who, Y), wait for hero(who)" # X, Y is the position in tile coordinates. script, jump hero to, who, x, y, begin variable(sl, midair) sl := get hero slice(who) fire projectile(sl, getherospeed(who), 20 * (x -- herox(who)), 20 * (y -- heroy(who))) midair := true while (midair) do ( midair := projectile eachtick(sl) # This is needed because you can't move a hero slice directly put hero(who, slicex(sl), slicey(sl)) wait(1) ) end
Example Usage[edit]
Here are two more examples of how to use fireprojectile directly: the first uses an onkeypress script to create the projectile, and a timer to run projectile eachtick. Set arrow onkeypress as the map on-keypress script to use it, and press Z to fire.
The second uses a while loop to check for keypresses and update projectiles. Set main loop as the map autorun script to use it, and press Z to fire.
# Set this to the correct map layer, above the hero define constant(sl:map layer1, projectile layer) define constant(80, arrow distance) script, dirX, dir, dist, begin switch (dir) do ( case (up, down) do (return (0)) case (right) do (return (dist)) case (left) do (return (-1 * dist)) ) end script, dirY, dir, dist, begin switch (dir) do ( case (left, right) do (return (0)) case (down) do (return (dist)) case (up) do (return (-1 * dist)) ) end ################################################################################ # Here is one way to run the scripts above: by using a timer. # Each projectile in the air will need a different timer and a different global variable. defineconstant(15, timer:arrow1) globalvariable(1, arrow1) plotscript, arrow onkeypress, begin # Press Z to shoot an arrow. Can only fire one arrow at a time. if (keyval(key:z) > 1 && arrow1 == 0) then (fire arrow1) end script, fire arrow1, begin arrow1 := load walkabout sprite(3) setparent(arrow1, lookupslice(projectile layer)) put slice(arrow1, heropixelx, heropixely) variable(x, y) x := dirX(herodirection, arrow distance) # Find the position 60 pixels ahead of the player y := dirY(herodirection, arrow distance) fire projectile(arrow1, 8, x, y) # 8 pixel/tick set timer(timer:arrow1, 0, 1, @arrow1 eachtick) end script, arrow1 eachtick, timer id, begin if (projectile eachtick(arrow1)) then ( # Still in the air, run timer again set timer(timer id, 0) # Restarts the timer with a count of 0 (next tick) ) else ( # Hit the ground,c hange to a 'splash' animation set timer(timer:arrow1, 0, 1, @arrow1 splash eachtick) ) end # Play a three-frame splash animation script, arrow1 splash eachtick, timer id, begin setspriteframe(arrow1, getspriteframe(arrow1) + 1) if (getspriteframe(arrow1) == 4) then ( free slice(arrow1) arrow1 := 0 ) else ( set timer(timer id, 0) ) end ################################################################################ # Here's another way: by using a loop # slice lookup code used by projectiles (normally you would define this in Custom instead) define constant(1000, sli:projectile) define constant(1001, sli:splash) # Projectile splash animation script, fire arrow, begin variable(arrow) arrow := load walkabout sprite(3) setparent(arrow, lookupslice(projectile layer)) put slice(arrow, heropixelx, heropixely) setslicelookup(arrow, sli:projectile) # Set this so we can find it later variable(x, y) x := dirX(herodirection, arrow distance) # Find the position 60 pixels ahead of the player y := dirY(herodirection, arrow distance) fire projectile(arrow, 8, x, y) # 8 pixel/tick end plotscript, main loop, begin while (true) do ( if (keyval(key:z) > 1) then (fire arrow) # Loop through projectiles and process them variable(sl, next) sl := firstchild(lookupslice(projectile layer)) while (sl) do ( next := nextsibling(sl) if (getslicelookup(sl) == sli:projectile) then ( # Update position, and delete the slice when it's reached its target tracevalue(sl) if (projectile eachtick(sl) == false) then ( # Change to a 'splash' animation setslicelookup(sl, sli:splash) ) ) elseif (getslicelookup(sl) == sli:splash) then ( # Play a three-frame splash animation setspriteframe(sl, getspriteframe(sl) + 1) if (getspriteframe(sl) == 4) then (free slice(sl)) ) sl := next ) wait ) end
See Also: other projectile scripts[edit]
- General jump hero script to jump in any direction, or up or down a ledge
- Simpler script to make the hero jump in an arc an arbitrary number of tiles in any direction: [1]
- Simpler script to make the hero jump in an arc off a cliff in any direction (move one tile down plus one tile in given direction) [2]
- Firing a projectile in an arc with a certain height and distance: using a for loop: (see [3] and [4]); or using a timer: [5]