// SPIDERMAN! - BASED ON SPACEDUDE'S NINJA HOOK

/* CVARS - copy and paste to shconfig.cfg

//Spiderman
spiderman_level 0
spiderman_moveacc 140        //How quickly he can move while on the hook
spiderman_reelspeed 400        //How fast hook line reels in
spiderman_hookstyle 2        //1=spacedude, 2=spacedude auto reel (spiderman), 3=cheap kids real    (batgirl)
spiderman_teamcolored 1        //1=teamcolored web lines 0=white web lines
spiderman_maxhooks 60        //Max ammout of hooks allowed (-1 is an unlimited ammount)

*/

#include <superheromod>

// GLOBAL VARIABLES
#define HOOKBEAMLIFE  100
#define HOOK_DELTA_T  0.1  // units per second

new gHeroID
new const gHeroName[] = "Spider-Man"
new bool:gHooked[SH_MAXSLOTS+1]
new gHookLocation[SH_MAXSLOTS+1][3]
new gHookLength[SH_MAXSLOTS+1]
new Float:gHookCreated[SH_MAXSLOTS+1]
new gHooksLeft[SH_MAXSLOTS+1]
new const gSoundWeb[] = "bullchicken/bc_bite2.wav"
new gSpriteWeb
new pCvarMoveAcc, pCvarReelSpeed, pCvarHookStyle
new pCvarMaxHooks, pCvarTeamColored, pCvarSvGravity
//----------------------------------------------------------------------------------------------
public plugin_init()
{
    // Plugin Info
    register_plugin("SUPERHERO Spider-Man", SH_VERSION_STR, "{HOJ} Batman/JTP10181")

    register_dictionary("sh_spiderman.txt")

    // DO NOT EDIT THIS FILE TO CHANGE CVARS, USE THE SHCONFIG.CFG
    new pcvarLevel = register_cvar("spiderman_level", "0")
    pCvarMoveAcc = register_cvar("spiderman_moveacc", "140")
    pCvarReelSpeed = register_cvar("spiderman_reelspeed", "400")
    pCvarHookStyle = register_cvar("spiderman_hookstyle", "2")
    pCvarMaxHooks = register_cvar("spiderman_maxhooks", "60")
    pCvarTeamColored = register_cvar("spiderman_teamcolored", "1")

    // FIRE THE EVENTS TO CREATE THIS SUPERHERO!
    gHeroID = sh_create_hero(gHeroName, pcvarLevel)
    sh_set_hero_info(gHeroID, "Web Swing", "Shoot Web to Swing - Jump Reels In, Duck Reels Out")
    sh_set_hero_bind(gHeroID)

    pCvarSvGravity = get_cvar_pointer("sv_gravity")
}
//----------------------------------------------------------------------------------------------
public plugin_precache()
{
    precache_sound(gSoundWeb)
    gSpriteWeb = precache_model("sprites/zbeam4.spr")
}
//----------------------------------------------------------------------------------------------
public sh_hero_init(id, heroID, mode)
{
    if ( gHeroID != heroID ) return

    if ( gHooked[id] ) spiderman_hook_off(id)

    sh_debug_message(id, 1, "%s %s", gHeroName, mode ? "ADDED" : "DROPPED")
}
//----------------------------------------------------------------------------------------------
public sh_client_spawn(id)
{
    gHooksLeft[id] = get_pcvar_num(pCvarMaxHooks)

    if ( gHooked[id] ) spiderman_hook_off(id)
}
//----------------------------------------------------------------------------------------------
public sh_hero_key(id, heroID, key)
{
    if ( gHeroID != heroID ) return

    switch(key)
    {
        case SH_KEYDOWN: {
            if ( sh_is_freezetime() || gHooked[id] || !is_user_alive(id) ) return

            new hooksleft = gHooksLeft[id]

            if ( hooksleft == 0 ) {
                sh_sound_deny(id)
                return
            }

            if ( hooksleft > 0 ) gHooksLeft[id] = --hooksleft

            if ( -1 < hooksleft < 6 ) {
                client_print(id, print_center, "[SH] %L", LANG_PLAYER, "SH_SPIDERMAN_YOU_LEFT", hooksleft, hooksleft == 1 ? "" : "s")
            }

            gHooked[id] = true

            set_user_info(id, "ROPE", "1")

            new parm[2], user_origin[3]
            parm[0] = id

            get_user_origin(id, user_origin)
            //would a trace line be better?
            get_user_origin(id, gHookLocation[id], 3)

            gHookLength[id] = get_distance(gHookLocation[id], user_origin)

            set_user_gravity(id, 0.001)

            beamentpoint(id)

            emit_sound(id, CHAN_STATIC, gSoundWeb, VOL_NORM, ATTN_NORM, 0, PITCH_NORM)

            parm[1] = get_pcvar_num(pCvarHookStyle)

            set_task(HOOK_DELTA_T, "spiderman_check_web", id, parm, 2, "b")
        }

        case SH_KEYUP: {
            if ( gHooked[id] ) spiderman_hook_off(id)
        }
    }
}
//----------------------------------------------------------------------------------------------
public spiderman_check_web(parm[])
{
    new id = parm[0]

    //new style = parm[1]
    switch(parm[1]) {
        case 1: spiderman_physics(id, false)
        case 2: spiderman_physics(id, true)
        default: spiderman_cheapReel(id)
    }
}
//----------------------------------------------------------------------------------------------
spiderman_physics(id, bool:autoReel)
{
    if ( !gHooked[id] ) return

    if ( !is_user_alive(id) ) {
        spiderman_hook_off(id)
        return
    }

    if ( gHookCreated[id] + HOOKBEAMLIFE/10 <= get_gametime() ) {
        beamentpoint(id)
    }

    new user_origin[3], null[3], A[3], D[3], buttonadjust[3], buttonpress
    new Float:vTowards_A, Float:DvTowards_A, Float:velocity[3]

    get_user_origin(id, user_origin)
    pev(id, pev_velocity, velocity)

    buttonpress = pev(id, pev_button)

    if ( buttonpress & IN_FORWARD ) ++buttonadjust[0]
    if ( buttonpress & IN_BACK ) --buttonadjust[0]

    if ( buttonpress & IN_MOVERIGHT ) ++buttonadjust[1]
    if ( buttonpress & IN_MOVELEFT ) --buttonadjust[1]

    if ( buttonpress & IN_JUMP ) ++buttonadjust[2]
    if ( buttonpress & IN_DUCK ) --buttonadjust[2]

    if ( buttonadjust[0] || buttonadjust[1] ) {
        new user_look[3], move_direction[3]
        get_user_origin(id, user_look, 2)
        user_look[0] -= user_origin[0]
        user_look[1] -= user_origin[1]

        move_direction[0] = buttonadjust[0] * user_look[0] + user_look[1] * buttonadjust[1]
        move_direction[1] = buttonadjust[0] * user_look[1] - user_look[0] * buttonadjust[1]
        move_direction[2] = 0

        new move_dist = get_distance(null, move_direction)
        new Float:accel = get_pcvar_float(pCvarMoveAcc) * HOOK_DELTA_T

        velocity[0] += move_direction[0] * accel / move_dist
        velocity[1] += move_direction[1] * accel / move_dist
    }

    if ( buttonadjust[2] < 0 || (buttonadjust[2] && gHookLength[id] >= 60) ) {
        gHookLength[id] -= floatround(buttonadjust[2] * get_pcvar_float(pCvarReelSpeed) * HOOK_DELTA_T)
    }
    else if ( autoReel && !(buttonpress&IN_DUCK) && gHookLength[id] >= 200) {
        buttonadjust[2] += 1
        gHookLength[id] -= floatround(buttonadjust[2] * get_pcvar_float(pCvarReelSpeed) * HOOK_DELTA_T)
    }

    A[0] = gHookLocation[id][0] - user_origin[0]
    A[1] = gHookLocation[id][1] - user_origin[1]
    A[2] = gHookLocation[id][2] - user_origin[2]

    new distA = get_distance(null, A)
    distA = distA ? distA : 1    // Avoid dividing by 0

    vTowards_A = (velocity[0] * A[0] + velocity[1] * A[1] + velocity[2] * A[2]) / distA
    DvTowards_A = float((get_distance(user_origin, gHookLocation[id]) - gHookLength[id]) * 4)

    D[0] = A[0]*A[2] / distA
    D[1] = A[1]*A[2] / distA
    D[2] = -(A[1]*A[1] + A[0]*A[0]) / distA

    new distD = get_distance(null, D)
    if ( distD > 10 ) {
        new Float:acceleration = ((-get_pcvar_num(pCvarSvGravity)) * D[2] / distD) * HOOK_DELTA_T
        velocity[0] += (acceleration * D[0]) / distD
        velocity[1] += (acceleration * D[1]) / distD
        velocity[2] += (acceleration * D[2]) / distD
    }

    new Float:difference = DvTowards_A - vTowards_A

    velocity[0] += (difference * A[0]) / distA
    velocity[1] += (difference * A[1]) / distA
    velocity[2] += (difference * A[2]) / distA

    set_pev(id, pev_velocity, velocity)
}
//----------------------------------------------------------------------------------------------
spiderman_cheapReel(id)
{
    // Cheat Web - just drags you where you shoot it...

    if ( !gHooked[id] ) return

    if ( !is_user_alive(id) ) {
        spiderman_hook_off(id)
        return
    }

    new user_origin[3]
    new Float:velocity[3]

    get_user_origin(id, user_origin)

    new distance = get_distance(gHookLocation[id], user_origin)

    if ( distance > 60 ) {
        new Float:inverseTime = get_pcvar_float(pCvarReelSpeed) / distance
        velocity[0] = (gHookLocation[id][0] - user_origin[0]) * inverseTime
        velocity[1] = (gHookLocation[id][1] - user_origin[1]) * inverseTime
        velocity[2] = (gHookLocation[id][2] - user_origin[2]) * inverseTime
    }

    set_pev(id, pev_velocity, velocity)
}
//----------------------------------------------------------------------------------------------
spiderman_hook_off(id)
{
    gHooked[id] = false

    set_user_info(id, "ROPE", "0")

    killbeam(id)

    if ( is_user_connected(id) ) sh_reset_min_gravity(id)

    remove_task(id)
}
//----------------------------------------------------------------------------------------------
beamentpoint(id)
{
    if ( !is_user_connected(id) ) return

    new rgb[3] = {250, 250, 250}

    if ( get_pcvar_num(pCvarTeamColored) )
    {
        switch(cs_get_user_team(id))
        {
            case CS_TEAM_T: rgb = {255, 0, 0}
            case CS_TEAM_CT: rgb = {0, 0, 255}
        }
    }

    message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
    write_byte(TE_BEAMENTPOINT)
    write_short(id)
    write_coord(gHookLocation[id][0])
    write_coord(gHookLocation[id][1])
    write_coord(gHookLocation[id][2])
    write_short(gSpriteWeb)    // sprite index
    write_byte(0)        // start frame
    write_byte(0)        // framerate
    write_byte(HOOKBEAMLIFE)// life
    write_byte(10)        // width
    write_byte(0)        // noise
    write_byte(rgb[0])       // r, g, b
    write_byte(rgb[1])       // r, g, b
    write_byte(rgb[2])       // r, g, b
    write_byte(150)        // brightness
    write_byte(0)          // speed
    message_end()

    gHookCreated[id] = get_gametime()
}
//----------------------------------------------------------------------------------------------
killbeam(id)
{
    message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
    write_byte(TE_KILLBEAM)
    write_short(id)
    message_end()
}
//----------------------------------------------------------------------------------------------
public sh_client_death(victim)
{
    if ( victim < 1 || victim > sh_maxplayers() ) return

    if ( gHooked[victim] ) spiderman_hook_off(victim)
}
//----------------------------------------------------------------------------------------------
public client_disconnected(id)
{
    // stupid check but lets see
    if ( id < 1 || id > sh_maxplayers() ) return

    // Yeah don't want any left over residuals
    remove_task(id)
}
//----------------------------------------------------------------------------------------------