-- Jonny's Tutorial v1.0local globaltimeouts = { }function set_timeout(timeout, fn) if fn then table.insert(globaltimeouts, { timeleft=timeout, func=fn } ) endendfunction run_timeout(delta) delta = delta or 1 -- Prevent it from being nil local final = { } for i,t in ipairs(globaltimeouts) do t.timeleft = t.timeleft - delta if (t.timeleft < 0) then t.func() else table.insert(final, t) end end globaltimeouts = finalend-- Some global vars (global within this chunk)local templen1, templen2, templen3 = 0, 0, 0local msg1, msg2, msg3 = '', '', ''local stepnumber = 0local font_type = FONTS.MEDIUMlocal font_info = get_font_info(font_type)-- Start buttonlocal BTN_UP = 0local BTN_HOVER = 1local BTN_DOWN = 2-- Note: anchor point for drawing stuff is at top-leftlocal win_w, win_h = get_window_size()local button = { pos = { x=win_w-100,y=win_h-40 }, dim = { w=32,h=32 }, texture = load_texture('tutorial/next.tga'), state = BTN_UP, enabled = false, onClick = function() advance() end, textx = win_w / 2 - 85, texty = win_h-35 }function button:hit(x, y) return not ( (x < self.pos.x or x > self.pos.x + self.dim.w) or (y < self.pos.y or y > self.pos.y + self.dim.h) )endfunction button.mouse_move(x, y) if (button.enabled == true and button:hit(x, y) == true) then if (button.state == BTN_UP) then button.state = BTN_HOVER end return 1 end button.state = BTN_UP return 0endfunction button.mouse_up(mouse_btn, x, y) if (button:hit(x, y) == true) then if (button.enabled == true) then button.state = BTN_HOVER button.onClick() end return 1 end button.state = BTN_UP return 0endfunction button.mouse_down(mouse_btn, x, y) if (button:hit(x, y) == true) then if (button.enabled == true) then button.state = BTN_DOWN end return 1 end return 0endfunction button:draw() local x, y = self.pos.x, self.pos.y local w, h = self.dim.w, self.dim.h if (self.enabled == false) then set_color(0, 0, 0, 0.3) draw_quad(x, y, w, h, self.texture) return end if self.state == BTN_HOVER then x, y = x - 2, y - 2 w, h = w + 4, h + 4 elseif self.state == BTN_DOWN then x, y = x, y w, h = w + 4, h + 4 end set_color(1, 1, 1, 1) draw_quad(x, y, w, h, self.texture) set_color(0, 0, 0, 0.5) draw_center_text("Continue", self.texty, 1, self.textx)end-- End button-- set_message - sets up to three lines of text for drawingfunction set_message(str1, str2, str3, mid) templen1, templen2, templen3 = 0, 0, 0 msg1 = str1 or '' msg2 = str2 or '' msg3 = str3 or '' mid_print = mid alpha = nil draw = on_draw2d_scrollerendfunction change_message(timeout, str1, str2, str3, mid) alpha = 1 draw = on_draw2d set_timeout(timeout, function() set_message(str1, str2, str3, mid) end)end-- Joint Tooltiplocal joint_text = nilfunction set_joint_tooltip(player, joint) if (joint ~= -1) then local joint_info = get_joint_info(player, joint) joint_text = { } joint_text['player'] = player joint_text['joint'] = joint joint_text['joint_name'] = joint_info.name joint_text['pos'] = { x=0, y=0 } joint_text['pos'].x, joint_text['pos'].y = get_joint_screen_pos(player, joint) else joint_text = nil endendlocal function draw_joint_tooltip() if (joint_text ~= nil) then -- Cannot get joint position while drawing (limitation) set_color(0.5, 0.5, 0.5, 0.4) draw_quad(joint_text.pos.x + 30, joint_text.pos.y + 10, 160, 30) set_color(0.0, 0.0, 0.0, 1.0) draw_text(joint_text.joint_name, joint_text.pos.x + 40, joint_text.pos.y + 15) -- Draw the state name set_color(0.7, 0.7, 0.7, 0.3) draw_quad(joint_text.pos.x + 30, joint_text.pos.y + 40, 160, 30) set_color(0.0, 0.3, 0.0, 1.0) draw_text(get_joint_info(joint_text.player, joint_text.joint).screen_state, joint_text.pos.x + 40, joint_text.pos.y + 45) endend-- GAME EVENT CALLBACKS ---- on_new_game()-- This is called every time a new game startsfunction on_new_game() prev_button_state = get_option("buttons") prev_footer_state = get_option("text") set_option("buttons", 0) set_option("text", 0) -- Set number of frames set_default_rules() run_cmd("setframes 100") select_player(-1) stepnumber = 0 set_message("--------", "Tutorial!", "--------", true) add_hook("joint_select", "tutorial1_extra", set_joint_tooltip) -- Hook for joint tooltip add_hook("mouse_button_down", "tutorial1_button", button.mouse_down) add_hook("mouse_button_up", "tutorial1_button", button.mouse_up) add_hook("mouse_move", "tutorial1_button", button.mouse_move) add_hook("key_up", "tutorial1_extra", function(key) if (key==32) then return 1 end end ) -- Prevent space from being pressed set_timeout(30, next_step)end-- on_end_game()function on_end_game() set_message("That's the end of a match", "Hit 'space' to watch the replay", "") add_hook("key_up", "tutorial1", function() return 1 end) set_timeout(60, function() remove_hook("key_up", "tutorial1") end) -- Remember to remove that extra hook remove_hooks("tutorial1_extra") remove_hooks("tutorial1_button")end-- on_leave_game-- On exiting the tutorialfunction on_leave_game() set_option("buttons", prev_button_state) set_option("text", prev_footer_state) remove_hooks("tutorial1") -- Remember to remove that extra hook remove_hooks("tutorial1_extra") remove_hooks("tutorial1_button")endfunction on_draw2d() local width, height = get_window_size() local y_mid = height - font_info.height * 2 if (mid_print == true) then y_mid = height/2 - font_info.height end set_color(0, 0, 0, alpha or 1) draw_center_text(msg1, y_mid - font_info.height, font_type) draw_center_text(msg2, y_mid, font_type) draw_center_text(msg3, y_mid + font_info.height, font_type) if (alpha) then alpha = alpha - 0.01 if (alpha < 0) then alpha = 0 end endendfunction on_draw2d_scroller() local width, height = get_window_size() local y_mid = height - font_info.height * 2 if (mid_print == true) then y_mid = height/2 - font_info.height end set_color(0,0,0,1) draw_center_text(string.sub(msg1, 1, templen1), y_mid - font_info.height, font_type) templen1 = templen1 + 1 if templen1 >= string.len(msg1) then draw_center_text(string.sub(msg2, 1, templen2), y_mid, font_type) templen2 = templen2 + 1 if templen2 >= string.len(msg2) then draw_center_text(string.sub(msg3, 1, templen3), y_mid + font_info.height, font_type) templen3 = templen3 + 1 end endend-- Init it to the normal drawing funcdraw = on_draw2d_scroller-- Set the hooksadd_hook("new_game", "tutorial1", on_new_game)add_hook("end_game", "tutorial1", on_end_game)add_hook("leave_game", "tutorial1", on_leave_game)add_hook("draw2d", "tutorial1", function() run_timeout(1); draw_joint_tooltip(); draw(); button:draw() end)function advance() button.enabled = false stepnumber = stepnumber + 1 if (steps[stepnumber] ~= nil) then steps[stepnumber]() endendfunction next_step() button.enabled = trueend-- Each step of the tutorial is a function-- Introductionfunction intro() remove_hook("key_up", "tutorial1") change_message(100, "Welcome to Toribash", "The ragdoll-physics turn-based fighting game") set_timeout(100, next_step) -- NOTE: Adding a hook to prevent space key being pressed add_hook("key_up", "tutorial1_extra", function(key) if (key == 32) then return 1 end end)endfunction intro2() change_message(100, "You can experiment with the ragdolls", "and come up with interesting moves") set_timeout(100, next_step)end-- Step 1: Move camerafunction move_camera() local key_func = function(key) if (key==string.byte('w') or key==string.byte('a') or key==string.byte('s') or key==string.byte('d')) then remove_hook("key_up", "tutorial1") change_message(100, "Good") set_timeout(100, next_step) end end change_message(140, "First, try moving the camera with the", "'w', 'a', 's' and 'd' keys", "") add_hook("key_up", "tutorial1", key_func)end-- Step 2: Move cam up and downfunction move_cam_updown() local key_func = function(key) if (key == string.byte('w') or key == string.byte('s')) and get_shift_key_state() > 0 then remove_hook("key_up", "tutorial1") change_message(100, "Good, spend some time", "getting used to the controls") set_timeout(100, next_step) end end change_message(140, "Now move the camera up/down", "by holding 'shift' and ", "pressing the 'w' or 's' keys") add_hook("key_up", "tutorial1", key_func)end-- Step 3: Select the playerfunction select_player_step() local function player_select(player) if (player == 0) then remove_hook("player_select", "tutorial1") change_message(100, "That's right.", "Clicking on the player selects him") next_step() end end change_message(100, "Now for some action.", "Select the red player by clicking on him") if (get_world_state().selected_player == 0) then next_step() else add_hook("player_select", "tutorial1", player_select) endend-- Step 4: Hold all jointsfunction hold_all_step() local key_func = function(key) if (key==string.byte('c') and get_world_state().selected_player == 0) then remove_hook("key_up", "tutorial1") change_message(100, "The 'c' key is useful in multiplayer matches,", "where you need to toggle your joints quickly") next_step() end end change_message(110, "With the red player selected,", "hit 'c' a few times to toggle", "the hold or relax state for ALL joints") add_hook("key_up", "tutorial1", key_func)end-- Step 5: Explain joint usagefunction explain_joints() change_message(100, "The spheres on the player are joints.", "Joints control how your player moves.") set_timeout(100, next_step)endfunction explain_joints2() change_message(100, "Joints have 4 states:", "hold, relax, extend, contract") set_timeout(100, next_step)end-- Step 6: Select jointfunction mouse_over_joint() local joint_select = function(player, joint) remove_hook("joint_select", "tutorial1") set_timeout(100, function() change_message(100, "That's how you select a joint") ; next_step() end) end change_message(100, "Move your mouse cursor", "over any joint to select it.", "") add_hook("joint_select", "tutorial1", joint_select)end-- Step 7: Manipulate jointfunction manipulate_joint_z() local key_func = function(key) if (key==string.byte('z') and get_world_state().selected_joint > -1) then remove_hook("key_up", "tutorial1") set_timeout(100, function() change_message(100, "The 'z' key applies a force that", "rotates the joint in one of", "two directions") end) set_timeout(200, next_step) end end change_message(100, "With the cursor over the joint,", "use the 'z' key", "to apply a force on the joint") add_hook("key_up", "tutorial1", key_func)endfunction manipulate_joint_x() local key_func = function(key) if (key==string.byte('x') and get_world_state().selected_joint > -1) then remove_hook("key_up", "tutorial1") set_timeout(100, function() change_message(100, "The 'x' key makes a joint", "stay in its position, or", "relaxes the joint") end) set_timeout(200, next_step) end end change_message(100, "Select a joint and", "use the 'x' key", "to make the joint hold or relax") add_hook("key_up", "tutorial1", key_func)end-- Manipulate more and explain ghostfunction manipulate_more() change_message(100, "Have you noticed the", "translucent figure that's moving?") set_timeout(260, function() change_message(100, "We call it a 'ghost'.", "The ghost tells you what will happen", "when you end your turn") end) set_timeout(350, next_step)end-- Now to teach the decaplocal function arrow_keys_only(key) if (key==string.byte('w') or key==string.byte('a') or key==string.byte('s') or key==string.byte('d')) then return 0 end return 1endlocal decap = { cur_step = 1 }decap.make_step = function(msg1, msg2, msg3, check_joint, check_state) return function() local key_func = function(key) if (key == string.byte('z')) then if (get_world_state().selected_joint == check_joint) then if (get_joint_info(0, check_joint).state == check_state) then add_hook("key_up", "tutorial1", arrow_keys_only) decap.cur_step = decap.cur_step + 1 set_timeout(60, decap[decap.cur_step]) end else return 1 end else return arrow_keys_only(key) end end change_message(100, msg1, msg2, msg3) set_timeout(100, function() add_hook("key_up", "tutorial1", key_func) end) add_hook("mouse_button_down", "tutorial1", function() return 1 end) endenddecap[1] = function () change_message(100, "Now for some real action") set_timeout(160, decap[2])enddecap[2] = function() local key_func = function(key) if (key == string.byte('c') and get_world_state().selected_player == 0 and get_joint_info(0, JOINTS.L_KNEE).state ~= JOINT_STATE.HOLD) then -- QUICK HACK! to check if the next will be hold all add_hook("key_up", "tutorial1", arrow_keys_only) add_hook("mouse_button_down", "tutorial1", function() return 1 end) remove_hook("key_up", "tutorial1_extra") set_timeout(60, decap[3]) -- Set Uke to hold all for k,v in pairs(JOINTS) do set_joint_state(1, v, JOINT_STATE.HOLD) end end end change_message(100, "Use the 'c' key to toggle all your joints", "to the 'HOLD' state") add_hook("key_up", "tutorial1", key_func) decap.cur_step = 3enddecap[3] = decap.make_step("Contract the left pecs", "by selecting it and hitting the", "'z' key twice", JOINTS.L_PECS, JOINT_STATE.FORWARD)decap[4] = decap.make_step("Next, contract the right pecs", "by selecting it and pressing the", "'z' key twice", JOINTS.R_PECS, JOINT_STATE.FORWARD)decap[5] = decap.make_step("Now, raise the left shoulder", "by selecting it and hitting the", "'z' key twice", JOINTS.L_SHOULDER, JOINT_STATE.FORWARD)decap[6] = decap.make_step("Now, raise the right shoulder", "by selecting it and pressing the", "'z' key twice", JOINTS.R_SHOULDER, JOINT_STATE.FORWARD)decap[7] = function() local enter_freeze = function() remove_hook("enter_freeze", "tutorial1") add_hook("key_up", "tutorial1", arrow_keys_only) change_message(100, "The number at the top decreased.", "That's the countdown timer, it shows you the number", "of frames left for this match") set_timeout(60, next_step) end change_message(100, "We're going to end our turn now.", "Hit space to end") add_hook("enter_freeze", "tutorial1", enter_freeze) add_hook("key_up", "tutorial1", function(key) if key==32 then return 0 else return arrow_keys_only(key) end end ) decap.cur_step = decap.cur_step + 1enddecap[8] = decap.make_step("Lower the left shoulder", "by selecting it and pressing the", "'z' key", JOINTS.L_SHOULDER, JOINT_STATE.BACK)decap[9] = decap.make_step("Lower the right shoulder", "by selecting it and pressing the", "'z' key", JOINTS.R_SHOULDER, JOINT_STATE.BACK)decap[10] = decap.make_step("Raise the left leg", "by selecting the left hip", "and pressing the 'z' key", JOINTS.L_HIP, JOINT_STATE.HOLD)decap[11] = decap.make_step("Raise the right leg", "by selecting the right hip", "and pressing the 'z' key", JOINTS.R_HIP, JOINT_STATE.HOLD)decap[12] = function() local enter_freeze = function() remove_hook("enter_freeze", "tutorial1") add_hook("key_up", "tutorial1", arrow_keys_only) set_timeout(60, advance) end change_message(100, "We're going to end our turn now.", "Hit space to end") add_hook("enter_freeze", "tutorial1", enter_freeze) add_hook("key_up", "tutorial1", function(key) if key==32 then return 0 else return arrow_keys_only(key) end end )end-- Step 8: End of decapfunction enter_next_turn() local key_func = function(key) if (key==32) then change_message(100, "There! A decap!") advance() return 0 else return arrow_keys_only(key) end end change_message(100, "Almost there. Hit space to continue.", "Hint: you can click on the hands to", "make them grab") add_hook("key_up", "tutorial1", key_func) remove_hook("mouse_button_down", "tutorial1")end-- Step 9: Practisefunction freeze_turn() local enter_freeze = function() remove_hook("enter_freeze", "tutorial1") set_timeout(140, function() change_message(100, "Continue making changes using the", "'z', 'x' and 'c' keys", "and press 'space' to end your turn.") end ) end add_hook("enter_freeze", "tutorial1", enter_freeze) remove_hook("key_up", "tutorial1")end-- List of all the stepssteps = { intro, intro2, move_camera, move_cam_updown, select_player_step, hold_all_step, explain_joints, explain_joints2, mouse_over_joint, manipulate_joint_z, manipulate_joint_x, manipulate_more, decap[1], decap[8], enter_next_turn, freeze_turn,}