
   /* AMX Mod script.
   *
   *  Ninja Rope
   *  by SpaceDude
   *  email: eayumns@nottingham.ac.uk
   *  MSN: eayumns@nottingham.ac.uk
   *  ICQ: 1615758
   *  IRC: Quakenet, nickname: "SpaceDude"
   *
   *  Description:
   *
   *  Formely  known  as Grappling Hook, i changed the name to Ninja Rope
   to avoid confusion
   *  with  the  hook metamod plugin. Also the plugin now acts in exactly
   the same way as the
   *  ninja  rope  from  the game worms. Its based on real world physics,
   modelled as a point
   *  mass on the end of a rod.
   *
   *  In order to use this plugin you will need the latest xtrafun module
   which can be found here:
   *
   *  http://amxmod.net/forums/viewtopic.php?t=10714
   *
   *  Server Side Cvars:
   *
   *    sv_ninjarope - enable/disable the plugin, default is 1
   *
   *  Client Side Commands:
   *
   *    +rope - bind a key to +rope like this: bind <key> +rope, once you
   have done that
   *            look in the direction where you want to fire the hook and
   press the button.
   *            You  can  shorten  the rope by holding down the jump key,
   and make it longer
   *            by  holding the crouch key. Also you can move around with
   the regular move
   *            keys  (+forward, +back, +moveleft, +moveright) to release
   the rope just let
   *            go of the button.
   *
   *    rope_toggle - has the same effect as +rope, except that you press
   it once to fire
   *                  it and once again to release.
   *
   *  Revision History:
   *
   *   v1.1 - Changed name from Grappling Hook to Ninja Rope
   *        - Added more control to the rope while swinging
   *
   *   v1.0 - Initial Release
   */
   #include <amxmod>
   #include <xtrafun>
   #define TE_BEAMENTPOINT 1
   #define TE_KILLBEAM 99
   #define DELTA_T 0.1                // seconds
   #define BEAMLIFE 100            // deciseconds
   #define MOVEACCELERATION 150    // units per second^2
   #define REELSPEED 200            // units per second
   new hooklocation[33][3]
   new hooklength[33]
   new bool:hooked[33]
   new Float:beamcreated[33]
   new global_gravity
   new beam
   public hooktask(parm[])
   {
       new id = parm[0]
       new      user_origin[3],      user_look[3],     user_direction[3],
   move_direction[3]
       new A[3], D[3], buttonadjust[3]
       new acceleration, velocity_towards_A, desired_velocity_towards_A
       new velocity[3], null[3]
       if (!is_user_alive(id))
       {
           release(id)
           return
       }
       if (beamcreated[id] + BEAMLIFE/10 <= get_gametime())
       {
           beamentpoint(id)
       }
       null[0] = 0
       null[1] = 0
       null[2] = 0
       get_user_origin(id, user_origin)
       get_user_origin(id, user_look,2)
       get_user_velocity(id, velocity)
       buttonadjust[0]=0
       buttonadjust[1]=0
       if (get_user_button(id)&IN_FORWARD)
       {
           buttonadjust[0]+=1
       }
       if (get_user_button(id)&IN_BACK)
       {
           buttonadjust[0]-=1
       }
       if (get_user_button(id)&IN_MOVERIGHT)
       {
           buttonadjust[1]+=1
       }
       if (get_user_button(id)&IN_MOVELEFT)
       {
           buttonadjust[1]-=1
       }
       if (get_user_button(id)&IN_JUMP)
       {
           buttonadjust[2]+=1
       }
       if (get_user_button(id)&IN_DUCK)
       {
           buttonadjust[2]-=1
       }
       if (buttonadjust[0] || buttonadjust[1])
       {
           user_direction[0] = user_look[0] - user_origin[0]
           user_direction[1] = user_look[1] - user_origin[1]
           move_direction[0]    =   buttonadjust[0]*user_direction[0]   +
   user_direction[1]*buttonadjust[1]
           move_direction[1]    =   buttonadjust[0]*user_direction[1]   -
   user_direction[0]*buttonadjust[1]
           move_direction[2] = 0
           velocity[0] += floatround(move_direction[0] * MOVEACCELERATION
   * DELTA_T / get_distance(null,move_direction))
           velocity[1] += floatround(move_direction[1] * MOVEACCELERATION
   * DELTA_T / get_distance(null,move_direction))
       }
       if (buttonadjust[2])
       {
           hooklength[id]  -=  floatround(buttonadjust[2]  *  REELSPEED *
   DELTA_T)
       }
       if (hooklength[id] < 100)
       {
           (hooklength[id]) = 100
       }
       A[0] = hooklocation[id][0] - user_origin[0]
       A[1] = hooklocation[id][1] - user_origin[1]
       A[2] = hooklocation[id][2] - user_origin[2]
       D[0] = A[0]*A[2] / get_distance(null,A)
       D[1] = A[1]*A[2] / get_distance(null,A)
       D[2] = -(A[1]*A[1] + A[0]*A[0]) / get_distance(null,A)
       acceleration = - global_gravity * D[2] / get_distance(null,D)
       velocity_towards_A  =  (velocity[0]  * A[0] + velocity[1] * A[1] +
   velocity[2] * A[2]) / get_distance(null,A)
       desired_velocity_towards_A =
   (get_distance(user_origin,hooklocation[id]) - hooklength[id] /*- 10*/)
   * 4
       if (get_distance(null,D)>10)
       {
           velocity[0]  +=  floatround((acceleration  * DELTA_T * D[0]) /
   get_distance(null,D))
           velocity[1]  +=  floatround((acceleration  * DELTA_T * D[1]) /
   get_distance(null,D))
           velocity[2]  +=  floatround((acceleration  * DELTA_T * D[2]) /
   get_distance(null,D))
       }
       velocity[0]  += ((desired_velocity_towards_A - velocity_towards_A)
   * A[0]) / get_distance(null,A)
       velocity[1]  += ((desired_velocity_towards_A - velocity_towards_A)
   * A[1]) / get_distance(null,A)
       velocity[2]  += ((desired_velocity_towards_A - velocity_towards_A)
   * A[2]) / get_distance(null,A)
       set_user_velocity(id, velocity)
   }
   public hook_on(id)
   {
       if     (get_cvar_num("sv_ninjarope")     &&     !hooked[id]     &&
   is_user_alive(id))
       {
           attach(id)
       }
       return PLUGIN_HANDLED
   }
   public hook_off(id)
   {
       if (get_cvar_num("sv_ninjarope") && hooked[id])
       {
           release(id)
       }
       return PLUGIN_HANDLED
   }
   public rope_toggle(id)
   {
       if     (get_cvar_num("sv_ninjarope")     &&     !hooked[id]     &&
   is_user_alive(id))
       {
           attach(id)
       }
       else if (get_cvar_num("sv_ninjarope") && hooked[id])
       {
           release(id)
       }
       return PLUGIN_HANDLED
   }
   public attach(id)
   {
       new parm[1], user_origin[3]
       parm[0] = id
       hooked[id] = true
       get_user_origin(id, user_origin)
       get_user_origin(id, hooklocation[id], 3)
       hooklength[id] = get_distance(hooklocation[id],user_origin)
       global_gravity = get_cvar_num("sv_gravity")
       set_user_gravity(id,0.001)
       beamentpoint(id)
       emit_sound(id,    CHAN_STATIC,    "weapons/xbow_hit2.wav",    1.0,
   ATTN_NORM, 0, PITCH_NORM)
       set_task(DELTA_T, "hooktask", 200+id, parm, 1, "b")
   }
   public release(id)
   {
       hooked[id] = false
       killbeam(id)
       set_user_gravity(id)
       remove_task(200+id)
   }
   public beamentpoint(id)
   {
       message_begin( MSG_BROADCAST, SVC_TEMPENTITY )
       write_byte( TE_BEAMENTPOINT )
       write_short( id )
       write_coord( hooklocation[id][0] )
       write_coord( hooklocation[id][1] )
       write_coord( hooklocation[id][2] )
       write_short( beam )    // sprite index
       write_byte( 0 )        // start frame
       write_byte( 0 )        // framerate
       write_byte( BEAMLIFE )    // life
       write_byte( 10 )    // width
       write_byte( 0 )        // noise
       if (get_user_team(id)==1)        // Terrorist
       {
           write_byte( 255 )    // r, g, b
           write_byte( 0 )    // r, g, b
           write_byte( 0 )    // r, g, b
       }
       else                            // Counter-Terrorist
       {
           write_byte( 0 )    // r, g, b
           write_byte( 0 )    // r, g, b
           write_byte( 255 )    // r, g, b
       }
       write_byte( 150 )    // brightness
       write_byte( 0 )        // speed
       message_end( )
       beamcreated[id] = get_gametime()
   }
   public killbeam(id)
   {
       message_begin( MSG_BROADCAST, SVC_TEMPENTITY )
       write_byte( TE_KILLBEAM )
       write_short( id )
       message_end()
   }
   public new_round(id)
   {
       if (hooked[id])
       {
           release(id)
       }
       return PLUGIN_CONTINUE
   }
   public plugin_precache()
   {
       beam = precache_model("sprites/zbeam4.spr")
       precache_sound("weapons/xbow_hit2.wav")
       return PLUGIN_CONTINUE
   }
   public plugin_init()
   {
       register_plugin("Ninja Rope", "1.0", "SpaceDude")
       register_cvar("sv_ninjarope","1")
       register_clcmd("rope_toggle", "rope_toggle")
       register_clcmd("+rope", "hook_on")
       register_clcmd("-rope", "hook_off")
       register_event("ResetHUD", "new_round", "b")
       return PLUGIN_CONTINUE
   }
