-- name: \\#FF9900\\Tether X -- incompatible: -- description: Gives Mario a tether/grappling hook move to pull and swing across stages. \n \nMade by \\#8888DD\\SkyCaster\\#FFFF66\\09 -- Setting some constants local aim_dist = 1000 local maximum_speed = 160 -- Allocating Actions local sins = sins local coss = coss local ACT_GRAPPLING = allocate_mario_action(ACT_FLAG_MOVING | ACT_FLAG_ATTACKING | ACT_FLAG_AIR) local ACT_WATER_GRAPPLING = allocate_mario_action(ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_ATTACKING) -- Resetting Per Player Data Lists local lakitu_posx = {} local lakitu_posy = {} local lakitu_posz = {} local lakitu_focx = {} local lakitu_focy = {} local lakitu_focz = {} local collisionx = {} local collisiony = {} local collisionz = {} local collisionsurface = {} local crosshair_positionx = {} local crosshair_positiony = {} local crosshair_positionz = {} local cam_grapx = {} local cam_grapy = {} local cam_grapz = {} local freecam_mode = {} local hook_dist = {} local can_grab = {} local curr_aim_dist = {} local tutorial = {} local hook_length = {} local grapple_mode = {} local r_slider = {} local g_slider = {} local b_slider = {} local crosshair_size = {} local pressed_y = {} local fling_cam = {} -- Normalizes a vec3 :p function vec3_normalize(vec) local length = math.sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) -- Prevent division by zero if length == 0 then return {x = 0, y = 0, z = 0} end return { x = vec.x / length, y = vec.y / length, z = vec.z / length } end -- Finds the cross product of two vec3s local function vec3f_cross(a, b) return { x = a.y * b.z - a.z * b.y, y = a.z * b.x - a.x * b.z, z = a.x * b.y - a.y * b.x } end -- Gets a 3D distance between two vectors. local function get_3d_dist(vec1, vec2) return math.sqrt(math.sqr(vec1.x - vec2.x) + math.sqr(vec1.y - vec2.y) + math.sqr(vec1.z - vec2.z)) end -- The grappling state. Bounces off ground, fixes camera, spins, applies force, -- applies drag, spawns particles, and exits state. hook_mario_action(ACT_GRAPPLING, function(m) set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) if gLakituState.mode == CAMERA_MODE_WATER_SURFACE or gLakituState.mode == CAMERA_MODE_BEHIND_MARIO then set_camera_mode(m.area.camera, CAMERA_MODE_RADIAL, 10) end local s = gPlayerSyncTable[m.playerIndex] if collision_find_floor(m.pos.x, m.pos.y, m.pos.z).type == SURFACE_BURNING and m.pos.y < m.floorHeight + 1 then m.health = m.health - 3 * 0x110 m.hurtCounter = m.hurtCounter + 3 * 0x110 return set_mario_action(m, ACT_LAVA_BOOST, 0) end if m.pos.y > m.floorHeight + 2 then perform_air_step(m, 0) else if (m.controller.buttonDown & A_BUTTON) ~= 0 then m.vel.y = 50 perform_air_step(m, 0) play_character_sound(m, CHAR_SOUND_PUNCH_YAH) else perform_ground_step(m) end end m.actionTimer = m.actionTimer + 1 if (m.actionTimer % 10) < 1 then play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject) end if m.wall ~= nil then local dot = vec3f_dot(m.vel, m.wall.normal) if dot < 0 then m.vel.x = m.vel.x - (m.wall.normal.x * dot) m.vel.y = m.vel.y - (m.wall.normal.y * dot) m.vel.z = m.vel.z - (m.wall.normal.z * dot) else m.vel.x = m.vel.x + sins(m.intendedYaw) * 0.1 * m.intendedMag m.vel.z = m.vel.z + coss(m.intendedYaw) * 0.1 * m.intendedMag end elseif m.intendedMag > 10 then m.vel.x = m.vel.x + sins(m.intendedYaw) * 0.1 * m.intendedMag m.vel.z = m.vel.z + coss(m.intendedYaw) * 0.1 * m.intendedMag end local vel_dist = get_3d_dist({x=0, y=0, z=0}, {x=m.vel.x, y=m.vel.y, z=m.vel.z}) if vel_dist > maximum_speed then m.vel.x = m.vel.x / vel_dist * maximum_speed m.vel.y = m.vel.y / vel_dist * maximum_speed m.vel.z = m.vel.z / vel_dist * maximum_speed end m.vel.x = m.vel.x * 0.985 if m.vel.y < 0 then m.vel.y = m.vel.y * 0.985 end m.vel.z = m.vel.z * 0.985 if (m.controller.buttonDown & Z_TRIG) ~= 0 then spawn_mist_particles_variable(1, 0, 5) m.vel.x = m.vel.x * 0.95 m.vel.y = m.vel.y * 0.95 m.vel.z = m.vel.z * 0.95 end if s.hooked == false then if m.prevAction == ACT_FLYING or m.prevAction == ACT_FLYING_TRIPLE_JUMP then play_character_sound(m, CHAR_SOUND_YAHOO) play_sound(SOUND_ACTION_FLYING_FAST, m.marioObj.header.gfx.cameraToObject) return set_mario_action(m, ACT_FLYING, 0) end return set_mario_action(m, ACT_FREEFALL, 0) end end) -- The water grapple state. Accelerates player, sets animation, and returns state. hook_mario_action(ACT_WATER_GRAPPLING, function(m) if m.playerIndex ~= 0 then return end local s = gPlayerSyncTable[m.playerIndex] set_mario_animation(m, MARIO_ANIM_SWIM_PART1) perform_water_step(m) if vec3f_dist({x = 0, y = 0, z = 0}, {x = m.vel.x, y = m.vel.y, z = m.vel.z}) < maximum_speed then m.vel.x = m.vel.x * 1.02 m.vel.y = m.vel.y * 1.02 m.vel.z = m.vel.z * 1.02 end if m.waterLevel < m.pos.y then return set_mario_action(m, ACT_GRAPPLING, 0) end if s.hooked == false or vec3f_dist(m.pos, {x = s.grab_positionx, y = s.grab_positiony, z = s.grab_positionz}) < 100 then return set_mario_action(m, ACT_BREASTSTROKE, 0) end end) -- This function sends out vectors to find a surface to grapple. -- Water or flight grapple shoots in the direction Mario is facing. -- Directional grapple aims in a cone in the direction inputted. -- Reticle grapple aims at the on screen reticle. function find_grapple_collision(m, s) if cam_grapx[m.playerIndex] == nil then cam_grapx[m.playerIndex] = 0 cam_grapy[m.playerIndex] = 0 cam_grapz[m.playerIndex] = 0 end local collision_point local freecam = freecam_mode[m.playerIndex] local dist_rope = -hook_length[m.playerIndex] if (m.action & ACT_FLAG_SWIMMING_OR_FLYING) ~= 0 then local pitch = -m.faceAngle.x local yaw = m.faceAngle.y local vector_facing = {x = sins(yaw) * coss(pitch), y = -sins(pitch), z = coss(yaw) * coss(pitch)} cam_grapx[m.playerIndex] = m.pos.x + vector_facing.x * -dist_rope cam_grapy[m.playerIndex] = m.pos.y + vector_facing.y * -dist_rope cam_grapz[m.playerIndex] = m.pos.z + vector_facing.z * -dist_rope collision_point = collision_find_surface_on_ray(m.pos.x, m.pos.y, m.pos.z, vector_facing.x * -dist_rope, vector_facing.y * -dist_rope, vector_facing.z * -dist_rope, 10.0) return collision_point elseif freecam == true then local vector_facing = {x = sins(m.faceAngle.y), y = 5, z = coss(m.faceAngle.y)} local vector_store = vec3_normalize(vector_facing) if tostring(collision_find_surface_on_ray(m.pos.x, m.pos.y + 10, m.pos.z, vector_store.x * -dist_rope, vector_store.y * -dist_rope, vector_store.z * -dist_rope, 10.0).surface) == "nil" or math.abs(m.controller.stickX) > 10 or math.abs(m.controller.stickY) > 10 or m.area.camera.mode == CAMERA_MODE_C_UP then else return collision_find_surface_on_ray(m.pos.x, m.pos.y + 10, m.pos.z, vector_store.x * -dist_rope, vector_store.y * -dist_rope, vector_store.z * -dist_rope, 10.0) end vector_facing = {x = lakitu_posx[m.playerIndex] - lakitu_focx[m.playerIndex], y = (lakitu_posy[m.playerIndex] - lakitu_focy[m.playerIndex]) * 1, z = lakitu_posz[m.playerIndex] - lakitu_focz[m.playerIndex]} vector_store = vec3_normalize(vector_facing) local global_up = {x = 0, y = 1, z = 0} local right = vec3f_cross(vector_store, global_up) right = vec3_normalize(right) local up = vec3f_cross(right, vector_store) up = vec3_normalize(up) offsetX = 0 offsetY = 1700 / (7000 / -dist_rope) local offset = { x = (right.x * offsetX) + (up.x * offsetY), y = (right.y * offsetX) + (up.y * offsetY), z = (right.z * offsetX) + (up.z * offsetY) } cam_grapx[m.playerIndex] = (lakitu_posx[m.playerIndex] + vector_store.x * (dist_rope) + offset.x) cam_grapy[m.playerIndex] = (lakitu_posy[m.playerIndex] + vector_store.y * (dist_rope) + offset.y) cam_grapz[m.playerIndex] = (lakitu_posz[m.playerIndex] + vector_store.z * (dist_rope) + offset.z) local camera_dist = (get_3d_dist({x = lakitu_posx[m.playerIndex], y = lakitu_posy[m.playerIndex], z = lakitu_posz[m.playerIndex]}, m.pos)) camera_dist = camera_dist / 5 collision_point = collision_find_surface_on_ray(lakitu_posx[m.playerIndex] + vector_store.x * (-camera_dist), lakitu_posy[m.playerIndex] + vector_store.y * (-camera_dist), lakitu_posz[m.playerIndex] + vector_store.z * (-camera_dist), cam_grapx[m.playerIndex] - lakitu_posx[m.playerIndex], cam_grapy[m.playerIndex] - lakitu_posy[m.playerIndex], cam_grapz[m.playerIndex] - lakitu_posz[m.playerIndex], 10.0) return collision_point else local table_o_values = {1, 1.42, 0.7, 2.1, 0.4, 3.73, 0.26, 100, 0, -0.13, -0.26, -0.4, -0.7, -1, -1.42, -2.1, -3.73, -100} local backup local x_vec = sins(m.intendedYaw) local z_vec = coss(m.intendedYaw) for j = 1, #table_o_values do local looking_direction = {x = x_vec, y = table_o_values[j], z = z_vec} looking_direction = vec3_normalize(looking_direction) collision_point = collision_find_surface_on_ray(m.pos.x, m.pos.y + 30, m.pos.z, looking_direction.x * -dist_rope, looking_direction.y * -dist_rope, looking_direction.z * -dist_rope, 3.0) if j == 1 then backup = collision_point end if collision_point.surface ~= nil then cam_grapx[m.playerIndex] = m.pos.x + looking_direction.x * -dist_rope cam_grapy[m.playerIndex] = m.pos.y + looking_direction.y * -dist_rope cam_grapz[m.playerIndex] = m.pos.z + looking_direction.z * -dist_rope return collision_point end end local looking_direction = {x = x_vec, y = table_o_values[1], z = z_vec} looking_direction = vec3_normalize(looking_direction) cam_grapx[m.playerIndex] = m.pos.x + looking_direction.x * -dist_rope cam_grapy[m.playerIndex] = m.pos.y + looking_direction.y * -dist_rope cam_grapz[m.playerIndex] = m.pos.z + looking_direction.z * -dist_rope return backup end end -- Stuff that needs to happen every frame. local function mario_update(m) if m.playerIndex ~= 0 then return end local s = gPlayerSyncTable[m.playerIndex] -- Initializing player values. if tutorial[m.playerIndex] == nil then tutorial[m.playerIndex] = true if camera_config_is_free_cam_enabled() == true then freecam_mode[m.playerIndex] = true grapple_mode[m.playerIndex] = "Reticle" else freecam_mode[m.playerIndex] = false grapple_mode[m.playerIndex] = "Direction" end s.hooked = false can_grab[m.playerIndex] = false hook_dist[m.playerIndex] = 0 hook_length[m.playerIndex] = 4300 curr_aim_dist[m.playerIndex] = aim_dist crosshair_positionx[m.playerIndex] = 0 crosshair_positiony[m.playerIndex] = 0 crosshair_positionz[m.playerIndex] = 0 s.grab_positionx = 0 s.grab_positiony = 0 s.grab_positionz = 0 collisionx[m.playerIndex] = 0 collisiony[m.playerIndex] = 0 collisionz[m.playerIndex] = 0 collisionsurface[m.playerIndex] = 0 lakitu_focx[m.playerIndex] = 0 lakitu_focy[m.playerIndex] = 0 lakitu_focz[m.playerIndex] = 0 lakitu_posx[m.playerIndex] = 0 lakitu_posy[m.playerIndex] = 0 lakitu_posz[m.playerIndex] = 0 pressed_y[m.playerIndex] = false fling_cam[m.playerIndex] = false end lakitu_focx[m.playerIndex] = gLakituState.curFocus.x lakitu_focy[m.playerIndex] = gLakituState.curFocus.y lakitu_focz[m.playerIndex] = gLakituState.curFocus.z lakitu_posx[m.playerIndex] = gLakituState.curPos.x lakitu_posy[m.playerIndex] = gLakituState.curPos.y lakitu_posz[m.playerIndex] = gLakituState.curPos.z -- Release hook for velocity boost. if s.hooked == true and m.action ~= ACT_GRAPPLING and m.action ~= ACT_WATER_GRAPPLING then s.hooked = false m.vel.x = m.vel.x * 1.2 if m.vel.y > 5 then m.vel.y = m.vel.y * 1.2 end m.vel.z = m.vel.z * 1.2 end if (m.action & ACT_FLAG_AIR) == 0 then fling_cam[m.playerIndex] = false end if gLakituState.curFocus.y - gLakituState.curPos.y > 50 and fling_cam[m.playerIndex] == true then if m.area.camera.mode == CAMERA_MODE_BOSS_FIGHT or m.area.camera.mode == CAMERA_MODE_INSIDE_CANNON or m.area.camera.mode == CAMERA_MODE_SPIRAL_STAIRS or m.area.camera.mode == CAMERA_MODE_SLIDE_HOOT then else local diff = {x = gLakituState.curPos.x - gLakituState.curFocus.x, y = 0, z = gLakituState.curPos.z - gLakituState.curFocus.z} diff = vec3_normalize(diff) gLakituState.curPos.x = gLakituState.curFocus.x + diff.x * 1200 gLakituState.curPos.y = gLakituState.curFocus.y - 50 gLakituState.curPos.z = gLakituState.curFocus.z + diff.z * 1200 end end -- Get collision data & organize it. local collision = find_grapple_collision(m, s) collisionx[m.playerIndex] = collision.hitPos.x collisiony[m.playerIndex] = collision.hitPos.y collisionz[m.playerIndex] = collision.hitPos.z if tostring(collision.surface) == "nil" then collisionsurface[m.playerIndex] = 0 else collisionsurface[m.playerIndex] = 1 end if tostring(collisionsurface[m.playerIndex]) == "0" then can_grab[m.playerIndex] = false else can_grab[m.playerIndex] = true end -- Some checks to see if the player can grapple on pushing Y. if (m.controller.buttonPressed & Y_BUTTON ~= 0) then if tostring(collisionsurface[m.playerIndex]) == "0"then s.grab_positionx = 0 s.grab_positiony = 0 s.grab_positionz = 0 s.hooked = false elseif m.action == ACT_PUSHING_DOOR or m.action == ACT_PULLING_DOOR or m.action == ACT_WAITING_FOR_DIALOG or m.action == ACT_READING_NPC_DIALOG or m.action == ACT_READING_SIGN or m.action == ACT_READING_AUTOMATIC_DIALOG or m.action == ACT_INTRO_CUTSCENE or m.action == ACT_CREDITS_CUTSCENE or m.action == ACT_STAR_DANCE_NO_EXIT or m.action == ACT_STAR_DANCE_EXIT or m.action == ACT_STAR_DANCE_WATER or m.action == ACT_DEATH_ON_BACK or m.action == ACT_DEATH_ON_STOMACH or m.action == ACT_DEATH_EXIT or m.action == ACT_DEATH_EXIT_LAND or m.action == ACT_WATER_DEATH or m.action == ACT_STANDING_DEATH or m.action == ACT_QUICKSAND_DEATH or m.action == ACT_UNLOCKING_KEY_DOOR or m.action == ACT_UNLOCKING_STAR_DOOR or m.action == ACT_BUBBLED or m.action == ACT_EXIT_AIRBORNE or m.action == ACT_EXIT_LAND_SAVE_DIALOG or m.action == ACT_DEATH_EXIT or m.action == ACT_DEATH_EXIT_LAND or m.action == ACT_FALL_AFTER_STAR_GRAB or m.action == ACT_BURNING_GROUND or m.action == ACT_BURNING_FALL or m.action == ACT_BURNING_JUMP or m.action == ACT_ELECTROCUTION or m.action == ACT_LAVA_BOOST or m.action == ACT_LAVA_BOOST_LAND or m.action == ACT_IN_CANNON or (m.marioObj.header.gfx.node.flags & GRAPH_RENDER_ACTIVE) == 0 then s.grab_positionx = 0 s.grab_positiony = 0 s.grab_positionz = 0 s.hooked = false else -- Congrats! You can grapple! Organizes grapple data and effects, and applies a small force. s.grab_positionx = collisionx[m.playerIndex] s.grab_positiony = collisiony[m.playerIndex] s.grab_positionz = collisionz[m.playerIndex] local grab_force = vec3_normalize({x = s.grab_positionx - m.pos.x, y = s.grab_positiony - m.pos.y, z = s.grab_positionz - m.pos.z,}) m.vel.x = m.vel.x + grab_force.x * 3.5 m.vel.y = m.vel.y + grab_force.y * 3.5 m.vel.z = m.vel.z + grab_force.z * 3.5 local grab_vector = {x=s.grab_positionx, y=s.grab_positiony, z=s.grab_positionz} hook_dist[m.playerIndex] = get_3d_dist(grab_vector, m.pos) * 1 if aim_dist < hook_dist[m.playerIndex] then curr_aim_dist[m.playerIndex] = aim_dist else curr_aim_dist[m.playerIndex] = hook_dist[m.playerIndex] * 0.95 end play_sound_rbutton_changed() spawn_non_sync_object(id_bhvVertStarParticleSpawner, E_MODEL_NONE, m.pos.x, m.pos.y, m.pos.z, nil) spawn_non_sync_object(id_bhvTriangleParticleSpawner, E_MODEL_NONE, grab_vector.x, grab_vector.y, grab_vector.z, nil) s.hooked = true fling_cam[m.playerIndex] = true if pressed_y[m.playerIndex] == false then play_sound(SOUND_MENU_STAR_SOUND, gLakituState.pos) djui_chat_message_create("TETHER X, by \\#8888DD\\SkyCaster\\#FFFF66\\09") djui_chat_message_create("Check the Mod Settings for more.") end pressed_y[m.playerIndex] = true if (m.action & ACT_FLAG_SWIMMING) ~= 0 then set_mario_action(m, ACT_WATER_GRAPPLING, 0) else set_mario_action(m, ACT_GRAPPLING, 0) end end end -- Check if the player should still be hooked, and runs the swing physics if all is well. if (m.controller.buttonDown & Y_BUTTON ~= 0)then if tostring(s.grab_positionx) == "0" or s.hooked == false then else local grab_force = vec3_normalize({x = s.grab_positionx - m.pos.x, y = s.grab_positiony - m.pos.y, z = s.grab_positionz - m.pos.z,}) local grab_vector = {x=s.grab_positionx, y=s.grab_positiony, z=s.grab_positionz} local dist = get_3d_dist(grab_vector, m.pos) if (m.controller.buttonDown & D_JPAD) ~= 0 then curr_aim_dist[m.playerIndex] = clampf(curr_aim_dist[m.playerIndex] + 30, 300, hook_length[m.playerIndex]) elseif (m.controller.buttonDown & U_JPAD) ~= 0 then curr_aim_dist[m.playerIndex] = clampf(curr_aim_dist[m.playerIndex] - 30, 300, hook_length[m.playerIndex]) elseif curr_aim_dist[m.playerIndex] > 500 then curr_aim_dist[m.playerIndex] = clampf(curr_aim_dist[m.playerIndex] - 1, 300, hook_length[m.playerIndex]) end local strength = 1 + math.min(dist / hook_dist[m.playerIndex], 2.0) / 1 if hook_dist[m.playerIndex] > curr_aim_dist[m.playerIndex] then m.vel.z = m.vel.z + grab_force.z * strength * 3 m.vel.x = m.vel.x + grab_force.x * strength * 3 m.vel.y = m.vel.y + grab_force.y * strength * 3 hook_dist[m.playerIndex] = hook_dist[m.playerIndex] - 15 * (strength + 2) end if hook_dist[m.playerIndex] < curr_aim_dist[m.playerIndex] then hook_dist[m.playerIndex] = dist elseif hook_dist[m.playerIndex] < dist then local dx = (m.pos.x - s.grab_positionx) local dy = (m.pos.y - s.grab_positiony) local dz = (m.pos.z - s.grab_positionz) local nx = dx / dist local ny = dy / dist local nz = dz / dist local radial_speed = (m.vel.x * nx) + (m.vel.y * ny) + (m.vel.z * nz) if radial_speed > 0 then local swing_x = m.vel.x - (radial_speed * nx) local swing_y = m.vel.y - (radial_speed * ny) local swing_z = m.vel.z - (radial_speed * nz) local swing_dist = math.sqrt(swing_x*swing_x + swing_y*swing_y + swing_z*swing_z) if swing_dist > 0.1 then local sx = swing_x / swing_dist local sy = swing_y / swing_dist local sz = swing_z / swing_dist local transferred_energy = radial_speed * 0.23 m.vel.x = swing_x + (sx * transferred_energy) m.vel.y = swing_y + (sy * transferred_energy) m.vel.z = swing_z + (sz * transferred_energy) else m.vel.x = swing_x m.vel.y = swing_y m.vel.z = swing_z end end m.pos.x = s.grab_positionx + nx * (hook_dist[m.playerIndex] + dist) / 2 m.pos.y = s.grab_positiony + ny * (hook_dist[m.playerIndex] + dist) / 2 m.pos.z = s.grab_positionz + nz * (hook_dist[m.playerIndex] + dist) / 2 end local h_speed = math.sqrt(m.vel.x * m.vel.x + m.vel.z * m.vel.z) m.forwardVel = h_speed if h_speed > 0.1 then m.faceAngle.y = math.atan2(m.vel.x, m.vel.z) * (0x10000 / (2 * math.pi)) end end else -- Okay so you can't grapple. Get out. if s.hooked == true then s.hooked = false m.vel.x = m.vel.x * 1.2 if m.vel.y > 5 then m.vel.y = m.vel.y * 1.2 end m.vel.z = m.vel.z * 1.2 end s.hooked = false end end -- Renders the grappling hook line or reticle. local function render_hook_line(playerIndex) local s = gPlayerSyncTable[playerIndex] local m = gMarioStates[playerIndex] -- Creates data if not already created. if s.grab_positionx == nil then s.grab_positionx = 0 s.grab_positiony = 0 s.grab_positionz = 0 end if s.red == nil then local color = network_player_get_override_palette_color(gNetworkPlayers[m.playerIndex], CAP) s.red = color.r s.green = color.g s.blue = color.b r_slider[m.playerIndex] = 255 g_slider[m.playerIndex] = 255 b_slider[m.playerIndex] = 255 end if crosshair_positionx[m.playerIndex] == nil then crosshair_positionx[m.playerIndex] = 0 crosshair_positiony[m.playerIndex] = 0 crosshair_positionz[m.playerIndex] = 0 end if s.smooth_endx == nil then s.smooth_endx = 0 s.smooth_endy = 0 end if collisionx[m.playerIndex] == nil then collisionx[m.playerIndex] = 0 collisiony[m.playerIndex] = 0 collisionz[m.playerIndex] = 0 collisionsurface[m.playerIndex] = 0 end if crosshair_size[m.playerIndex] == nil then crosshair_size[m.playerIndex] = 8 end if (m.marioObj.header.gfx.node.flags & GRAPH_RENDER_ACTIVE) == 0 then return 0 -- The mesh invisible end if s.hooked == false then -- Renders the player's reticle. Local only. if playerIndex ~= 0 then return 0 else local screen_pos = { x = 0, y = 0, z = 0 } djui_hud_set_resolution(RESOLUTION_N64) if tostring(collisionsurface[m.playerIndex]) == "0" then crosshair_positionx[m.playerIndex] = cam_grapx[m.playerIndex] crosshair_positiony[m.playerIndex] = cam_grapy[m.playerIndex] crosshair_positionz[m.playerIndex] = cam_grapz[m.playerIndex] else crosshair_positionx[m.playerIndex] = collisionx[m.playerIndex] crosshair_positiony[m.playerIndex] = collisiony[m.playerIndex] crosshair_positionz[m.playerIndex] = collisionz[m.playerIndex] end local new_position = {x=collisionx[m.playerIndex], y=collisiony[m.playerIndex], z=collisionz[m.playerIndex]} djui_hud_world_pos_to_screen_pos(new_position, screen_pos) s.smooth_endx = s.smooth_endx + (screen_pos.x - s.smooth_endx) * 0.5 s.smooth_endy = s.smooth_endy + (screen_pos.y - s.smooth_endy) * 0.5 if screen_pos.z < 0 then if playerIndex ~= 0 then return else local player_pos = { x = 0, y = 0, z = 0 } djui_hud_world_pos_to_screen_pos(gMarioStates[0].pos, player_pos) if can_grab[m.playerIndex] == true then djui_hud_set_color(255, 120, 120, 255) else djui_hud_set_color(255, 255, 255, 180) end djui_hud_render_line(s.smooth_endx, s.smooth_endy, player_pos.x, player_pos.y- 5, 1.5) if can_grab[m.playerIndex] == true then djui_hud_set_color(255, 0, 0, 255) else djui_hud_set_color(255, 255, 0, 180) end djui_hud_render_line(s.smooth_endx - crosshair_size[playerIndex] / 1.6, s.smooth_endy, s.smooth_endx + crosshair_size[playerIndex] / 1.6, s.smooth_endy, 2) djui_hud_render_line(s.smooth_endx, s.smooth_endy - crosshair_size[playerIndex] / 1.6, s.smooth_endx, s.smooth_endy + crosshair_size[playerIndex] / 1.6, 2) end end end return 0 end if s.grab_positionx == 0 and s.grab_positiony == 0 and s.grab_positionz == 0 then return end -- Render's a grapple line. This can be any player's line. local screen_pos = { x = 0, y = 0, z = 0 } local player_pos = { x = 0, y = 0, z = 0 } djui_hud_set_resolution(RESOLUTION_N64) local new_position = {x=s.grab_positionx, y=s.grab_positiony, z=s.grab_positionz} djui_hud_world_pos_to_screen_pos(new_position, screen_pos) s.smooth_endx = s.smooth_endx + (screen_pos.x - s.smooth_endx) * 1 s.smooth_endy = s.smooth_endy + (screen_pos.y - s.smooth_endy) * 1 djui_hud_world_pos_to_screen_pos(gMarioStates[playerIndex].pos, player_pos) if screen_pos.z < 0 then djui_hud_set_color(s.red, s.green, s.blue, 255) djui_hud_render_line(s.smooth_endx, s.smooth_endy, player_pos.x, player_pos.y - 5, 2) local ticks = get_global_timer() / 3 if s.red < 20 and s.green < 20 and s.blue < 20 then djui_hud_set_color(255, 255, 255, 255) else djui_hud_set_color(0, 0, 0, 255) end djui_hud_render_line(s.smooth_endx - math.sin(ticks) * crosshair_size[playerIndex], s.smooth_endy - math.cos(ticks) * crosshair_size[playerIndex] - 1.5, s.smooth_endx + math.sin(ticks) * crosshair_size[playerIndex], s.smooth_endy + math.cos(ticks) * crosshair_size[playerIndex] - 1.5, 6) djui_hud_render_line(s.smooth_endx - math.cos(-ticks) * crosshair_size[playerIndex], s.smooth_endy - math.sin(-ticks) * crosshair_size[playerIndex] - 1.5, s.smooth_endx + math.cos(-ticks) * crosshair_size[playerIndex], s.smooth_endy + math.sin(-ticks) * crosshair_size[playerIndex] - 1.5, 6) djui_hud_set_color(s.red * 1, s.green * 1, s.blue * 1, 255) local small_cross = crosshair_size[playerIndex] * 0.8 djui_hud_render_line(s.smooth_endx - math.sin(ticks) * small_cross, s.smooth_endy - math.cos(ticks) * small_cross, s.smooth_endx + math.sin(ticks) * small_cross, s.smooth_endy + math.cos(ticks) * small_cross, 3) djui_hud_render_line(s.smooth_endx - math.cos(-ticks) * small_cross, s.smooth_endy - math.sin(-ticks) * small_cross, s.smooth_endx + math.cos(-ticks) * small_cross, s.smooth_endy + math.sin(-ticks) * small_cross, 3) end end -- Renders all hook lines for all players, and checks if players are still connected. -- If not connected, their information is wiped for the next player. local function render_all_hook_lines() if pressed_y[0] == false then djui_hud_set_resolution(RESOLUTION_DJUI) djui_hud_set_font(FONT_MENU) djui_hud_set_color(255, 0, 0, 255) djui_hud_print_text("PRESS Y", djui_hud_get_screen_width() / 2 - (125 * 2), djui_hud_get_screen_height() * (0.8 + sins(clock_elapsed_ticks() * 3000) * 0.02), 2) end for i = 0, #gMarioStates do local local_player = gNetworkPlayers[0] local test_player = gNetworkPlayers[i] if test_player.connected and local_player.currLevelNum == test_player.currLevelNum and local_player.currActNum == test_player.currActNum and local_player.currAreaIndex == test_player.currAreaIndex then render_hook_line(i) end if test_player.connected == false and tutorial[i] == true then lakitu_posx[i] = nil lakitu_posy[i] = nil lakitu_posz[i] = nil lakitu_focx[i] = nil lakitu_focy[i] = nil lakitu_focz[i] = nil collisionx[i] = nil collisiony[i] = nil collisionz[i] = nil collisionsurface[i] = nil crosshair_positionx[i] = nil crosshair_positiony[i] = nil crosshair_positionz[i] = nil cam_grapx[i] = nil cam_grapy[i] = nil cam_grapz[i] = nil freecam_mode[i] = nil hook_dist[i] = nil can_grab[i] = nil curr_aim_dist[i] = nil tutorial[i] = nil hook_length[i] = nil r_slider[i] = nil g_slider[i] = nil b_slider[i] = nil crosshair_size[i] = nil pressed_y[i] = nil fling_cam[i] = false end end end -- Allows water grapple to function as a water type move. local function on_allow_force_water_action(m) if m.action == ACT_WATER_GRAPPLING then return false end return true end -- A safety wipe of player grapple data when warping. hook_event(HOOK_ON_WARP, function() local m = gMarioStates[0] local s = gPlayerSyncTable[0] s.hooked = false s.grab_positionx = m.pos.x s.grab_positiony = m.pos.y s.grab_positionz = m.pos.z end) -- Some hooks. hook_event(HOOK_MARIO_UPDATE, mario_update) hook_event(HOOK_ALLOW_FORCE_WATER_ACTION, on_allow_force_water_action) hook_event(HOOK_ON_HUD_RENDER, render_all_hook_lines) -- The chat commands to change values. hook_chat_command("tx-mode", "Changes the aiming mode. Reticle and Directional.", function(msg) local s = gPlayerSyncTable[0] local m = gMarioStates[0] if freecam_mode[m.playerIndex] == true then freecam_mode[m.playerIndex] = false grapple_mode[m.playerIndex] = "Dircetion" djui_chat_message_create("Switched to Direction Mode. \n \nInput a direction and the hook will aim for a surface in that direction.") else freecam_mode[m.playerIndex] = true djui_chat_message_create("Switched to Reticle Mode. \n \nAim the hook using the crosshair on your screen. Release the Joystick to aim up.") grapple_mode[m.playerIndex] = "Reticle" end return true end) hook_chat_command("tx-length", "Sets hook length. Must be between 2000 and 8000", function(msg) local tried_length = tonumber(msg) if tried_length ~= nil then tried_length = clampf(tried_length, 2000, 8000) hook_length[0] = tried_length djui_chat_message_create("Set hook length to " .. tostring(tried_length)) return true else djui_chat_message_create("String given was not a number.") return true end end) hook_chat_command("tx-control", "Lists TETHER X's controls.", function(msg) djui_chat_message_create("Press and hold Y to activate the tether.") djui_chat_message_create("Hold Z to slow down while tethering.") djui_chat_message_create("Hold A to bounce off the ground while tethering.") djui_chat_message_create("Use the D-PAD to extend and contract the tether.") return true end) -- The menu commands to change values. hook_mod_menu_text("Grapple Settings: ") hook_mod_menu_text(" ") hook_mod_menu_text("Checked: Reticle Unchecked: Direction") hook_mod_menu_checkbox("Change Grapple Mode", (freecam_mode[0] or camera_config_is_free_cam_enabled() or true), function(index, value) local s = gPlayerSyncTable[0] local m = gMarioStates[0] if value == false then freecam_mode[m.playerIndex] = false djui_chat_message_create("Switched to Direction Mode. \n \nInput a direction and the hook will aim for a surface in that direction.") else freecam_mode[m.playerIndex] = true djui_chat_message_create("Switched to Reticle Mode. \n \nAim the hook using the crosshair on your screen. Release the Joystick to aim up.") end return true end) hook_mod_menu_slider("Hook Length (x100)", 43, 20, 80, function(index, value) hook_length[0] = value * 100 end) hook_mod_menu_slider("Crosshair Size", 8, 1, 15, function(index, value) crosshair_size[0] = value end) hook_mod_menu_button("List Controls:", function(msg) djui_chat_message_create("Press and hold Y to activate the tether.") djui_chat_message_create("Hold Z to slow down while tethering.") djui_chat_message_create("Hold A to bounce off the ground while tethering.") djui_chat_message_create("Use the D-PAD to extend and contract the tether.") end) hook_mod_menu_text(" ") hook_mod_menu_text("Colors:") hook_mod_menu_button("\\#FF0000\\RED", function(msg) local s = gPlayerSyncTable[0] s.red = 255 s.green = 0 s.blue = 0 djui_chat_message_create("Switched hook color to RED.") end) hook_mod_menu_button("\\#FFA500\\ORANGE", function(msg) local s = gPlayerSyncTable[0] s.red = 255 s.green = 165 s.blue = 0 djui_chat_message_create("Switched hook color to ORANGE.") end) hook_mod_menu_button("\\#FFFF00\\YELLOW", function(msg) local s = gPlayerSyncTable[0] s.red = 255 s.green = 255 s.blue = 0 djui_chat_message_create("Switched hook color to YELLOW.") end) hook_mod_menu_button("\\#00FF00\\GREEN", function(msg) local s = gPlayerSyncTable[0] s.red = 0 s.green = 255 s.blue = 0 djui_chat_message_create("Switched hook color to GREEN.") end) hook_mod_menu_button("\\#0000FF\\BLUE", function(msg) local s = gPlayerSyncTable[0] s.red = 0 s.green = 0 s.blue = 255 djui_chat_message_create("Switched hook color to BLUE.") end) hook_mod_menu_button("\\#9400D3\\PURPLE", function(msg) local s = gPlayerSyncTable[0] s.red = 148 s.green = 0 s.blue = 211 djui_chat_message_create("Switched hook color to PURPLE.") end) hook_mod_menu_button("\\#FF69B4\\PINK", function(msg) local s = gPlayerSyncTable[0] s.red = 255 s.green = 105 s.blue = 180 djui_chat_message_create("Switched hook color to PINK.") end) hook_mod_menu_text("More Colors:") hook_mod_menu_button("WHITE", function(msg) local s = gPlayerSyncTable[0] s.red = 255 s.green = 255 s.blue = 255 djui_chat_message_create("Switched hook color to WHITE.") end) hook_mod_menu_button("BLACK", function(msg) local s = gPlayerSyncTable[0] s.red = 0 s.green = 0 s.blue = 0 djui_chat_message_create("Switched hook color to BLACK.") end) hook_mod_menu_text("Custom Color:") hook_mod_menu_slider("Red", 255, 0, 255, function(index, value) r_slider[0] = value gPlayerSyncTable[0].red = r_slider[0] gPlayerSyncTable[0].green = g_slider[0] gPlayerSyncTable[0].blue = b_slider[0] end) hook_mod_menu_slider("Green", 255, 0, 255, function(index, value) g_slider[0] = value gPlayerSyncTable[0].red = r_slider[0] gPlayerSyncTable[0].green = g_slider[0] gPlayerSyncTable[0].blue = b_slider[0] end) hook_mod_menu_slider("Blue", 255, 0, 255, function(index, value) b_slider[0] = value gPlayerSyncTable[0].red = r_slider[0] gPlayerSyncTable[0].green = g_slider[0] gPlayerSyncTable[0].blue = b_slider[0] end)