Files
gtav-src/script/dev_ng/singleplayer/scripts/debug/nodeViewer.sc
T
2025-09-29 00:52:08 +02:00

1352 lines
39 KiB
Python
Executable File

// NODE VIEWER - test script for placing and editing node paths
// Rob Bray 06/11/09
USING "rage_builtins.sch"
USING "globals.sch"
#IF IS_FINAL_BUILD
script
endscript
#endif
#IF IS_DEBUG_BUILD
USING "brains.sch"
USING "script_player.sch"
USING "commands_script.sch"
USING "commands_pad.sch"
USING "commands_graphics.sch"
USING "commands_camera.sch"
USING "commands_streaming.sch"
USING "commands_interiors.sch"
USING "commands_object.sch"
USING "commands_audio.sch"
USING "script_drawing.sch"
// CONSTANTS
CONST_INT NUMBER_OF_NODES_PER_SET 12
CONST_INT NUMBER_OF_SETS 2
CONST_INT NUMBER_OF_SUB_MENU_ITEMS 4
CONST_INT SPHERE_ALPHA 170
CONST_INT BOX_ALPHA 115
CONST_FLOAT POINT_NODE_SIZE 1.65
CONST_FLOAT BOX_NODE_SIZE 1.4
CONST_FLOAT POINT_NODE_TEXT_OFFSET 1.95
CONST_FLOAT BOX_NODE_TEXT_OFFSET 1.65
// ENUMS
ENUM VIEWER_STATE_ENUM
STATE_MENU = 0,
STATE_FLYCAM
ENDENUM
VIEWER_STATE_ENUM current_viewer_state = STATE_MENU
ENUM SUB_MENU_ENUM
SUB_MENU_HIDDEN = 0,
SUB_MENU_EDIT,
SUB_MENU_COPY,
SUB_MENU_DELETE
ENDENUM
SUB_MENU_ENUM current_sub_menu_selection = SUB_MENU_HIDDEN
ENUM NODE_TYPE_ENUM
TYPE_FREE_NODE = 0,
TYPE_POINT_NODE,
TYPE_BOX_FIRST_NODE,
TYPE_BOX_SECOND_NODE
ENDENUM
ENUM AUDIO_CONFIRM_ENUM
AUDIO_CONFIRM_CHANGE = 0,
AUDIO_CONFIRM_EDIT
ENDENUM
// STRUCTURES
STRUCT NODE_STRUCT
NODE_TYPE_ENUM type
VECTOR v_coords
TEXT_LABEL_31 tl_label
INT i_creation_number
INT i_box_counterpart_index
ENDSTRUCT
STRUCT NODE_SET_STRUCT
NODE_STRUCT nodes[NUMBER_OF_NODES_PER_SET]
TEXT_LABEL_31 tl_name
BOOL b_open
//INT i_creation_number
INT i_node_creation_counter
ENDSTRUCT
// GLOBALS
NODE_SET_STRUCT node_sets[NUMBER_OF_SETS]
CAMERA_INDEX debug_cam
CAMERA_INDEX point_cam
CAMERA_INDEX menu_cam
INT i_current_set_index = -1
INT i_current_node_index = -1
INT i_set_creation_counter
INT i_white = INT_COLOUR(255, 255, 255, 255)
INT i_red = INT_COLOUR(255, 0, 0, 255)
BOOL b_do_update_widget
BOOL b_update_label
BOOL b_update_name
BOOL b_show_quit_confirm
BOOL b_set_menu_cam
BOOL b_equidistant_drop
BOOL b_draw_help = TRUE
BOOL b_quit_tool
VECTOR v_cam_coords
//VECTOR v_cam_rot
FLOAT f_distance_travelled
FLOAT f_distance_to_travel_for_next_drop = 50.0
VECTOR v_last_cam_coords
WIDGET_GROUP_ID w_nodes_group
TEXT_WIDGET_ID w_node_label
TEXT_WIDGET_ID w_set_name
// FUNCTIONS
// play audio confirmation of action
PROC DO_SOUND(AUDIO_CONFIRM_ENUM confirm_type)
IF confirm_type = AUDIO_CONFIRM_EDIT
PLAY_SOUND_FRONTEND(-1, "AK47_COCK_PULL")
ELIF confirm_type = AUDIO_CONFIRM_CHANGE
PLAY_SOUND_FRONTEND(-1, "GENERAL_WEAPONS_MP5_SLAM")
ENDIF
ENDPROC
// create a new set
PROC CREATE_NEW_SET(INT i_index)
INT i
// fill set attributes
REPEAT NUMBER_OF_NODES_PER_SET i
node_sets[i_index].nodes[i].type = TYPE_FREE_NODE
node_sets[i_index].nodes[i].v_coords = <<0,0,0>>
node_sets[i_index].nodes[i].tl_label = ""
//node_sets[i_index].nodes[i].i_creation_number = -1
node_sets[i_index].nodes[i].i_box_counterpart_index = -1
ENDREPEAT
node_sets[i_index].b_open = TRUE
node_sets[i_index].tl_name = "Set "
node_sets[i_index].tl_name += i_set_creation_counter
//node_sets[i_index].i_creation_number = i_set_creation_counter
node_sets[i_index].i_node_creation_counter = 0
i_set_creation_counter++
ENDPROC
// copy data from one set to another
FUNC BOOL COPY_SET_DATA_INTO_SLOT(INT i_from_index, INT i_to_index)
INT i
IF i_from_index >= 0
AND i_to_index >= 0
IF node_sets[i_from_index].b_open
IF NOT node_sets[i_to_index].b_open
// fill set attributes
REPEAT NUMBER_OF_NODES_PER_SET i
node_sets[i_to_index].nodes[i].type = node_sets[i_from_index].nodes[i].type
node_sets[i_to_index].nodes[i].v_coords = node_sets[i_from_index].nodes[i].v_coords
node_sets[i_to_index].nodes[i].tl_label = node_sets[i_from_index].nodes[i].tl_label
node_sets[i_to_index].nodes[i].i_creation_number = node_sets[i_from_index].nodes[i].i_creation_number
node_sets[i_to_index].nodes[i].i_box_counterpart_index = node_sets[i_from_index].nodes[i].i_box_counterpart_index
ENDREPEAT
node_sets[i_to_index].b_open = TRUE
TEXT_LABEL_63 tl_name = "(Copy) "
tl_name += node_sets[i_from_index].tl_name
INT i_name_length = GET_LENGTH_OF_LITERAL_STRING(node_sets[i_to_index].tl_name)
IF i_name_length >= 15
tl_name = GET_FIRST_N_CHARACTERS_OF_STRING(tl_name, 15)
ENDIF
node_sets[i_to_index].tl_name = tl_name
//node_sets[i_to_index].i_creation_number = i_set_creation_counter
node_sets[i_to_index].i_node_creation_counter = node_sets[i_from_index].i_node_creation_counter
RETURN TRUE
ELSE
PRINTSTRING("Attempting to copy to an open set") PRINTNL()
ENDIF
ELSE
PRINTSTRING("Attempting to copy from a closed set") PRINTNL()
ENDIF
ELSE
PRINTSTRING("One or both copy slots do not exist") PRINTNL()
ENDIF
RETURN FALSE
ENDFUNC
// delete a set
PROC DELETE_SET(INT i_index)
IF i_index >= 0
IF node_sets[i_index].b_open
node_sets[i_index].b_open = FALSE
ENDIF
ENDIF
ENDPROC
// fill the data of initial sets (temp for testing)
PROC FILL_INITIAL_SETS()
INT i
REPEAT NUMBER_OF_SETS i
CREATE_NEW_SET(i)
ENDREPEAT
ENDPROC
// get the first available index of a free set
FUNC INT GET_FREE_SET_INDEX()
INT i
REPEAT NUMBER_OF_SETS i
IF NOT node_sets[i].b_open // only return sets that are not open
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
// get next open set iterating through array
FUNC INT GET_NEXT_OPEN_SET(INT i_index)
INT i
IF i_index >= 0
FOR i = i_index TO (NUMBER_OF_SETS - 1)
IF node_sets[i].b_open // only return open sets
AND i <> i_index // don't return this index
RETURN i
ENDIF
ENDFOR
// loop round if not found any
FOR i = 0 TO i_index
IF node_sets[i].b_open // only return open sets
AND i <> i_index // don't return this index
RETURN i
ENDIF
ENDFOR
ENDIF
RETURN -1
ENDFUNC
// get previous open set iterating through array
FUNC INT GET_PREVIOUS_OPEN_SET(INT i_index)
INT i
IF i_index >= 0
FOR i = i_index TO 0 STEP -1
IF node_sets[i].b_open // only return open sets
AND i <> i_index // don't return this index
RETURN i
ENDIF
ENDFOR
// loop round if not found any
FOR i = (NUMBER_OF_SETS - 1) TO i_index STEP -1
IF node_sets[i].b_open // only return open sets
AND i <> i_index // don't return this index
RETURN i
ENDIF
ENDFOR
ENDIF
RETURN -1
ENDFUNC
// set the current set index
PROC SET_CURRENT_SET_INDEX(INT i_index)
i_current_set_index = i_index
b_do_update_widget = TRUE
ENDPROC
// set set name
PROC SET_SET_NAME(INT i_index, STRING s_name)
node_sets[i_index].tl_name = ""
node_sets[i_index].tl_name += s_name
ENDPROC
// set the current node index
PROC SET_CURRENT_NODE_INDEX(INT i_index)
i_current_node_index = i_index
b_do_update_widget = TRUE
ENDPROC
// get the node at a specific index
FUNC NODE_STRUCT GET_NODE_AT_INDEX(INT i_index)
//PRINTSTRING("GET_NODE_AT_INDEX - Index = ")
//PRINTINT(i_index)
//PRINTSTRING(" i_current_set_index = ")
//PRINTINT(i_current_set_index)
//PRINTNL()
RETURN node_sets[i_current_set_index].nodes[i_index]
ENDFUNC
// get the index of the closest node to the specified index
FUNC INT GET_CLOSEST_NODE_INDEX_TO_NODE(INT i_index)
INT i
NODE_STRUCT this_node = GET_NODE_AT_INDEX(i_index)
INT i_closest_index = -1
FLOAT f_closest_distance = -1
FLOAT f_distance
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
AND i <> i_index // don't check specified index
f_distance = GET_DISTANCE_BETWEEN_COORDS(this_node.v_coords, check_node.v_coords)
IF f_distance <= f_closest_distance
OR f_closest_distance = -1
// if closer than previous closest, set to return this index
i_closest_index = i
f_closest_distance = f_distance
ENDIF
ENDIF
ENDREPEAT
RETURN i_closest_index
ENDFUNC
// get the node with the lowest creation number
FUNC INT GET_FIRST_NODE_INDEX()
INT i
INT i_closest_index = -1
INT i_closest_creation_number = -1
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
IF check_node.i_creation_number < i_closest_creation_number
OR i_closest_creation_number = -1
i_closest_index = i
i_closest_creation_number = check_node.i_creation_number
ENDIF
ENDIF
ENDREPEAT
RETURN i_closest_index
ENDFUNC
// get next node in order of creation
FUNC INT GET_NEXT_NODE_INDEX_IN_ORDER(INT i_index, BOOL b_loop = FALSE)
INT i
NODE_STRUCT this_node = GET_NODE_AT_INDEX(i_index)
INT i_closest_index = -1
INT i_closest_creation_number = -1
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
AND i <> i_index // don't check specified index
IF check_node.i_creation_number > this_node.i_creation_number
AND (check_node.i_creation_number < i_closest_creation_number OR i_closest_creation_number = -1)
i_closest_index = i
i_closest_creation_number = check_node.i_creation_number
ENDIF
ENDIF
ENDREPEAT
// if loop around, find lowest
IF b_loop
IF i_closest_index = -1
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
AND i <> i_index // don't check specified index
IF check_node.i_creation_number < this_node.i_creation_number
AND (check_node.i_creation_number < i_closest_creation_number OR i_closest_creation_number = -1)
i_closest_index = i
i_closest_creation_number = check_node.i_creation_number
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDIF
RETURN i_closest_index
ENDFUNC
// get previous node in order of creation
FUNC INT GET_PREVIOUS_NODE_INDEX_IN_ORDER(INT i_index, BOOL b_loop = FALSE)
INT i
NODE_STRUCT this_node = GET_NODE_AT_INDEX(i_index)
INT i_closest_index = -1
INT i_closest_creation_number = -1
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
AND i <> i_index // don't check specified index
IF check_node.i_creation_number < this_node.i_creation_number
AND (check_node.i_creation_number > i_closest_creation_number OR i_closest_creation_number = -1)
i_closest_index = i
i_closest_creation_number = check_node.i_creation_number
ENDIF
ENDIF
ENDREPEAT
// if loop around, find highest
IF b_loop
IF i_closest_index = -1
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type <> TYPE_FREE_NODE // only check active nodes
AND i <> i_index // don't check specified index
IF check_node.i_creation_number > this_node.i_creation_number
AND (check_node.i_creation_number > i_closest_creation_number OR i_closest_creation_number = -1)
i_closest_index = i
i_closest_creation_number = check_node.i_creation_number
ENDIF
ENDIF
ENDREPEAT
ENDIF
ENDIF
RETURN i_closest_index
ENDFUNC
// get coords
FUNC VECTOR GET_NODE_COORDS_AT_INDEX(INT i_index)
RETURN node_sets[i_current_set_index].nodes[i_index].v_coords
ENDFUNC
// get label
FUNC TEXT_LABEL GET_NODE_LABEL_AT_INDEX(INT i_index)
RETURN node_sets[i_current_set_index].nodes[i_index].tl_label
ENDFUNC
// set node coords
PROC SET_NODE_COORDS_AT_INDEX(INT i_index, VECTOR v_coords)
node_sets[i_current_set_index].nodes[i_index].v_coords = v_coords
ENDPROC
// set node type
PROC SET_NODE_TYPE_AT_INDEX(INT i_index, NODE_TYPE_ENUM type)
node_sets[i_current_set_index].nodes[i_index].type = type
ENDPROC
// set node label
PROC SET_NODE_LABEL_AT_INDEX(INT i_index, STRING s_label)
node_sets[i_current_set_index].nodes[i_index].tl_label = ""
node_sets[i_current_set_index].nodes[i_index].tl_label += s_label
ENDPROC
// set node box counterpart
PROC SET_NODE_BOX_COUNTERPART_AT_INDEX(INT i_index, INT i_counterpart_index)
node_sets[i_current_set_index].nodes[i_index].i_box_counterpart_index = i_counterpart_index
ENDPROC
// delete node
PROC DELETE_NODE_AT_INDEX(INT i_index)
node_sets[i_current_set_index].nodes[i_index].type = TYPE_FREE_NODE
ENDPROC
// get the first available index of a free node in a set
FUNC INT GET_FREE_NODE_INDEX()
INT i
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT check_node = GET_NODE_AT_INDEX(i)
IF check_node.type = TYPE_FREE_NODE
RETURN i
ENDIF
ENDREPEAT
RETURN -1
ENDFUNC
// set camera to look down on the specified coords
PROC CAM_LOOK_DOWN_ON_COORDS(VECTOR v_coords)
SET_CAM_COORD(debug_cam, <<v_coords.x, v_coords.y, v_coords.z + 30>>)
SET_CAM_ROT(debug_cam, <<-89, 0, 0>>)
ENDPROC
// point cam at node if not on screen
PROC POINT_CAM_AT_NODE(INT i_index)
VECTOR v_node_coords = GET_NODE_COORDS_AT_INDEX(i_index)
IF NOT IS_SPHERE_VISIBLE(v_node_coords, POINT_NODE_SIZE)
IF GET_DISTANCE_BETWEEN_COORDS(v_node_coords, v_cam_coords) >= 120.0
// if further than 120m away, jump cam pos over there
CAM_LOOK_DOWN_ON_COORDS(v_node_coords)
ELSE
// otherwise just point at it
SET_CAM_COORD(point_cam, v_cam_coords)
POINT_CAM_AT_COORD(point_cam, v_node_coords)
WAIT(0)
SET_CAM_ROT(debug_cam, GET_CAM_ROT(point_cam))
ENDIF
ENDIF
ENDPROC
// create a node
PROC CREATE_NODE(NODE_STRUCT &node, NODE_SET_STRUCT &set)
// fill node attributes
node.type = TYPE_POINT_NODE
node.v_coords = v_cam_coords
node.tl_label = "Node "
node.tl_label += set.i_node_creation_counter
node.i_creation_number = set.i_node_creation_counter
node.i_box_counterpart_index = -1
// increment creation counter
set.i_node_creation_counter++
ENDPROC
// create a new node at the current debug cam coords
PROC PLACE_NEW_NODE(NODE_SET_STRUCT &set)
// find a free index
INT i_index = GET_FREE_NODE_INDEX()
IF i_index >= 0
// create the node
CREATE_NODE(node_sets[i_current_set_index].nodes[i_index], set)
// set this node as current
SET_CURRENT_NODE_INDEX(i_index)
DO_SOUND(AUDIO_CONFIRM_EDIT)
ELSE
SCRIPT_ASSERT("Maximum number of nodes for this set reached")
ENDIF
ENDPROC
// move the currently selected node
PROC MOVE_CURRENT_NODE()
IF i_current_node_index >= 0
// set the node coords
SET_NODE_COORDS_AT_INDEX(i_current_node_index, v_cam_coords)
DO_SOUND(AUDIO_CONFIRM_EDIT)
ELSE
PRINTSTRING("Attempting to move a node which does not exist") PRINTNL()
ENDIF
ENDPROC
// delete the currently selected node
PROC DELETE_CURRENT_NODE()
IF i_current_node_index >= 0
NODE_STRUCT current_node = GET_NODE_AT_INDEX(i_current_node_index)
IF current_node.type = TYPE_POINT_NODE
// if a point node, can just delete it
DELETE_NODE_AT_INDEX(i_current_node_index)
ELIF current_node.type = TYPE_BOX_FIRST_NODE
OR current_node.type = TYPE_BOX_SECOND_NODE
// if a box node, have to delete its counterpart too
IF current_node.i_box_counterpart_index >= 0
DELETE_NODE_AT_INDEX(current_node.i_box_counterpart_index)
ENDIF
DELETE_NODE_AT_INDEX(i_current_node_index)
ENDIF
// set currently selected node as the closest to the deleted one
INT i_closest_node = GET_CLOSEST_NODE_INDEX_TO_NODE(i_current_node_index)
SET_CURRENT_NODE_INDEX(i_closest_node)
IF i_current_node_index >= 0
POINT_CAM_AT_NODE(i_current_node_index)
ENDIF
DO_SOUND(AUDIO_CONFIRM_CHANGE)
ELSE
PRINTSTRING("Attempting to delete a node which does not exist") PRINTNL()
ENDIF
ENDPROC
// place currently selected node on ground
PROC PLACE_CURRENT_NODE_ON_GROUND()
IF i_current_node_index >= 0
NODE_STRUCT current_node = GET_NODE_AT_INDEX(i_current_node_index)
VECTOR v_current_coords = current_node.v_coords
// get ground level
FLOAT f_ground_z
IF GET_GROUND_Z_FOR_3D_COORD(v_current_coords, f_ground_z)
// move the node
SET_NODE_COORDS_AT_INDEX(i_current_node_index, <<v_current_coords.x, v_current_coords.y, f_ground_z + (POINT_NODE_SIZE/2)>>)
DO_SOUND(AUDIO_CONFIRM_EDIT)
ENDIF
ELSE
PRINTSTRING("Attempting to move a node which does not exist") PRINTNL()
ENDIF
ENDPROC
// change currently selected node and the one created before that into a box or vice versa
PROC CONVERT_CURRENT_AND_PRECEDING_NODES_TO_BOX()
IF i_current_node_index >= 0
NODE_STRUCT current_node = GET_NODE_AT_INDEX(i_current_node_index)
IF current_node.type = TYPE_POINT_NODE
// convert to box
INT i_preceding_node_index = GET_PREVIOUS_NODE_INDEX_IN_ORDER(i_current_node_index)
IF i_preceding_node_index >= 0
NODE_STRUCT preceding_node = GET_NODE_AT_INDEX(i_preceding_node_index)
IF preceding_node.type = TYPE_POINT_NODE
// setup preceding node as first box node
SET_NODE_TYPE_AT_INDEX(i_preceding_node_index, TYPE_BOX_FIRST_NODE)
SET_NODE_BOX_COUNTERPART_AT_INDEX(i_preceding_node_index, i_current_node_index)
// setup current node as second box node
SET_NODE_TYPE_AT_INDEX(i_current_node_index, TYPE_BOX_SECOND_NODE)
SET_NODE_BOX_COUNTERPART_AT_INDEX(i_current_node_index, i_preceding_node_index)
DO_SOUND(AUDIO_CONFIRM_EDIT)
ELSE
PRINTSTRING("Preceding node is already a box") PRINTNL()
ENDIF
ELSE
PRINTSTRING("Couldn't find preceding node") PRINTNL()
ENDIF
ELIF current_node.type = TYPE_BOX_FIRST_NODE
OR current_node.type = TYPE_BOX_SECOND_NODE
// change this node back to a point
SET_NODE_TYPE_AT_INDEX(i_current_node_index, TYPE_POINT_NODE)
// change the box's counterpart back to a point
IF current_node.i_box_counterpart_index >= 0
NODE_STRUCT counterpart_node = GET_NODE_AT_INDEX(current_node.i_box_counterpart_index)
IF counterpart_node.type = TYPE_BOX_FIRST_NODE
OR counterpart_node.type = TYPE_BOX_SECOND_NODE
SET_NODE_TYPE_AT_INDEX(current_node.i_box_counterpart_index, TYPE_POINT_NODE)
SET_NODE_BOX_COUNTERPART_AT_INDEX(current_node.i_box_counterpart_index, -1)
ENDIF
ENDIF
// reset this node's counterpart
SET_NODE_BOX_COUNTERPART_AT_INDEX(i_current_node_index, -1)
DO_SOUND(AUDIO_CONFIRM_EDIT)
ENDIF
ELSE
PRINTSTRING("Attempting to convert a node which does not exist") PRINTNL()
ENDIF
ENDPROC
// set current node to next one along
PROC GO_TO_NEXT_NODE()
IF i_current_node_index >= 0
INT i_next_node = GET_NEXT_NODE_INDEX_IN_ORDER(i_current_node_index, TRUE)
IF i_next_node >= 0
SET_CURRENT_NODE_INDEX(i_next_node)
POINT_CAM_AT_NODE(i_current_node_index)
DO_SOUND(AUDIO_CONFIRM_CHANGE)
ENDIF
ENDIF
ENDPROC
// set current node to previous one along
PROC GO_TO_PREVIOUS_NODE()
IF i_current_node_index >= 0
INT i_previous_node = GET_PREVIOUS_NODE_INDEX_IN_ORDER(i_current_node_index, TRUE)
IF i_previous_node >= 0
SET_CURRENT_NODE_INDEX(i_previous_node)
POINT_CAM_AT_NODE(i_current_node_index)
DO_SOUND(AUDIO_CONFIRM_CHANGE)
ENDIF
ENDIF
ENDPROC
// debug cam coords and rot to menu cam
PROC COPY_DEBUG_CAM_TO_MENU_CAM()
SET_CAM_COORD(menu_cam, GET_CAM_COORD(debug_cam))
SET_CAM_ROT(menu_cam, GET_CAM_ROT(debug_cam))
SET_CAM_FOV(menu_cam, GET_CAM_FOV(debug_cam))
ENDPROC
// menu cam coords and rot to debug cam
PROC COPY_MENU_CAM_TO_DEBUG_CAM()
SET_CAM_COORD(debug_cam, GET_CAM_COORD(menu_cam))
SET_CAM_ROT(debug_cam, GET_CAM_ROT(menu_cam))
SET_CAM_FOV(debug_cam, GET_CAM_FOV(menu_cam))
ENDPROC
// jump the camera to the first node in the set
PROC JUMP_VIEW_TO_FIRST_NODE_IN_SET()
INT i_first_node_index = GET_FIRST_NODE_INDEX()
SET_CURRENT_NODE_INDEX(i_first_node_index)
IF i_first_node_index >= 0
VECTOR v_first_coords = GET_NODE_COORDS_AT_INDEX(i_first_node_index)
CAM_LOOK_DOWN_ON_COORDS(v_first_coords)
COPY_DEBUG_CAM_TO_MENU_CAM()
ENDIF
ENDPROC
// select next set
PROC GO_TO_NEXT_SET()
IF i_current_set_index >= 0
INT i_next_set_index = GET_NEXT_OPEN_SET(i_current_set_index)
IF i_next_set_index >= 0
SET_CURRENT_SET_INDEX(i_next_set_index)
JUMP_VIEW_TO_FIRST_NODE_IN_SET()
ENDIF
ENDIF
ENDPROC
// select previous set
PROC GO_TO_PREVIOUS_SET()
IF i_current_set_index >= 0
INT i_previous_set_index = GET_PREVIOUS_OPEN_SET(i_current_set_index)
IF i_previous_set_index >= 0
SET_CURRENT_SET_INDEX(i_previous_set_index)
JUMP_VIEW_TO_FIRST_NODE_IN_SET()
ENDIF
ENDIF
ENDPROC
// go into set submenu
PROC DO_SUB_MENU()
IF i_current_set_index >= 0
current_sub_menu_selection = SUB_MENU_EDIT
ENDIF
ENDPROC
// select next submenu item
PROC GO_TO_NEXT_SUB_MENU_ITEM()
INT i_next_menu_pos
i_next_menu_pos = ENUM_TO_INT(current_sub_menu_selection) + 1
IF i_next_menu_pos > (NUMBER_OF_SUB_MENU_ITEMS -1)
i_next_menu_pos = 1
ENDIF
current_sub_menu_selection = INT_TO_ENUM(SUB_MENU_ENUM, i_next_menu_pos)
ENDPROC
// select previous submenu item
PROC GO_TO_PREVIOUS_SUB_MENU_ITEM()
INT i_next_menu_pos
i_next_menu_pos = ENUM_TO_INT(current_sub_menu_selection) - 1
IF i_next_menu_pos < 1
i_next_menu_pos = NUMBER_OF_SUB_MENU_ITEMS -1
ENDIF
current_sub_menu_selection = INT_TO_ENUM(SUB_MENU_ENUM, i_next_menu_pos)
ENDPROC
// create new set from menu
PROC DO_CREATE_SET()
INT i_slot = GET_FREE_SET_INDEX()
IF i_slot >= 0
CREATE_NEW_SET(i_slot)
SET_CURRENT_SET_INDEX(i_slot)
ELSE
PRINTSTRING("Could not find a free set index") PRINTNL()
ENDIF
ENDPROC
// go into edit from menu
PROC DO_EDIT_SET()
IF i_current_set_index >= 0
RENDER_SCRIPT_CAMS(FALSE, FALSE)
IF i_current_node_index = -1
// set debug cam pos to game cam pos if no node selected
SET_CAM_COORD(debug_cam, GET_GAMEPLAY_CAM_COORD())
SET_CAM_ROT(debug_cam, GET_GAMEPLAY_CAM_ROT())
SET_CAM_FOV(debug_cam, GET_GAMEPLAY_CAM_FOV())
ENDIF
v_last_cam_coords = v_cam_coords
b_equidistant_drop = FALSE
SET_DEBUG_CAM_ACTIVE(TRUE, TRUE)
current_sub_menu_selection = SUB_MENU_HIDDEN
current_viewer_state = STATE_FLYCAM
ENDIF
ENDPROC
// delete set from menu
PROC DO_DELETE_SET()
IF i_current_set_index >= 0
DELETE_SET(i_current_set_index)
i_current_set_index = GET_PREVIOUS_OPEN_SET(i_current_set_index)
b_do_update_widget = TRUE
current_sub_menu_selection = SUB_MENU_HIDDEN
ELSE
PRINTSTRING("Attempting to delete a set which does not exist") PRINTNL()
ENDIF
ENDPROC
// copy the current set into a free slot
PROC DO_COPY_SET()
IF i_current_set_index >= 0
INT i_slot = GET_FREE_SET_INDEX()
IF i_slot >= 0
IF COPY_SET_DATA_INTO_SLOT(i_current_set_index, i_slot)
SET_CURRENT_SET_INDEX(i_slot)
ELSE
PRINTSTRING("Copy failed") PRINTNL()
ENDIF
ELSE
PRINTSTRING("Could not find a free set index") PRINTNL()
ENDIF
current_sub_menu_selection = SUB_MENU_HIDDEN
ELSE
PRINTSTRING("Attempting to copy a set which does not exist") PRINTNL()
ENDIF
ENDPROC
// toggle equidistant drop
PROC TOGGLE_EQUIDISTANT_DROP()
IF NOT b_equidistant_drop
b_equidistant_drop = TRUE
ELSE
b_equidistant_drop = FALSE
ENDIF
f_distance_travelled = 0.0
ENDPROC
// back into menu from fly cam
PROC GO_TO_MENU()
// save the set
NODEVIEWER_SAVE_SET(0, i_current_set_index, node_sets[i_current_set_index], SIZE_OF(NODE_SET_STRUCT))
RENDER_SCRIPT_CAMS(TRUE, FALSE)
//SET_CURRENT_NODE_INDEX(-1)
b_equidistant_drop = FALSE
f_distance_travelled = 0.0
SET_DEBUG_CAM_ACTIVE(FALSE, TRUE)
current_viewer_state = STATE_MENU
ENDPROC
// check the control pad and perform desired action
PROC HANDLE_USER_ACTIONS()
SWITCH current_viewer_state
CASE STATE_MENU
IF NOT b_show_quit_confirm
// if haven't selected a set yet
IF current_sub_menu_selection = SUB_MENU_HIDDEN
// dpad down - next set
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADDOWN)
GO_TO_NEXT_SET()
ENDIF
// dpad up - previous set
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADUP)
GO_TO_PREVIOUS_SET()
ENDIF
// square - create set
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, SQUARE)
DO_CREATE_SET()
ENDIF
// cross - select set, go to submenu
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, CROSS)
DO_SUB_MENU()
ENDIF
// triangle - quit
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, TRIANGLE)
b_show_quit_confirm = TRUE
ENDIF
ELSE
// dpad down - next submenu item
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADDOWN)
GO_TO_NEXT_SUB_MENU_ITEM()
ENDIF
// dpad up - previous submenu item
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADUP)
GO_TO_PREVIOUS_SUB_MENU_ITEM()
ENDIF
// if in sub menu, perform action based on what is selected in sub menu
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, CROSS)
SWITCH current_sub_menu_selection
CASE SUB_MENU_EDIT
// edit - go into fly cam
DO_EDIT_SET()
BREAK
CASE SUB_MENU_COPY
// copy
DO_COPY_SET()
BREAK
CASE SUB_MENU_DELETE
// delete - get rid of all data in current set
DO_DELETE_SET()
BREAK
ENDSWITCH
ENDIF
// triangle - back out of sub menu
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, TRIANGLE)
current_sub_menu_selection = SUB_MENU_HIDDEN
ENDIF
ENDIF
ELSE
// controls for quit
// cross - confirm quit
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, CROSS)
b_quit_tool = TRUE
ENDIF
// triangle - cancel quit
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, TRIANGLE)
b_show_quit_confirm = FALSE
ENDIF
ENDIF
BREAK
// controls for dropping / editing in flycam
CASE STATE_FLYCAM
// edit functions
IF IS_BUTTON_PRESSED(DEBUG_PAD, LEFTSHOULDER1)
// cross - move node
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, RIGHTSHOULDER1)
MOVE_CURRENT_NODE()
ENDIF
// square - put node on ground
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, RIGHTSHOCK)
PLACE_CURRENT_NODE_ON_GROUND()
ENDIF
// rstick - convert last two nodes into a box
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, LEFTSHOCK)
CONVERT_CURRENT_AND_PRECEDING_NODES_TO_BOX()
ENDIF
// edit not held
ELSE
// r1 - create new node
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, RIGHTSHOULDER1)
PLACE_NEW_NODE(node_sets[i_current_set_index])
ENDIF
// triangle - delete node
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, TRIANGLE)
DELETE_CURRENT_NODE()
ENDIF
// dpad right - next node
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADRIGHT)
GO_TO_NEXT_NODE()
ENDIF
// dpad left - previous node
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, DPADLEFT)
GO_TO_PREVIOUS_NODE()
ENDIF
// select - toggle equidistant drop
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, SELECT)
TOGGLE_EQUIDISTANT_DROP()
ENDIF
// start - go back into menu
IF IS_BUTTON_JUST_PRESSED(DEBUG_PAD, START)
GO_TO_MENU()
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
// update distance travelled for equidistant drop amd drop if gone over limit
PROC UPDATE_EQUIDISTANT_DROP()
IF b_equidistant_drop
f_distance_travelled += GET_DISTANCE_BETWEEN_COORDS(v_cam_coords, v_last_cam_coords)
IF f_distance_travelled >= f_distance_to_travel_for_next_drop
PLACE_NEW_NODE(node_sets[i_current_set_index])
f_distance_travelled = 0.0
ENDIF
ELSE
// reset distance to 0 if turned off
IF f_distance_travelled > 0.0
f_distance_travelled = 0.0
ENDIF
ENDIF
v_last_cam_coords = v_cam_coords
ENDPROC
// return the correct size to draw a debug sphere
FUNC FLOAT GET_SIZE_TO_DRAW_SPHERE(NODE_TYPE_ENUM type)
IF type = TYPE_POINT_NODE
RETURN POINT_NODE_SIZE
ELIF type = TYPE_BOX_FIRST_NODE
OR type = TYPE_BOX_SECOND_NODE
RETURN BOX_NODE_SIZE
ENDIF
RETURN POINT_NODE_SIZE
ENDFUNC
// return correct amount to offset text label
FUNC FLOAT GET_TEXT_OFFSET(NODE_TYPE_ENUM type)
IF type = TYPE_POINT_NODE
RETURN POINT_NODE_TEXT_OFFSET
ELIF type = TYPE_BOX_FIRST_NODE
OR type = TYPE_BOX_SECOND_NODE
RETURN BOX_NODE_TEXT_OFFSET
ENDIF
RETURN POINT_NODE_TEXT_OFFSET
ENDFUNC
// draw shadow under camera
PROC DRAW_SHADOW()
FLOAT f_ground_z
IF GET_GROUND_Z_FOR_3D_COORD(v_cam_coords, f_ground_z)
DRAW_DEBUG_SPHERE(<<v_cam_coords.x, v_cam_coords.y, f_ground_z - 0.85>>, 2.0, 0, 0, 0, 185)
ENDIF
ENDPROC
// draw all the nodes in the current set
PROC DRAW_NODES_GROUP()
INT i
REPEAT NUMBER_OF_NODES_PER_SET i
NODE_STRUCT node = GET_NODE_AT_INDEX(i)
// draw sphere and text at each node coords
IF node.type <> TYPE_FREE_NODE
IF i = i_current_node_index
AND current_viewer_state = STATE_FLYCAM
// selected
DRAW_DEBUG_SPHERE(node.v_coords, GET_SIZE_TO_DRAW_SPHERE(node.type), 255, 0, 0, SPHERE_ALPHA)
DRAW_DEBUG_TEXT(node.tl_label, <<node.v_coords.x, node.v_coords.y, node.v_coords.z + GET_TEXT_OFFSET(node.type)>>, 255, 0, 0, SPHERE_ALPHA)
ELSE
// unselected
DRAW_DEBUG_SPHERE(node.v_coords, GET_SIZE_TO_DRAW_SPHERE(node.type), 0, 155, 255, SPHERE_ALPHA)
DRAW_DEBUG_TEXT(node.tl_label, <<node.v_coords.x, node.v_coords.y, node.v_coords.z + GET_TEXT_OFFSET(node.type)>>, 0, 155, 255, SPHERE_ALPHA)
ENDIF
ENDIF
// draw a box for any box nodes
IF node.type = TYPE_BOX_FIRST_NODE
IF node.i_box_counterpart_index >= 0
NODE_STRUCT second_node = GET_NODE_AT_INDEX(node.i_box_counterpart_index)
IF second_node.type = TYPE_BOX_SECOND_NODE
// setup the correct order to draw the points (for command to work first vector must be lowest, second highest)
VECTOR v_first_box_coords = <<0,0,0>>
VECTOR v_second_box_coords = <<0,0,0>>
IF node.v_coords.x < second_node.v_coords.x
v_first_box_coords.x = node.v_coords.x
v_second_box_coords.x = second_node.v_coords.x
ELSE
v_first_box_coords.x = second_node.v_coords.x
v_second_box_coords.x = node.v_coords.x
ENDIF
IF node.v_coords.y < second_node.v_coords.y
v_first_box_coords.y = node.v_coords.y
v_second_box_coords.y = second_node.v_coords.y
ELSE
v_first_box_coords.y = second_node.v_coords.y
v_second_box_coords.y = node.v_coords.y
ENDIF
IF node.v_coords.z < second_node.v_coords.z
v_first_box_coords.z = node.v_coords.z
v_second_box_coords.z = second_node.v_coords.z
ELSE
v_first_box_coords.z = second_node.v_coords.z
v_second_box_coords.z = node.v_coords.z
ENDIF
// do the box
IF (i = i_current_node_index
OR node.i_box_counterpart_index = i_current_node_index)
AND current_viewer_state = STATE_FLYCAM
// selected
DRAW_DEBUG_BOX(v_first_box_coords, v_second_box_coords, 255, 0, 0, BOX_ALPHA)
ELSE
// unselected
DRAW_DEBUG_BOX(v_first_box_coords, v_second_box_coords, 0, 155, 255, BOX_ALPHA)
ENDIF
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDPROC
PROC DRAW_SUB_MENU_ITEM(SUB_MENU_ENUM item, FLOAT f_y_pos)
INT i_colour
IF current_sub_menu_selection = item
i_colour = i_red
ELSE
i_colour = i_white
ENDIF
STRING s_text
SWITCH item
CASE SUB_MENU_EDIT
s_text = "EDIT"
BREAK
CASE SUB_MENU_COPY
s_text = "COPY"
BREAK
CASE SUB_MENU_DELETE
s_text = "DELETE"
BREAK
ENDSWITCH
DEBUG_DISPLAY_LITERAL_TEXT(s_text, 0.25, f_y_pos, 0.3, 0.3, 0.0, 1.0, i_colour)
ENDPROC
// draw the sub menu popup
PROC DRAW_SUB_MENU(FLOAT f_y_pos)
DRAW_SUB_MENU_ITEM(SUB_MENU_EDIT, f_y_pos)
DRAW_SUB_MENU_ITEM(SUB_MENU_COPY, f_y_pos + 0.02)
DRAW_SUB_MENU_ITEM(SUB_MENU_DELETE, f_y_pos + 0.04)
ENDPROC
// draw the menu screen
PROC DRAW_MENU()
INT i
// draw the background
DRAW_RECT(0.25, 0.5, 0.5, 1.0, 0, 0, 0, 220)
// draw the title
DEBUG_DISPLAY_LITERAL_TEXT("NODE VIEWER", 0.06, 0.06, 0.75, 0.75, 0.0, 1.0, i_white)
IF NOT b_show_quit_confirm
// draw the list of node sets
REPEAT NUMBER_OF_SETS i
IF node_sets[i].b_open
// draw set name
IF i = i_current_set_index
DEBUG_DISPLAY_LITERAL_TEXT(node_sets[i].tl_name, 0.06, 0.135 + (i*0.05), 0.4, 0.4, 0.0, 1.0, i_red)
ELSE
DEBUG_DISPLAY_LITERAL_TEXT(node_sets[i].tl_name, 0.06, 0.135 + (i*0.05), 0.4, 0.4, 0.0, 1.0, i_white)
ENDIF
ENDIF
ENDREPEAT
// draw the edit submenu
IF current_sub_menu_selection <> SUB_MENU_HIDDEN
DRAW_SUB_MENU(0.135 + (i_current_set_index*0.05))
ENDIF
ELSE
// show the message to confirm quit
DEBUG_DISPLAY_LITERAL_TEXT("Press Cross to confirm quit or Triangle to return", 0.06, 0.5, 0.32, 0.32, 0.0, 0.5, i_white)
ENDIF
ENDPROC
// draw control help
PROC DRAW_HELP()
SWITCH current_viewer_state
CASE STATE_FLYCAM
IF IS_BUTTON_PRESSED(DEBUG_PAD, LEFTSHOULDER1)
// edit menu
DEBUG_DISPLAY_LITERAL_TEXT("R1: move node", 0.06, 0.77, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("R3: set node on ground", 0.06, 0.80, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("L3: convert this and preceding nodes to box", 0.06, 0.83, 0.3, 0.3, 0.0, 1.0, i_white)
ELSE
DEBUG_DISPLAY_LITERAL_TEXT("R1: create node", 0.06, 0.77, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Triangle: delete node", 0.06, 0.80, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Dpad left/right: change selected node", 0.06, 0.83, 0.3, 0.3, 0.0, 1.0, i_white)
IF b_equidistant_drop
DEBUG_DISPLAY_LITERAL_TEXT("Select: Disable equidistant drop", 0.06, 0.86, 0.3, 0.3, 0.0, 1.0, i_white)
ELSE
DEBUG_DISPLAY_LITERAL_TEXT("Select: Enable equidistant drop", 0.06, 0.86, 0.3, 0.3, 0.0, 1.0, i_white)
ENDIF
DEBUG_DISPLAY_LITERAL_TEXT("Hold L1: edit node functions", 0.06, 0.89, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Start: return to menu", 0.06, 0.92, 0.3, 0.3, 0.0, 1.0, i_white)
ENDIF
// display state of equidistant drop
IF b_equidistant_drop
DEBUG_DISPLAY_LITERAL_TEXT("Equidistant drop ON", 0.06, 0.06, 0.3, 0.3, 0.0, 1.0, i_red)
ELSE
DEBUG_DISPLAY_LITERAL_TEXT("Equidistant drop OFF", 0.06, 0.06, 0.3, 0.3, 0.0, 1.0, i_red)
ENDIF
BREAK
CASE STATE_MENU
IF NOT b_show_quit_confirm
IF current_sub_menu_selection = SUB_MENU_HIDDEN
DEBUG_DISPLAY_LITERAL_TEXT("Square: create new set", 0.06, 0.77, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Cross: select set", 0.06, 0.80, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Dpad up/down: navigate sets", 0.06, 0.83, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Triangle: quit tool", 0.06, 0.86, 0.3, 0.3, 0.0, 1.0, i_white)
ELSE
DEBUG_DISPLAY_LITERAL_TEXT("Cross: select option", 0.06, 0.77, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Dpad up/down: navigate menu", 0.06, 0.80, 0.3, 0.3, 0.0, 1.0, i_white)
DEBUG_DISPLAY_LITERAL_TEXT("Triangle: go back", 0.06, 0.83, 0.3, 0.3, 0.0, 1.0, i_white)
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
// create widget
PROC CREATE_NODES_WIDGET()
w_nodes_group = START_WIDGET_GROUP("Node Viewer")
ADD_WIDGET_STRING("Current Set")
w_set_name = ADD_TEXT_WIDGET("Name")
ADD_WIDGET_BOOL("Update Name", b_update_name)
ADD_WIDGET_STRING("Current Node")
w_node_label = ADD_TEXT_WIDGET("Label")
ADD_WIDGET_BOOL("Update Label", b_update_label)
ADD_WIDGET_STRING("Other Commands")
ADD_WIDGET_BOOL("Use equidistant drop", b_equidistant_drop)
ADD_WIDGET_FLOAT_SLIDER("Distance for eq. drop", f_distance_to_travel_for_next_drop, 20, 200, 1)
ADD_WIDGET_BOOL("Show Help", b_draw_help)
ADD_WIDGET_BOOL("Quit", b_quit_tool)
STOP_WIDGET_GROUP()
b_do_update_widget = TRUE
ENDPROC
// update widget
PROC UPDATE_NODES_WIDGET()
// update with data of current node
IF b_do_update_widget
IF i_current_node_index >= 0
NODE_STRUCT current_node = GET_NODE_AT_INDEX(i_current_node_index)
SET_CONTENTS_OF_TEXT_WIDGET(w_node_label, current_node.tl_label)
ELSE
SET_CONTENTS_OF_TEXT_WIDGET(w_node_label, "<no node>")
ENDIF
IF i_current_set_index >= 0
SET_CONTENTS_OF_TEXT_WIDGET(w_set_name, node_sets[i_current_set_index].tl_name)
ELSE
SET_CONTENTS_OF_TEXT_WIDGET(w_set_name, "<no set>")
ENDIF
b_do_update_widget = FALSE
ENDIF
// change set name from text box
IF b_update_name
IF i_current_set_index >= 0
SET_SET_NAME(i_current_set_index, GET_CONTENTS_OF_TEXT_WIDGET(w_set_name))
ENDIF
b_update_name = FALSE
ENDIF
// change node label from text box
IF b_update_label
IF i_current_node_index >= 0
SET_NODE_LABEL_AT_INDEX(i_current_node_index, GET_CONTENTS_OF_TEXT_WIDGET(w_node_label))
ENDIF
b_update_label = FALSE
ENDIF
ENDPROC
SCRIPT
SET_SCRIPT_AS_NO_LONGER_NEEDED("nodeviewer")
CREATE_NODES_WIDGET()
REGISTER_SCRIPT_WITH_AUDIO()
NODEVIEWER_INIT()
INT i
REPEAT NUMBER_OF_SETS i
NODEVIEWER_LOAD_SET(0, i, node_sets[i], SIZE_OF(NODE_SET_STRUCT))
ENDREPEAT
i_current_node_index = -1
INT j
REPEAT COUNT_OF(node_sets) j
IF node_sets[j].b_open
i_current_set_index = j
ENDIF
ENDREPEAT
// player control off
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), FALSE, SPC_AMBIENT_SCRIPT)
DISPLAY_HUD(FALSE)
DISPLAY_RADAR(FALSE)
ENDIF
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_ENTITY_PROOFS(PLAYER_PED_ID(), TRUE, TRUE, TRUE, TRUE, TRUE)
ENDIF
// get debug cam
debug_cam = GET_DEBUG_CAM()
// other cams
point_cam = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_ACTIVE(point_cam, TRUE)
menu_cam = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA")
SET_CAM_ACTIVE(menu_cam, TRUE)
// debug drawing active
SET_DEBUG_LINES_AND_SPHERES_DRAWING_ACTIVE(TRUE)
WHILE TRUE
// deal with input
HANDLE_USER_ACTIONS()
IF current_viewer_state = STATE_FLYCAM
// get current cam coords
v_cam_coords = GET_CAM_COORD(debug_cam)
//v_cam_rot = GET_CAM_ROT(debug_cam)
//f_cam_fov = GET_CAM_FOV(debug_cam)
// update menu cam
IF NOT b_set_menu_cam
b_set_menu_cam = TRUE
ELSE
COPY_DEBUG_CAM_TO_MENU_CAM()
ENDIF
// draw shadow under camera
DRAW_SHADOW()
// update distance travelled for equidistant dropped
UPDATE_EQUIDISTANT_DROP()
ENDIF
// draw all the nodes in the current set
IF i_current_set_index >= 0
DRAW_NODES_GROUP()
ENDIF
IF current_viewer_state = STATE_MENU
// draw menu
DRAW_MENU()
ENDIF
IF b_draw_help
DRAW_HELP()
ENDIF
// update the widget
UPDATE_NODES_WIDGET()
// quit
IF b_quit_tool
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), TRUE, SPC_AMBIENT_SCRIPT)
DISPLAY_HUD(TRUE)
DISPLAY_RADAR(TRUE)
ENDIF
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_ENTITY_PROOFS(PLAYER_PED_ID(), FALSE, FALSE, FALSE, FALSE, FALSE)
ENDIF
RENDER_SCRIPT_CAMS(FALSE, FALSE)
SET_DEBUG_CAM_ACTIVE(FALSE, FALSE)
DELETE_WIDGET_GROUP(w_nodes_group)
UNREGISTER_SCRIPT_WITH_AUDIO()
TERMINATE_THIS_THREAD()
ENDIF
WAIT(0)
ENDWHILE
ENDSCRIPT
#ENDIF