diff options
35 files changed, 1240 insertions, 113 deletions
diff --git a/core/os/os.h b/core/os/os.h index 943c0498f1..f6404468b1 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -320,6 +320,7 @@ public: virtual void disable_crash_handler() {} virtual bool is_disable_crash_handler() const { return false; } + virtual void initialize_debugging() {} enum CursorShape { CURSOR_ARROW, diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index c0e115e300..55d7270473 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -29,12 +29,23 @@ /*************************************************************************/ #include "script_debugger_local.h" +#include "scene/main/scene_tree.h" #include "os/os.h" void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { - print_line("Debugger Break, Reason: '" + p_script->debug_get_error() + "'"); + if (!target_function.empty()) { + String current_function = p_script->debug_get_stack_level_function(0); + if (current_function != target_function) { + set_depth(0); + set_lines_left(1); + return; + } + target_function = ""; + } + + print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'"); print_line("*Frame " + itos(0) + " - " + p_script->debug_get_stack_level_source(0) + ":" + itos(p_script->debug_get_stack_level_line(0)) + " in function '" + p_script->debug_get_stack_level_function(0) + "'"); print_line("Enter \"help\" for assistance."); int current_frame = 0; @@ -44,8 +55,11 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { OS::get_singleton()->print("debug> "); String line = OS::get_singleton()->get_stdin_string().strip_edges(); + // Cache options + String variable_prefix = options["variable_prefix"]; + if (line == "") { - print_line("Debugger Break, Reason: '" + p_script->debug_get_error() + "'"); + print_line("\nDebugger Break, Reason: '" + p_script->debug_get_error() + "'"); print_line("*Frame " + itos(current_frame) + " - " + p_script->debug_get_stack_level_source(current_frame) + ":" + itos(p_script->debug_get_stack_level_line(current_frame)) + " in function '" + p_script->debug_get_stack_level_function(current_frame) + "'"); print_line("Enter \"help\" for assistance."); } else if (line == "c" || line == "continue") @@ -72,38 +86,56 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { } } + } else if (line.begins_with("set")) { + + if (line.get_slice_count(" ") == 1) { + + for (Map<String, String>::Element *E = options.front(); E; E = E->next()) { + print_line("\t" + E->key() + "=" + E->value()); + } + + } else { + String key_value = line.get_slicec(' ', 1); + int value_pos = key_value.find("="); + + if (value_pos < 0) { + print_line("Error: Invalid set format. Use: set key=value"); + } else { + + String key = key_value.left(value_pos); + + if (!options.has(key)) { + print_line("Error: Unknown option " + key); + } else { + + // Allow explicit tab character + String value = key_value.right(value_pos + 1).replace("\\t", "\t"); + + options[key] = value; + } + } + } + } else if (line == "lv" || line == "locals") { List<String> locals; List<Variant> values; p_script->debug_get_stack_level_locals(current_frame, &locals, &values); - List<Variant>::Element *V = values.front(); - for (List<String>::Element *E = locals.front(); E; E = E->next()) { - print_line(E->get() + ": " + String(V->get())); - V = V->next(); - } + print_variables(locals, values, variable_prefix); } else if (line == "gv" || line == "globals") { - List<String> locals; + List<String> globals; List<Variant> values; - p_script->debug_get_globals(&locals, &values); - List<Variant>::Element *V = values.front(); - for (List<String>::Element *E = locals.front(); E; E = E->next()) { - print_line(E->get() + ": " + String(V->get())); - V = V->next(); - } + p_script->debug_get_globals(&globals, &values); + print_variables(globals, values, variable_prefix); } else if (line == "mv" || line == "members") { - List<String> locals; + List<String> members; List<Variant> values; - p_script->debug_get_stack_level_members(current_frame, &locals, &values); - List<Variant>::Element *V = values.front(); - for (List<String>::Element *E = locals.front(); E; E = E->next()) { - print_line(E->get() + ": " + String(V->get())); - V = V->next(); - } + p_script->debug_get_stack_level_members(current_frame, &members, &values); + print_variables(members, values, variable_prefix); } else if (line.begins_with("p") || line.begins_with("print")) { @@ -121,65 +153,149 @@ void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { set_depth(-1); set_lines_left(1); break; - } else if (line.begins_with("n") || line.begins_with("next")) { + } else if (line == "n" || line == "next") { set_depth(0); set_lines_left(1); break; + } else if (line == "fin" || line == "finish") { + + String current_function = p_script->debug_get_stack_level_function(0); + + for (int i = 0; i < total_frames; i++) { + target_function = p_script->debug_get_stack_level_function(i); + if (target_function != current_function) { + set_depth(0); + set_lines_left(1); + return; + } + } + + print_line("Error: Reached last frame."); + target_function = ""; + } else if (line.begins_with("br") || line.begins_with("break")) { if (line.get_slice_count(" ") <= 1) { - //show breakpoints + + const Map<int, Set<StringName> > &breakpoints = get_breakpoints(); + if (breakpoints.size() == 0) { + print_line("No Breakpoints."); + continue; + } + + print_line("Breakpoint(s): " + itos(breakpoints.size())); + for (Map<int, Set<StringName> >::Element *E = breakpoints.front(); E; E = E->next()) { + print_line("\t" + String(E->value().front()->get()) + ":" + itos(E->key())); + } + } else { - String bppos = line.get_slicec(' ', 1); - String source = bppos.get_slicec(':', 0).strip_edges(); - int line = bppos.get_slicec(':', 1).strip_edges().to_int(); + Pair<String, int> breakpoint = to_breakpoint(line); + + String source = breakpoint.first; + int linenr = breakpoint.second; - source = breakpoint_find_source(source); + if (source.empty()) + continue; - insert_breakpoint(line, source); + insert_breakpoint(linenr, source); - print_line("BreakPoint at " + source + ":" + itos(line)); + print_line("Added breakpoint at " + source + ":" + itos(linenr)); } + } else if (line == "q" || line == "quit") { + + // Do not stop again on quit + clear_breakpoints(); + ScriptDebugger::get_singleton()->set_depth(-1); + ScriptDebugger::get_singleton()->set_lines_left(-1); + + SceneTree::get_singleton()->quit(); + break; } else if (line.begins_with("delete")) { if (line.get_slice_count(" ") <= 1) { clear_breakpoints(); } else { - String bppos = line.get_slicec(' ', 1); - String source = bppos.get_slicec(':', 0).strip_edges(); - int line = bppos.get_slicec(':', 1).strip_edges().to_int(); + Pair<String, int> breakpoint = to_breakpoint(line); + + String source = breakpoint.first; + int linenr = breakpoint.second; - source = breakpoint_find_source(source); + if (source.empty()) + continue; - remove_breakpoint(line, source); + remove_breakpoint(linenr, source); - print_line("Removed BreakPoint at " + source + ":" + itos(line)); + print_line("Removed breakpoint at " + source + ":" + itos(linenr)); } } else if (line == "h" || line == "help") { print_line("Built-In Debugger command list:\n"); - print_line("\tc,continue :\t\t Continue execution."); - print_line("\tbt,backtrace :\t\t Show stack trace (frames)."); + print_line("\tc,continue\t\t Continue execution."); + print_line("\tbt,backtrace\t\t Show stack trace (frames)."); print_line("\tfr,frame <frame>:\t Change current frame."); - print_line("\tlv,locals :\t\t Show local variables for current frame."); - print_line("\tmv,members :\t\t Show member variables for \"this\" in frame."); - print_line("\tgv,globals :\t\t Show global variables."); - print_line("\tp,print <expr> :\t Execute and print variable in expression."); - print_line("\ts,step :\t\t Step to next line."); - print_line("\tn,next :\t\t Next line."); - print_line("\tbr,break source:line :\t Place a breakpoint."); - print_line("\tdelete [source:line]:\t\t Delete one/all breakpoints."); + print_line("\tlv,locals\t\t Show local variables for current frame."); + print_line("\tmv,members\t\t Show member variables for \"this\" in frame."); + print_line("\tgv,globals\t\t Show global variables."); + print_line("\tp,print <expr>\t\t Execute and print variable in expression."); + print_line("\ts,step\t\t\t Step to next line."); + print_line("\tn,next\t\t\t Next line."); + print_line("\tfin,finish\t\t Step out of current frame."); + print_line("\tbr,break [source:line]\t List all breakpoints or place a breakpoint."); + print_line("\tdelete [source:line]:\t Delete one/all breakpoints."); + print_line("\tset [key=value]:\t List all options, or set one."); + print_line("\tq,quit\t\t\t Quit application."); } else { print_line("Error: Invalid command, enter \"help\" for assistance."); } } } +void ScriptDebuggerLocal::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) { + + String value; + Vector<String> value_lines; + const List<Variant>::Element *V = values.front(); + for (const List<String>::Element *E = names.front(); E; E = E->next()) { + + value = String(V->get()); + + if (variable_prefix.empty()) { + print_line(E->get() + ": " + String(V->get())); + } else { + + print_line(E->get() + ":"); + value_lines = value.split("\n"); + for (int i = 0; i < value_lines.size(); ++i) { + print_line(variable_prefix + value_lines[i]); + } + } + + V = V->next(); + } +} + +Pair<String, int> ScriptDebuggerLocal::to_breakpoint(const String &p_line) { + + String breakpoint_part = p_line.get_slicec(' ', 1); + Pair<String, int> breakpoint; + + int last_colon = breakpoint_part.rfind(":"); + if (last_colon < 0) { + print_line("Error: Invalid breakpoint format. Expected [source:line]"); + return breakpoint; + } + + breakpoint.first = breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges()); + breakpoint.second = breakpoint_part.right(last_colon).strip_edges().to_int(); + + return breakpoint; +} + struct _ScriptDebuggerLocalProfileInfoSort { bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const { @@ -304,4 +420,5 @@ ScriptDebuggerLocal::ScriptDebuggerLocal() { profiling = false; idle_accum = OS::get_singleton()->get_ticks_usec(); + options["variable_prefix"] = ""; } diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h index c87bc90bb4..7eea6ef215 100644 --- a/core/script_debugger_local.h +++ b/core/script_debugger_local.h @@ -31,6 +31,7 @@ #ifndef SCRIPT_DEBUGGER_LOCAL_H #define SCRIPT_DEBUGGER_LOCAL_H +#include "list.h" #include "script_language.h" class ScriptDebuggerLocal : public ScriptDebugger { @@ -38,9 +39,14 @@ class ScriptDebuggerLocal : public ScriptDebugger { bool profiling; float frame_time, idle_time, physics_time, physics_frame_time; uint64_t idle_accum; + String target_function; + Map<String, String> options; Vector<ScriptLanguage::ProfilingInfo> pinfo; + Pair<String, int> to_breakpoint(const String &p_line); + void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix); + public: void debug(ScriptLanguage *p_script, bool p_can_continue); virtual void send_message(const String &p_message, const Array &p_args); diff --git a/core/script_language.h b/core/script_language.h index 0c1f99cea6..55a20c7478 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -386,6 +386,7 @@ public: bool is_breakpoint(int p_line, const StringName &p_source) const; bool is_breakpoint_line(int p_line) const; void clear_breakpoints(); + const Map<int, Set<StringName> > &get_breakpoints() const { return breakpoints; } virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true) = 0; virtual void idle_poll(); diff --git a/core/ustring.cpp b/core/ustring.cpp index 921d20a6fd..85b7a16e6a 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -945,8 +945,8 @@ String String::num(double p_num, int p_decimals) { #ifndef NO_USE_STDLIB - if (p_decimals > 12) - p_decimals = 12; + if (p_decimals > 16) + p_decimals = 16; char fmt[7]; fmt[0] = '%'; diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml index 2aec6d7f4e..0863ead4ec 100644 --- a/doc/classes/Generic6DOFJoint.xml +++ b/doc/classes/Generic6DOFJoint.xml @@ -167,6 +167,33 @@ <member name="linear_limit_z/upper_distance" type="float" setter="set_param_z" getter="get_param_z"> The maximum difference between the pivot points' z-axis. </member> + <member name="linear_motor_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x"> + If [code]true[/code] then there is a linear motor on the x-axis. It will attempt to reach the target velocity while staying within the force limits. + </member> + <member name="linear_motor_x/force_limit" type="float" setter="set_param_x" getter="get_param_x"> + The maximum force the linear motor can apply on the x-axis while trying to reach the target velocity. + </member> + <member name="linear_motor_x/target_velocity" type="float" setter="set_param_x" getter="get_param_x"> + The speed that the linear motor will attempt to reach on the x-axis. + </member> + <member name="linear_motor_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y"> + If [code]true[/code] then there is a linear motor on the y-axis. It will attempt to reach the target velocity while staying within the force limits. + </member> + <member name="linear_motor_y/force_limit" type="float" setter="set_param_y" getter="get_param_y"> + The maximum force the linear motor can apply on the y-axis while trying to reach the target velocity. + </member> + <member name="linear_motor_y/target_velocity" type="float" setter="set_param_y" getter="get_param_y"> + The speed that the linear motor will attempt to reach on the y-axis. + </member> + <member name="linear_motor_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z"> + If [code]true[/code] then there is a linear motor on the z-axis. It will attempt to reach the target velocity while staying within the force limits. + </member> + <member name="linear_motor_z/force_limit" type="float" setter="set_param_z" getter="get_param_z"> + The maximum force the linear motor can apply on the z-axis while trying to reach the target velocity. + </member> + <member name="linear_motor_z/target_velocity" type="float" setter="set_param_z" getter="get_param_z"> + The speed that the linear motor will attempt to reach on the z-axis. + </member> </members> <constants> <constant name="PARAM_LINEAR_LOWER_LIMIT" value="0" enum="Param"> @@ -184,34 +211,40 @@ <constant name="PARAM_LINEAR_DAMPING" value="4" enum="Param"> The amount of damping that happens at the linear motion across the axes. </constant> - <constant name="PARAM_ANGULAR_LOWER_LIMIT" value="5" enum="Param"> + <constant name="PARAM_LINEAR_MOTOR_TARGET_VELOCITY" value="5" enum="Param"> + The velocity the linear motor will try to reach. + </constant> + <constant name="PARAM_LINEAR_MOTOR_FORCE_LIMIT" value="6" enum="Param"> + The maximum force the linear motor will apply while trying to reach the velocity target. + </constant> + <constant name="PARAM_ANGULAR_LOWER_LIMIT" value="7" enum="Param"> The minimum rotation in negative direction to break loose and rotate around the axes. </constant> - <constant name="PARAM_ANGULAR_UPPER_LIMIT" value="6" enum="Param"> + <constant name="PARAM_ANGULAR_UPPER_LIMIT" value="8" enum="Param"> The minimum rotation in positive direction to break loose and rotate around the axes. </constant> - <constant name="PARAM_ANGULAR_LIMIT_SOFTNESS" value="7" enum="Param"> + <constant name="PARAM_ANGULAR_LIMIT_SOFTNESS" value="9" enum="Param"> The speed of all rotations across the axes. </constant> - <constant name="PARAM_ANGULAR_DAMPING" value="8" enum="Param"> + <constant name="PARAM_ANGULAR_DAMPING" value="10" enum="Param"> The amount of rotational damping across the axes. The lower, the more dampening occurs. </constant> - <constant name="PARAM_ANGULAR_RESTITUTION" value="9" enum="Param"> + <constant name="PARAM_ANGULAR_RESTITUTION" value="11" enum="Param"> The amount of rotational restitution across the axes. The lower, the more restitution occurs. </constant> - <constant name="PARAM_ANGULAR_FORCE_LIMIT" value="10" enum="Param"> + <constant name="PARAM_ANGULAR_FORCE_LIMIT" value="12" enum="Param"> The maximum amount of force that can occur, when rotating around the axes. </constant> - <constant name="PARAM_ANGULAR_ERP" value="11" enum="Param"> + <constant name="PARAM_ANGULAR_ERP" value="13" enum="Param"> When rotating across the axes, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower. </constant> - <constant name="PARAM_ANGULAR_MOTOR_TARGET_VELOCITY" value="12" enum="Param"> + <constant name="PARAM_ANGULAR_MOTOR_TARGET_VELOCITY" value="14" enum="Param"> Target speed for the motor at the axes. </constant> - <constant name="PARAM_ANGULAR_MOTOR_FORCE_LIMIT" value="13" enum="Param"> + <constant name="PARAM_ANGULAR_MOTOR_FORCE_LIMIT" value="15" enum="Param"> Maximum acceleration for the motor at the axes. </constant> - <constant name="PARAM_MAX" value="14" enum="Param"> + <constant name="PARAM_MAX" value="16" enum="Param"> End flag of PARAM_* constants, used internally. </constant> <constant name="FLAG_ENABLE_LINEAR_LIMIT" value="0" enum="Flag"> @@ -223,7 +256,9 @@ <constant name="FLAG_ENABLE_MOTOR" value="2" enum="Flag"> If [code]set[/code] there is a rotational motor across these axes. </constant> - <constant name="FLAG_MAX" value="3" enum="Flag"> + <constant name="FLAG_ENABLE_LINEAR_MOTOR" value="3" enum="Flag"> + </constant> + <constant name="FLAG_MAX" value="4" enum="Flag"> End flag of FLAG_* constants, used internally. </constant> </constants> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index 6c3a435360..dd2c322ee3 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -1331,31 +1331,37 @@ <constant name="G6DOF_JOINT_LINEAR_DAMPING" value="4" enum="G6DOFJointAxisParam"> The amount of damping that happens at the linear motion across the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_LOWER_LIMIT" value="5" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY" value="5" enum="G6DOFJointAxisParam"> + The velocity that the joint's linear motor will attempt to reach. + </constant> + <constant name="G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT" value="6" enum="G6DOFJointAxisParam"> + The maximum force that the linear motor can apply while trying to reach the target velocity. + </constant> + <constant name="G6DOF_JOINT_ANGULAR_LOWER_LIMIT" value="7" enum="G6DOFJointAxisParam"> The minimum rotation in negative direction to break loose and rotate around the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_UPPER_LIMIT" value="6" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_UPPER_LIMIT" value="8" enum="G6DOFJointAxisParam"> The minimum rotation in positive direction to break loose and rotate around the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS" value="7" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS" value="9" enum="G6DOFJointAxisParam"> A factor that gets multiplied onto all rotations across the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_DAMPING" value="8" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_DAMPING" value="10" enum="G6DOFJointAxisParam"> The amount of rotational damping across the axes. The lower, the more dampening occurs. </constant> - <constant name="G6DOF_JOINT_ANGULAR_RESTITUTION" value="9" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_RESTITUTION" value="11" enum="G6DOFJointAxisParam"> The amount of rotational restitution across the axes. The lower, the more restitution occurs. </constant> - <constant name="G6DOF_JOINT_ANGULAR_FORCE_LIMIT" value="10" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_FORCE_LIMIT" value="12" enum="G6DOFJointAxisParam"> The maximum amount of force that can occur, when rotating around the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_ERP" value="11" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_ERP" value="13" enum="G6DOFJointAxisParam"> When correcting the crossing of limits in rotation across the axes, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower. </constant> - <constant name="G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY" value="12" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY" value="14" enum="G6DOFJointAxisParam"> Target speed for the motor at the axes. </constant> - <constant name="G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT" value="13" enum="G6DOFJointAxisParam"> + <constant name="G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT" value="15" enum="G6DOFJointAxisParam"> Maximum acceleration for the motor at the axes. </constant> <constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT" value="0" enum="G6DOFJointAxisFlag"> @@ -1367,6 +1373,9 @@ <constant name="G6DOF_JOINT_FLAG_ENABLE_MOTOR" value="2" enum="G6DOFJointAxisFlag"> If [code]set[/code] there is a rotational motor across these axes. </constant> + <constant name="G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR" value="3" enum="G6DOFJointAxisFlag"> + If [code]set[/code] there is a linear motor on this axis that targets a specific velocity. + </constant> <constant name="SHAPE_PLANE" value="0" enum="ShapeType"> The [Shape] is a [PlaneShape]. </constant> diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index eeb3b31fc2..1e34d63b13 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -73,6 +73,23 @@ void OS_Unix::debug_break() { assert(false); }; +static void handle_interrupt(int sig) { + if (ScriptDebugger::get_singleton() == NULL) + return; + + ScriptDebugger::get_singleton()->set_depth(-1); + ScriptDebugger::get_singleton()->set_lines_left(1); +} + +void OS_Unix::initialize_debugging() { + + if (ScriptDebugger::get_singleton() != NULL) { + struct sigaction action; + action.sa_handler = handle_interrupt; + sigaction(SIGINT, &action, NULL); + } +} + int OS_Unix::unix_initialize_audio(int p_audio_driver) { return 0; diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index db0fe1e00b..95b74d23ff 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -101,6 +101,7 @@ public: virtual int get_processor_count() const; virtual void debug_break(); + virtual void initialize_debugging(); virtual String get_executable_path() const; virtual String get_user_data_dir() const; diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index b584107bcb..4ac3e33cb9 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -898,7 +898,7 @@ Array EditorSelection::_get_transformable_selected_nodes() { return ret; } -Array EditorSelection::_get_selected_nodes() { +Array EditorSelection::get_selected_nodes() { Array ret; @@ -916,7 +916,7 @@ void EditorSelection::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &EditorSelection::clear); ClassDB::bind_method(D_METHOD("add_node", "node"), &EditorSelection::add_node); ClassDB::bind_method(D_METHOD("remove_node", "node"), &EditorSelection::remove_node); - ClassDB::bind_method(D_METHOD("get_selected_nodes"), &EditorSelection::_get_selected_nodes); + ClassDB::bind_method(D_METHOD("get_selected_nodes"), &EditorSelection::get_selected_nodes); ClassDB::bind_method(D_METHOD("get_transformable_selected_nodes"), &EditorSelection::_get_transformable_selected_nodes); ClassDB::bind_method(D_METHOD("_emit_change"), &EditorSelection::_emit_change); ADD_SIGNAL(MethodInfo("selection_changed")); diff --git a/editor/editor_data.h b/editor/editor_data.h index 5a0b58464a..f020d07ea7 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -226,7 +226,6 @@ private: List<Node *> selected_node_list; void _update_nl(); - Array _get_selected_nodes(); Array _get_transformable_selected_nodes(); void _emit_change(); @@ -234,6 +233,7 @@ protected: static void _bind_methods(); public: + Array get_selected_nodes(); void add_node(Node *p_node); void remove_node(Node *p_node); bool is_selected(Node *) const; diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp new file mode 100644 index 0000000000..d617089142 --- /dev/null +++ b/editor/rename_dialog.cpp @@ -0,0 +1,691 @@ +/*************************************************************************/ +/* rename_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rename_dialog.h" + +#include "editor_node.h" +#include "editor_settings.h" +#include "editor_themes.h" +#include "modules/regex/regex.h" +#include "plugins/script_editor_plugin.h" +#include "print_string.h" +#include "scene/gui/control.h" +#include "scene/gui/label.h" +#include "scene/gui/tab_container.h" + +RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo) { + + scene_tree_editor = p_scene_tree_editor; + undo_redo = p_undo_redo; + preview_node = NULL; + + set_title(TTR("Batch Rename")); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + + // -- Search/Replace Area + + GridContainer *grd_main = memnew(GridContainer); + grd_main->set_columns(2); + grd_main->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(grd_main); + + // ---- 1st & 2nd row + + Label *lbl_search = memnew(Label); + lbl_search->set_text(TTR("Search")); + + lne_search = memnew(LineEdit); + lne_search->set_placeholder(TTR("Search")); + lne_search->set_name("lne_search"); + lne_search->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *lbl_replace = memnew(Label); + lbl_replace->set_text(TTR("Replace")); + + lne_replace = memnew(LineEdit); + lne_replace->set_placeholder(TTR("Replace")); + lne_replace->set_name("lne_replace"); + lne_replace->set_h_size_flags(SIZE_EXPAND_FILL); + + grd_main->add_child(lbl_search); + grd_main->add_child(lbl_replace); + grd_main->add_child(lne_search); + grd_main->add_child(lne_replace); + + // ---- 3rd & 4th row + + Label *lbl_prefix = memnew(Label); + lbl_prefix->set_text(TTR("Prefix")); + + lne_prefix = memnew(LineEdit); + lne_prefix->set_placeholder(TTR("Prefix")); + lne_prefix->set_name("lne_prefix"); + lne_prefix->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *lbl_suffix = memnew(Label); + lbl_suffix->set_text(TTR("Suffix")); + + lne_suffix = memnew(LineEdit); + lne_suffix->set_placeholder(TTR("Suffix")); + lne_suffix->set_name("lne_suffix"); + lne_suffix->set_h_size_flags(SIZE_EXPAND_FILL); + + grd_main->add_child(lbl_prefix); + grd_main->add_child(lbl_suffix); + grd_main->add_child(lne_prefix); + grd_main->add_child(lne_suffix); + + // -- Feature Tabs + + const int feature_min_height = 160; + + Ref<Theme> collapse_theme = create_editor_theme(); + collapse_theme->set_icon("checked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowDown", "EditorIcons")); + collapse_theme->set_icon("unchecked", "CheckBox", collapse_theme->get_icon("GuiTreeArrowRight", "EditorIcons")); + + CheckBox *chk_collapse_features = memnew(CheckBox); + chk_collapse_features->set_text(TTR("Advanced options")); + chk_collapse_features->set_theme(collapse_theme); + chk_collapse_features->set_focus_mode(FOCUS_NONE); + vbc->add_child(chk_collapse_features); + + tabc_features = memnew(TabContainer); + tabc_features->set_tab_align(TabContainer::ALIGN_LEFT); + vbc->add_child(tabc_features); + + // ---- Tab Substitute + + VBoxContainer *vbc_substitute = memnew(VBoxContainer); + vbc_substitute->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_substitute->set_custom_minimum_size(Size2(0, feature_min_height)); + + vbc_substitute->set_name(TTR("Substitute")); + tabc_features->add_child(vbc_substitute); + + cbut_substitute = memnew(CheckButton); + cbut_substitute->set_text(TTR("Substitute")); + vbc_substitute->add_child(cbut_substitute); + + GridContainer *grd_substitute = memnew(GridContainer); + grd_substitute->set_columns(3); + vbc_substitute->add_child(grd_substitute); + + // Name + + but_insert_name = memnew(Button); + but_insert_name->set_text("NAME"); + but_insert_name->set_tooltip(String("${NAME}\n") + TTR("Node name")); + but_insert_name->set_focus_mode(FOCUS_NONE); + but_insert_name->connect("pressed", this, "_insert_text", make_binds("${NAME}")); + but_insert_name->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_name); + + // Parent + + but_insert_parent = memnew(Button); + but_insert_parent->set_text("PARENT"); + but_insert_parent->set_tooltip(String("${PARENT}\n") + TTR("Node's parent name, if available")); + but_insert_parent->set_focus_mode(FOCUS_NONE); + but_insert_parent->connect("pressed", this, "_insert_text", make_binds("${PARENT}")); + but_insert_parent->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_parent); + + // Type + + but_insert_type = memnew(Button); + but_insert_type->set_text("TYPE"); + but_insert_type->set_tooltip(String("${TYPE}\n") + TTR("Node type")); + but_insert_type->set_focus_mode(FOCUS_NONE); + but_insert_type->connect("pressed", this, "_insert_text", make_binds("${TYPE}")); + but_insert_type->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_type); + + // Scene + + but_insert_scene = memnew(Button); + but_insert_scene->set_text("SCENE"); + but_insert_scene->set_tooltip(String("${SCENE}\n") + TTR("Current scene name")); + but_insert_scene->set_focus_mode(FOCUS_NONE); + but_insert_scene->connect("pressed", this, "_insert_text", make_binds("${SCENE}")); + but_insert_scene->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_scene); + + // Root + + but_insert_root = memnew(Button); + but_insert_root->set_text("ROOT"); + but_insert_root->set_tooltip(String("${ROOT}\n") + TTR("Root node name")); + but_insert_root->set_focus_mode(FOCUS_NONE); + but_insert_root->connect("pressed", this, "_insert_text", make_binds("${ROOT}")); + but_insert_root->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_root); + + // Count + + but_insert_count = memnew(Button); + but_insert_count->set_text("COUNTER"); + but_insert_count->set_tooltip(String("${COUNTER}\n") + TTR("Sequential integer counter.\nCompare counter options.")); + but_insert_count->set_focus_mode(FOCUS_NONE); + but_insert_count->connect("pressed", this, "_insert_text", make_binds("${COUNTER}")); + but_insert_count->set_h_size_flags(SIZE_EXPAND_FILL); + grd_substitute->add_child(but_insert_count); + + chk_per_level_counter = memnew(CheckBox); + chk_per_level_counter->set_text(TTR("Per Level counter")); + chk_per_level_counter->set_tooltip(TTR("If set the counter restarts for each group of child nodes")); + vbc_substitute->add_child(chk_per_level_counter); + + HBoxContainer *hbc_count_options = memnew(HBoxContainer); + vbc_substitute->add_child(hbc_count_options); + + Label *lbl_count_start = memnew(Label); + lbl_count_start->set_text(TTR("Start")); + lbl_count_start->set_tooltip(TTR("Initial value for the counter")); + hbc_count_options->add_child(lbl_count_start); + + spn_count_start = memnew(SpinBox); + spn_count_start->set_tooltip(TTR("Initial value for the counter")); + spn_count_start->set_step(1); + spn_count_start->set_min(0); + hbc_count_options->add_child(spn_count_start); + + Label *lbl_count_step = memnew(Label); + lbl_count_step->set_text(TTR("Step")); + lbl_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + hbc_count_options->add_child(lbl_count_step); + + spn_count_step = memnew(SpinBox); + spn_count_step->set_tooltip(TTR("Ammount by which counter is incremented for each node")); + spn_count_step->set_step(1); + hbc_count_options->add_child(spn_count_step); + + Label *lbl_count_padding = memnew(Label); + lbl_count_padding->set_text(TTR("Padding")); + lbl_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + hbc_count_options->add_child(lbl_count_padding); + + spn_count_padding = memnew(SpinBox); + spn_count_padding->set_tooltip(TTR("Minium number of digits for the counter.\nMissing digits are padded with leading zeros.")); + spn_count_padding->set_step(1); + hbc_count_options->add_child(spn_count_padding); + + // ---- Tab RegEx + + VBoxContainer *vbc_regex = memnew(VBoxContainer); + vbc_regex->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_regex->set_name(TTR("Regular Expressions")); + vbc_regex->set_custom_minimum_size(Size2(0, feature_min_height)); + tabc_features->add_child(vbc_regex); + + cbut_regex = memnew(CheckButton); + cbut_regex->set_text(TTR("Regular Expressions")); + vbc_regex->add_child(cbut_regex); + + // ---- Tab Process + + VBoxContainer *vbc_process = memnew(VBoxContainer); + vbc_process->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_process->set_name(TTR("Post-Process")); + vbc_process->set_custom_minimum_size(Size2(0, feature_min_height)); + tabc_features->add_child(vbc_process); + + cbut_process = memnew(CheckButton); + cbut_process->set_text(TTR("Post-Process")); + vbc_process->add_child(cbut_process); + + // ------ Style + + HBoxContainer *hbc_style = memnew(HBoxContainer); + vbc_process->add_child(hbc_style); + + Label *lbl_style = memnew(Label); + lbl_style->set_text(TTR("Style")); + hbc_style->add_child(lbl_style); + + opt_style = memnew(OptionButton); + opt_style->add_item(TTR("Keep")); + opt_style->add_item(TTR("CamelCase to under_scored")); + opt_style->add_item(TTR("under_scored to CamelCase")); + hbc_style->add_child(opt_style); + + // ------ Case + + HBoxContainer *hbc_case = memnew(HBoxContainer); + vbc_process->add_child(hbc_case); + + Label *lbl_case = memnew(Label); + lbl_case->set_text(TTR("Case")); + hbc_case->add_child(lbl_case); + + opt_case = memnew(OptionButton); + opt_case->add_item(TTR("Keep")); + opt_case->add_item(TTR("To Lowercase")); + opt_case->add_item(TTR("To Uppercase")); + hbc_case->add_child(opt_case); + + // -- Preview + + HSeparator *sep_preview = memnew(HSeparator); + sep_preview->set_custom_minimum_size(Size2(10, 20)); + vbc->add_child(sep_preview); + + lbl_preview_title = memnew(Label); + lbl_preview_title->set_text(TTR("Preview")); + vbc->add_child(lbl_preview_title); + + lbl_preview = memnew(Label); + lbl_preview->set_text(""); + lbl_preview->add_color_override("font_color", Color(1, 0.5f, 0, 1)); + vbc->add_child(lbl_preview); + + // ---- Dialog related + + set_custom_minimum_size(Size2(383, 0)); + set_as_toplevel(true); + get_ok()->set_text(TTR("Rename")); + Button *but_reset = add_button(TTR("Reset")); + + eh.errfunc = _error_handler; + eh.userdata = this; + + // ---- Connections + + chk_collapse_features->connect("toggled", this, "_features_toggled"); + + // Substitite Buttons + + lne_search->connect("focus_entered", this, "_update_substitute"); + lne_search->connect("focus_exited", this, "_update_substitute"); + lne_replace->connect("focus_entered", this, "_update_substitute"); + lne_replace->connect("focus_exited", this, "_update_substitute"); + lne_prefix->connect("focus_entered", this, "_update_substitute"); + lne_prefix->connect("focus_exited", this, "_update_substitute"); + lne_suffix->connect("focus_entered", this, "_update_substitute"); + lne_suffix->connect("focus_exited", this, "_update_substitute"); + + // Preview + + lne_prefix->connect("text_changed", this, "_update_preview"); + lne_suffix->connect("text_changed", this, "_update_preview"); + lne_search->connect("text_changed", this, "_update_preview"); + lne_replace->connect("text_changed", this, "_update_preview"); + spn_count_start->connect("value_changed", this, "_update_preview_int"); + spn_count_step->connect("value_changed", this, "_update_preview_int"); + spn_count_padding->connect("value_changed", this, "_update_preview_int"); + opt_style->connect("item_selected", this, "_update_preview_int"); + opt_case->connect("item_selected", this, "_update_preview_int"); + cbut_substitute->connect("pressed", this, "_update_preview", varray("")); + cbut_regex->connect("pressed", this, "_update_preview", varray("")); + cbut_process->connect("pressed", this, "_update_preview", varray("")); + + but_reset->connect("pressed", this, "reset"); + + reset(); + _features_toggled(false); +} + +void RenameDialog::_bind_methods() { + + ClassDB::bind_method("_features_toggled", &RenameDialog::_features_toggled); + ClassDB::bind_method("_update_preview", &RenameDialog::_update_preview); + ClassDB::bind_method("_update_preview_int", &RenameDialog::_update_preview_int); + ClassDB::bind_method("_insert_text", &RenameDialog::_insert_text); + ClassDB::bind_method("_update_substitute", &RenameDialog::_update_substitute); + ClassDB::bind_method("reset", &RenameDialog::reset); + ClassDB::bind_method("rename", &RenameDialog::rename); +} + +void RenameDialog::_update_substitute() { + + LineEdit *focus_owner_line_edit = Object::cast_to<LineEdit>(get_focus_owner()); + bool is_main_field = _is_main_field(focus_owner_line_edit); + + but_insert_name->set_disabled(!is_main_field); + but_insert_parent->set_disabled(!is_main_field); + but_insert_type->set_disabled(!is_main_field); + but_insert_scene->set_disabled(!is_main_field); + but_insert_root->set_disabled(!is_main_field); + but_insert_count->set_disabled(!is_main_field); + + // The focus mode seems to be reset when disabling/re-enabling + but_insert_name->set_focus_mode(FOCUS_NONE); + but_insert_parent->set_focus_mode(FOCUS_NONE); + but_insert_type->set_focus_mode(FOCUS_NONE); + but_insert_scene->set_focus_mode(FOCUS_NONE); + but_insert_root->set_focus_mode(FOCUS_NONE); + but_insert_count->set_focus_mode(FOCUS_NONE); +} + +void RenameDialog::_post_popup() { + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + preview_node = NULL; + + Array selected_node_list = editor_selection->get_selected_nodes(); + ERR_FAIL_COND(selected_node_list.size() == 0); + + preview_node = selected_node_list[0]; + + _update_preview(); + _update_substitute(); +} + +void RenameDialog::_update_preview_int(int new_value) { + _update_preview(); +} + +void RenameDialog::_update_preview(String new_text) { + + if (lock_preview_update || preview_node == NULL) + return; + + has_errors = false; + add_error_handler(&eh); + + String new_name = _apply_rename(preview_node, spn_count_start->get_value()); + + if (!has_errors) { + + lbl_preview_title->set_text(TTR("Preview")); + lbl_preview->set_text(new_name); + + if (new_name == preview_node->get_name()) { + lbl_preview->add_color_override("font_color", Color(0, 0.5f, 0.25f, 1)); + } else { + lbl_preview->add_color_override("font_color", Color(0, 1, 0.5f, 1)); + } + } + + remove_error_handler(&eh); +} + +String RenameDialog::_apply_rename(const Node *node, int count) { + + String search = lne_search->get_text(); + String replace = lne_replace->get_text(); + String prefix = lne_prefix->get_text(); + String suffix = lne_suffix->get_text(); + String new_name = node->get_name(); + + if (cbut_substitute->is_pressed()) { + search = _substitute(search, node, count); + replace = _substitute(replace, node, count); + prefix = _substitute(prefix, node, count); + suffix = _substitute(suffix, node, count); + } + + if (cbut_regex->is_pressed()) { + + new_name = _regex(search, new_name, replace); + } else { + new_name = new_name.replace(search, replace); + } + + new_name = prefix + new_name + suffix; + + if (cbut_process->is_pressed()) { + new_name = _postprocess(new_name); + } + + return new_name; +} + +String RenameDialog::_substitute(const String &subject, const Node *node, int count) { + + String result = subject.replace("${COUNTER}", vformat("%0" + itos(spn_count_padding->get_value()) + "d", count)); + + if (node) { + result = result.replace("${NAME}", node->get_name()); + result = result.replace("${TYPE}", node->get_class()); + } + + int current = EditorNode::get_singleton()->get_editor_data().get_edited_scene(); + result = result.replace("${SCENE}", EditorNode::get_singleton()->get_editor_data().get_scene_title(current)); + + Node *root_node = SceneTree::get_singleton()->get_edited_scene_root(); + if (root_node) { + result = result.replace("${ROOT}", root_node->get_name()); + } + + Node *parent_node = node->get_parent(); + if (parent_node) { + if (node == root_node) { + // Can not substitute parent of root. + result = result.replace("${PARENT}", ""); + } else { + result = result.replace("${PARENT}", parent_node->get_name()); + } + } + + return result; +} + +void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type) { + + RenameDialog *self = (RenameDialog *)p_self; + String source_file(p_file); + + // Only show first error that is related to "regex" + if (self->has_errors || source_file.find("regex") < 0) + return; + + String err_str; + if (p_errorexp && p_errorexp[0]) { + err_str = p_errorexp; + } else { + err_str = p_error; + } + + self->has_errors = true; + self->lbl_preview_title->set_text(TTR("Error")); + self->lbl_preview->add_color_override("font_color", Color(1, 0.25f, 0, 1)); + self->lbl_preview->set_text(err_str); +} + +String RenameDialog::_regex(const String &pattern, const String &subject, const String &replacement) { + + RegEx regex(pattern); + + return regex.sub(subject, replacement, true); +} + +String RenameDialog::_postprocess(const String &subject) { + + int style_id = opt_style->get_selected(); + + String result = subject; + + if (style_id == 1) { + + // CamelCase to Under_Line + result = result.camelcase_to_underscore(true); + result = _regex("_+", result, "_"); + + } else if (style_id == 2) { + + // Under_Line to CamelCase + RegEx pattern("_+(.?)"); + Array matches = pattern.search_all(result); + + // _ name would become empty. Ignore + if (matches.size() && result != "_") { + String buffer; + int start = 0; + int end = 0; + for (int i = 0; i < matches.size(); ++i) { + start = ((Ref<RegExMatch>)matches[i])->get_start(1); + buffer += result.substr(end, start - end - 1); + buffer += result.substr(start, 1).to_upper(); + end = start + 1; + } + buffer += result.substr(end, result.size() - (end + 1)); + result = buffer.replace("_", "").capitalize(); + } + } + + int case_id = opt_case->get_selected(); + + if (case_id == 1) { + // To Lowercase + result = result.to_lower(); + } else if (case_id == 2) { + // To Upercase + result = result.to_upper(); + } + + return result; +} + +void RenameDialog::_iterate_scene(const Node *node, const Array &selection, int *counter) { + + if (!node) + return; + + if (selection.has(node)) { + + String new_name = _apply_rename(node, *counter); + + if (node->get_name() != new_name) { + Pair<NodePath, String> rename_item; + rename_item.first = node->get_path(); + rename_item.second = new_name; + to_rename.push_back(rename_item); + } + + *counter += spn_count_step->get_value(); + } + + int *cur_counter = counter; + int level_counter = spn_count_start->get_value(); + + if (chk_per_level_counter->is_pressed()) { + cur_counter = &level_counter; + } + + for (int i = 0; i < node->get_child_count(); ++i) { + _iterate_scene(node->get_child(i), selection, cur_counter); + } +} + +void RenameDialog::rename() { + + // Editor selection is not ordered via scene tree. Instead iterate + // over scene tree until all selected nodes are found in order. + + EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); + Array selected_node_list = editor_selection->get_selected_nodes(); + Node *root_node = SceneTree::get_singleton()->get_edited_scene_root(); + + global_count = spn_count_start->get_value(); + to_rename.clear(); + + // Forward recursive as opposed to the actual renaming. + _iterate_scene(root_node, selected_node_list, &global_count); + + if (undo_redo && !to_rename.empty()) { + + undo_redo->create_action(TTR("Batch Rename")); + + // Make sure to iterate reversed so that child nodes will find parents. + for (int i = to_rename.size() - 1; i >= 0; --i) { + + Node *n = root_node->get_node(to_rename[i].first); + const String &new_name = to_rename[i].second; + + if (!n) { + ERR_PRINTS("Skipping missing node: " + to_rename[i].first.get_concatenated_subnames()); + continue; + } + + scene_tree_editor->emit_signal("node_prerename", n, new_name); + undo_redo->add_do_method(scene_tree_editor, "_rename_node", n->get_instance_id(), new_name); + undo_redo->add_undo_method(scene_tree_editor, "_rename_node", n->get_instance_id(), n->get_name()); + } + + undo_redo->commit_action(); + } +} + +void RenameDialog::reset() { + + lock_preview_update = true; + + lne_prefix->clear(); + lne_suffix->clear(); + lne_search->clear(); + lne_replace->clear(); + + cbut_substitute->set_pressed(false); + cbut_regex->set_pressed(false); + cbut_process->set_pressed(false); + + chk_per_level_counter->set_pressed(true); + + spn_count_start->set_value(1); + spn_count_step->set_value(1); + spn_count_padding->set_value(1); + + opt_style->select(0); + opt_case->select(0); + + lock_preview_update = false; + _update_preview(); +} + +bool RenameDialog::_is_main_field(LineEdit *line_edit) { + return line_edit && + (line_edit == lne_search || line_edit == lne_replace || line_edit == lne_prefix || line_edit == lne_suffix); +} + +void RenameDialog::_insert_text(String text) { + + LineEdit *focus_owner = Object::cast_to<LineEdit>(get_focus_owner()); + + if (_is_main_field(focus_owner)) { + focus_owner->selection_delete(); + focus_owner->append_at_cursor(text); + _update_preview(); + } +} + +void RenameDialog::_features_toggled(bool pressed) { + if (pressed) { + tabc_features->show(); + } else { + tabc_features->hide(); + } + + // Adjust to minimum size in y + Size2i size = get_size(); + size.y = 0; + set_size(size); +} diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h new file mode 100644 index 0000000000..c5ebc30c0c --- /dev/null +++ b/editor/rename_dialog.h @@ -0,0 +1,119 @@ +/*************************************************************************/ +/* rename_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENAME_DIALOG_H +#define RENAME_DIALOG_H + +#include "scene/gui/check_box.h" +#include "scene/gui/check_button.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/option_button.h" +#include "scene/gui/spin_box.h" + +#include "editor/scene_tree_editor.h" +#include "undo_redo.h" + +/** +@author Blazej Floch +*/ + +class RenameDialog : public ConfirmationDialog { + + GDCLASS(RenameDialog, ConfirmationDialog); + + virtual void ok_pressed() { rename(); }; + void _cancel_pressed(){}; + void _features_toggled(bool pressed); + void _insert_text(String text); + void _update_substitute(); + bool _is_main_field(LineEdit *line_edit); + + void _iterate_scene(const Node *node, const Array &selection, int *count); + String _apply_rename(const Node *node, int count); + String _substitute(const String &subject, const Node *node, int count); + String _regex(const String &pattern, const String &subject, const String &replacement); + String _postprocess(const String &subject); + void _update_preview(String new_text = ""); + void _update_preview_int(int new_value = 0); + static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type); + + SceneTreeEditor *scene_tree_editor; + UndoRedo *undo_redo; + int global_count; + + LineEdit *lne_search; + LineEdit *lne_replace; + LineEdit *lne_prefix; + LineEdit *lne_suffix; + + TabContainer *tabc_features; + + CheckButton *cbut_substitute; + CheckButton *cbut_regex; + CheckButton *cbut_process; + CheckBox *chk_per_level_counter; + + Button *but_insert_name; + Button *but_insert_parent; + Button *but_insert_type; + Button *but_insert_scene; + Button *but_insert_root; + Button *but_insert_count; + + SpinBox *spn_count_start; + SpinBox *spn_count_step; + SpinBox *spn_count_padding; + + OptionButton *opt_style; + OptionButton *opt_case; + + Label *lbl_preview_title; + Label *lbl_preview; + + List<Pair<NodePath, String> > to_rename; + Node *preview_node; + bool lock_preview_update; + ErrorHandlerList eh; + bool has_errors; + +protected: + void _notification(int p_what){}; + static void _bind_methods(); + virtual void _post_popup(); + +public: + void reset(); + void rename(); + + RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo = NULL); + ~RenameDialog(){}; +}; + +#endif diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index ba661813d6..8b99a3d503 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -73,7 +73,9 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) { if (!p_event->is_pressed() || p_event->is_echo()) return; - if (ED_IS_SHORTCUT("scene_tree/add_child_node", p_event)) { + if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) { + _tool_selected(TOOL_BATCH_RENAME); + } else if (ED_IS_SHORTCUT("scene_tree/add_child_node", p_event)) { _tool_selected(TOOL_NEW); } else if (ED_IS_SHORTCUT("scene_tree/instance_scene", p_event)) { _tool_selected(TOOL_INSTANCE); @@ -285,6 +287,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { switch (p_tool) { + case TOOL_BATCH_RENAME: { + Tree *tree = scene_tree->get_scene_tree(); + if (tree->is_anything_selected()) { + rename_dialog->popup_centered(); + } + } break; case TOOL_NEW: { String preferred = ""; @@ -1862,6 +1870,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->clear(); + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); if (selection.size() == 1) { subresources.clear(); @@ -2055,6 +2064,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter_hbc->add_constant_override("separate", 0); ToolButton *tb; + ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2); ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A); ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene")); ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); @@ -2153,6 +2163,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(create_dialog); create_dialog->connect("create", this, "_create"); + rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); + add_child(rename_dialog); + script_create_dialog = memnew(ScriptCreateDialog); add_child(script_create_dialog); script_create_dialog->connect("script_created", this, "_script_created"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index a4f36e31ee..fd7d616a80 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_sub_scene.h" #include "editor/groups_editor.h" +#include "editor/rename_dialog.h" #include "editor/reparent_dialog.h" #include "editor/script_create_dialog.h" #include "scene/animation/animation_player.h" @@ -58,6 +59,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_NEW, TOOL_INSTANCE, + TOOL_BATCH_RENAME, TOOL_REPLACE, TOOL_ATTACH_SCRIPT, TOOL_CLEAR_SCRIPT, @@ -90,6 +92,7 @@ class SceneTreeDock : public VBoxContainer { int current_option; CreateDialog *create_dialog; + RenameDialog *rename_dialog; ToolButton *button_add; ToolButton *button_instance; diff --git a/main/main.cpp b/main/main.cpp index fa60bd4e96..5959deb495 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -730,6 +730,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (debug_mode == "local") { script_debugger = memnew(ScriptDebuggerLocal); + OS::get_singleton()->initialize_debugging(); } FileAccessNetwork::configure(); diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp index 151a79a69f..adfad7803f 100644 --- a/modules/bullet/generic_6dof_joint_bullet.cpp +++ b/modules/bullet/generic_6dof_joint_bullet.cpp @@ -138,6 +138,12 @@ void Generic6DOFJointBullet::set_param(Vector3::Axis p_axis, PhysicsServer::G6DO case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING: sixDOFConstraint->getTranslationalLimitMotor()->m_damping = p_value; break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: + sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis] = p_value; + break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: + sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis] = p_value; + break; case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: limits_lower[1][p_axis] = p_value; set_flag(p_axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, flags[p_axis][p_param]); // Reload bullet parameter @@ -185,6 +191,10 @@ real_t Generic6DOFJointBullet::get_param(Vector3::Axis p_axis, PhysicsServer::G6 return sixDOFConstraint->getTranslationalLimitMotor()->m_restitution; case PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING: return sixDOFConstraint->getTranslationalLimitMotor()->m_damping; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: + return sixDOFConstraint->getTranslationalLimitMotor()->m_targetVelocity.m_floats[p_axis]; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: + return sixDOFConstraint->getTranslationalLimitMotor()->m_maxMotorForce.m_floats[p_axis]; case PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: return limits_lower[1][p_axis]; case PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: @@ -232,6 +242,9 @@ void Generic6DOFJointBullet::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOF case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR: sixDOFConstraint->getRotationalLimitMotor(p_axis)->m_enableMotor = flags[p_axis][p_flag]; break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: + sixDOFConstraint->getTranslationalLimitMotor()->m_enableMotor[p_axis] = flags[p_axis][p_flag]; + break; default: WARN_PRINT("This flag is not supported by Bullet engine"); return; diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 8d25dfec0d..a88ba477c6 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -330,10 +330,24 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ } break; case MATH_LERP: { VALIDATE_ARG_COUNT(3); - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); VALIDATE_ARG_NUM(2); - r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]); + const double t = (double)*p_args[2]; + switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::REAL) { + case Variant::VECTOR2: { + r_ret = ((Vector2)*p_args[0]).linear_interpolate((Vector2)*p_args[1], t); + } break; + case Variant::VECTOR3: { + r_ret = ((Vector3)*p_args[0]).linear_interpolate((Vector3)*p_args[1], t); + } break; + case Variant::COLOR: { + r_ret = ((Color)*p_args[0]).linear_interpolate((Color)*p_args[1], t); + } break; + default: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t); + } break; + } } break; case MATH_INVERSE_LERP: { VALIDATE_ARG_COUNT(3); @@ -1500,7 +1514,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { return mi; } break; case MATH_LERP: { - MethodInfo mi("lerp", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight")); + MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::REAL, "weight")); mi.return_val.type = Variant::REAL; return mi; } break; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 6ed03d7aee..6fe137a386 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1643,9 +1643,9 @@ public: List<String> args; args.push_back("-digestalg"); - args.push_back("SHA1"); + args.push_back("SHA-256"); args.push_back("-sigalg"); - args.push_back("MD5withRSA"); + args.push_back("SHA256withRSA"); String tsa_url = EditorSettings::get_singleton()->get("export/android/timestamping_authority_url"); if (tsa_url != "") { args.push_back("-tsa"); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 2ce55d98be..6d559520d7 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -193,7 +193,10 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ if manual_msvc_config: # should be automatic if SCons found it - env.Append(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) + if os.getenv("WindowsSdkDir") is not None: + env.Append(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) + else: + print("Missing environment variable: WindowsSdkDir") env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', @@ -211,7 +214,10 @@ def configure_msvc(env, manual_msvc_config): env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) if manual_msvc_config: - env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) + if os.getenv("WindowsSdkDir") is not None: + env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) + else: + print("Missing environment variable: WindowsSdkDir") ## LTO diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index bed5781ae4..7b46608c55 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -152,6 +152,25 @@ void RedirectIOToConsole() { // point to console as well } +BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { + if (ScriptDebugger::get_singleton() == NULL) + return FALSE; + + switch (dwCtrlType) { + case CTRL_C_EVENT: + ScriptDebugger::get_singleton()->set_depth(-1); + ScriptDebugger::get_singleton()->set_lines_left(1); + return TRUE; + default: + return FALSE; + } +} + +void OS_Windows::initialize_debugging() { + + SetConsoleCtrlHandler(HandlerRoutine, TRUE); +} + void OS_Windows::initialize_core() { crash_handler.initialize(); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 3d13627bfa..584f6fb334 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -294,6 +294,7 @@ public: void disable_crash_handler(); bool is_disable_crash_handler() const; + virtual void initialize_debugging(); void force_process_input(); diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 447bd9a090..60a7961293 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -351,7 +351,7 @@ void AnimatedSprite::_notification(int p_what) { if (frame < 0) return; - float speed = frames->get_animation_speed(animation); + float speed = frames->get_animation_speed(animation) * speed_scale; if (speed == 0) return; //do nothing @@ -481,6 +481,16 @@ int AnimatedSprite::get_frame() const { return frame; } +void AnimatedSprite::set_speed_scale(float p_speed_scale) { + + speed_scale = MAX(p_speed_scale, 0.0f); +} + +float AnimatedSprite::get_speed_scale() const { + + return speed_scale; +} + void AnimatedSprite::set_centered(bool p_center) { centered = p_center; @@ -570,7 +580,7 @@ void AnimatedSprite::_reset_timeout() { return; if (frames.is_valid() && frames->has_animation(animation)) { - float speed = frames->get_animation_speed(animation); + float speed = frames->get_animation_speed(animation) * speed_scale; if (speed > 0) { timeout = 1.0 / speed; } else { @@ -636,6 +646,9 @@ void AnimatedSprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite::get_frame); + ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite::set_speed_scale); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite::get_speed_scale); + ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite::_res_changed); ADD_SIGNAL(MethodInfo("frame_changed")); @@ -644,6 +657,7 @@ void AnimatedSprite::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); @@ -658,6 +672,7 @@ AnimatedSprite::AnimatedSprite() { vflip = false; frame = 0; + speed_scale = 1.0f; playing = false; animation = "default"; timeout = 0; diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index c7606d88aa..7b91a1faef 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -129,6 +129,7 @@ class AnimatedSprite : public Node2D { bool playing; StringName animation; int frame; + float speed_scale; bool centered; Point2 offset; @@ -172,6 +173,9 @@ public: void set_frame(int p_frame); int get_frame() const; + void set_speed_scale(float p_speed_scale); + float get_speed_scale() const; + void set_centered(bool p_center); bool is_centered() const; diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index bc39368c88..64d0771fab 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -68,31 +68,31 @@ bool Sprite::_edit_use_rect() const { void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const { - Size2 s; - r_filter_clip = false; + Rect2 base_rect; if (region) { - - s = region_rect.size; - r_src_rect = region_rect; r_filter_clip = region_filter_clip; + base_rect = region_rect; } else { - s = Size2(texture->get_size()); - s = s / Size2(hframes, vframes); - - r_src_rect.size = s; - r_src_rect.position.x = float(frame % hframes) * s.x; - r_src_rect.position.y = float(frame / hframes) * s.y; + r_filter_clip = false; + base_rect = Rect2(0, 0, texture->get_width(), texture->get_height()); } - Point2 ofs = offset; + Size2 frame_size = base_rect.size / Size2(hframes, vframes); + Point2 frame_offset = Point2(frame % hframes, frame / hframes); + frame_offset *= frame_size; + + r_src_rect.size = frame_size; + r_src_rect.position = base_rect.position + frame_offset; + + Point2 dest_offset = offset; if (centered) - ofs -= s / 2; + dest_offset -= frame_size / 2; if (Engine::get_singleton()->get_use_pixel_snap()) { - ofs = ofs.floor(); + dest_offset = dest_offset.floor(); } - r_dst_rect = Rect2(ofs, s); + r_dst_rect = Rect2(dest_offset, frame_size); if (hflip) r_dst_rect.size.x = -r_dst_rect.size.x; @@ -375,13 +375,13 @@ Rect2 Sprite::get_rect() const { Size2i s; if (region) { - s = region_rect.size; } else { s = texture->get_size(); - s = s / Point2(hframes, vframes); } + s = s / Point2(hframes, vframes); + Point2 ofs = offset; if (centered) ofs -= s / 2; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 3d3f43d5c6..60766862cc 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -845,13 +845,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { mask |= TileSet::BIND_BOTTOMRIGHT; } } else if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3) { - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) { mask |= TileSet::BIND_TOPLEFT; } if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) { mask |= TileSet::BIND_TOP; } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) { mask |= TileSet::BIND_TOPRIGHT; } if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { @@ -861,13 +861,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { mask |= TileSet::BIND_RIGHT; } - if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) { mask |= TileSet::BIND_BOTTOMLEFT; } if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) { mask |= TileSet::BIND_BOTTOM; } - if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) { + if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) { mask |= TileSet::BIND_BOTTOMRIGHT; } } diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp index 2e9f1a241a..b2d10006f7 100644 --- a/scene/3d/physics_joint.cpp +++ b/scene/3d/physics_joint.cpp @@ -716,6 +716,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x"); @@ -734,6 +737,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y"); @@ -752,6 +758,9 @@ void Generic6DOFJoint::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z"); @@ -769,6 +778,8 @@ void Generic6DOFJoint::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS); BIND_ENUM_CONSTANT(PARAM_LINEAR_RESTITUTION); BIND_ENUM_CONSTANT(PARAM_LINEAR_DAMPING); + BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_TARGET_VELOCITY); + BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_FORCE_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(PARAM_ANGULAR_LIMIT_SOFTNESS); @@ -783,6 +794,7 @@ void Generic6DOFJoint::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_LIMIT); BIND_ENUM_CONSTANT(FLAG_ENABLE_ANGULAR_LIMIT); BIND_ENUM_CONSTANT(FLAG_ENABLE_MOTOR); + BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_MOTOR); BIND_ENUM_CONSTANT(FLAG_MAX); } @@ -912,6 +924,8 @@ Generic6DOFJoint::Generic6DOFJoint() { set_param_x(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_x(PARAM_LINEAR_RESTITUTION, 0.5); set_param_x(PARAM_LINEAR_DAMPING, 1.0); + set_param_x(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_x(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_x(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_x(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_x(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -925,12 +939,15 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_x(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_x(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_x(FLAG_ENABLE_MOTOR, false); + set_flag_x(FLAG_ENABLE_LINEAR_MOTOR, false); set_param_y(PARAM_LINEAR_LOWER_LIMIT, 0); set_param_y(PARAM_LINEAR_UPPER_LIMIT, 0); set_param_y(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_y(PARAM_LINEAR_RESTITUTION, 0.5); set_param_y(PARAM_LINEAR_DAMPING, 1.0); + set_param_y(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_y(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_y(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_y(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_y(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -944,12 +961,15 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_y(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_y(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_y(FLAG_ENABLE_MOTOR, false); + set_flag_y(FLAG_ENABLE_LINEAR_MOTOR, false); set_param_z(PARAM_LINEAR_LOWER_LIMIT, 0); set_param_z(PARAM_LINEAR_UPPER_LIMIT, 0); set_param_z(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7); set_param_z(PARAM_LINEAR_RESTITUTION, 0.5); set_param_z(PARAM_LINEAR_DAMPING, 1.0); + set_param_z(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0); + set_param_z(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0); set_param_z(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_z(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_z(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -963,4 +983,5 @@ Generic6DOFJoint::Generic6DOFJoint() { set_flag_z(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_z(FLAG_ENABLE_LINEAR_LIMIT, true); set_flag_z(FLAG_ENABLE_MOTOR, false); + set_flag_z(FLAG_ENABLE_LINEAR_MOTOR, false); } diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h index 000109ac55..37870d6f30 100644 --- a/scene/3d/physics_joint.h +++ b/scene/3d/physics_joint.h @@ -249,6 +249,8 @@ public: PARAM_LINEAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, PARAM_LINEAR_RESTITUTION = PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, PARAM_LINEAR_DAMPING = PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, + PARAM_LINEAR_MOTOR_TARGET_VELOCITY = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY, + PARAM_LINEAR_MOTOR_FORCE_LIMIT = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT, PARAM_ANGULAR_LOWER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, PARAM_ANGULAR_UPPER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, PARAM_ANGULAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, @@ -265,6 +267,7 @@ public: FLAG_ENABLE_LINEAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, FLAG_ENABLE_ANGULAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, FLAG_ENABLE_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR, + FLAG_ENABLE_LINEAR_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR, FLAG_MAX = PhysicsServer::G6DOF_JOINT_FLAG_MAX }; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 304faed9bd..e9314ba8dd 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -124,7 +124,6 @@ private: void shift_selection_check_post(bool); void selection_fill_at_cursor(); - void selection_delete(); void set_window_pos(int p_pos); void set_cursor_at_pixel_pos(int p_x); @@ -158,6 +157,7 @@ public: void select(int p_from = 0, int p_to = -1); void select_all(); + void selection_delete(); void deselect(); void delete_char(); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index a9402d6404..c5e4149782 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -297,7 +297,7 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_focused"), &OptionButton::_focused); ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item); + ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &OptionButton::set_item_text); ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "texture"), &OptionButton::set_item_icon); ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled); diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 24b04c0c4a..1443d5efbf 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -52,6 +52,7 @@ bool InstancePlaceholder::_get(const StringName &p_name, Variant &r_ret) const { } return false; } + void InstancePlaceholder::_get_property_list(List<PropertyInfo> *p_list) const { for (const List<PropSet>::Element *E = stored_values.front(); E; E = E->next()) { @@ -73,13 +74,14 @@ String InstancePlaceholder::get_instance_path() const { return path; } -void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) { - ERR_FAIL_COND(!is_inside_tree()); +Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene> &p_custom_scene) { + + ERR_FAIL_COND_V(!is_inside_tree(), NULL); Node *base = get_parent(); if (!base) - return; + return NULL; Ref<PackedScene> ps; if (p_custom_scene.is_valid()) @@ -88,7 +90,7 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s ps = ResourceLoader::load(path, "PackedScene"); if (!ps.is_valid()) - return; + return NULL; Node *scene = ps->instance(); scene->set_name(get_name()); int pos = get_position_in_parent(); @@ -97,11 +99,20 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s scene->set(E->get().name, E->get().value); } - queue_delete(); + if (p_replace) { + queue_delete(); + base->remove_child(this); + } - base->remove_child(this); base->add_child(scene); base->move_child(scene, pos); + + return scene; +} + +void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) { + //Deprecated by + create_instance(true, p_custom_scene); } Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { @@ -124,6 +135,7 @@ Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) { void InstancePlaceholder::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("create_instance", "replace", "custom_scene"), &InstancePlaceholder::create_instance, DEFVAL(false), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("replace_by_instance", "custom_scene"), &InstancePlaceholder::replace_by_instance, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("get_instance_path"), &InstancePlaceholder::get_instance_path); } diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h index d70f1318ea..2158257c93 100644 --- a/scene/main/instance_placeholder.h +++ b/scene/main/instance_placeholder.h @@ -60,6 +60,7 @@ public: Dictionary get_stored_values(bool p_with_order = false); + Node *create_instance(bool p_replace = false, const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>()); void replace_by_instance(const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>()); InstancePlaceholder(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 11b663e413..9a9a19ed1a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2168,9 +2168,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { p_control->set_position(gui.last_mouse_pos); p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport p_control->raise(); - if (gui.drag_preview) { - memdelete(gui.drag_preview); - } + gui.drag_preview = p_control; } diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index f01a4c2f64..08af26c24b 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -603,6 +603,8 @@ void PhysicsServer::_bind_methods() { BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS); BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_RESTITUTION); BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_DAMPING); + BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY); + BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS); @@ -616,6 +618,7 @@ void PhysicsServer::_bind_methods() { BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_MOTOR); + BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR); ClassDB::bind_method(D_METHOD("joint_get_type", "joint"), &PhysicsServer::joint_get_type); diff --git a/servers/physics_server.h b/servers/physics_server.h index 6a342b36d4..6712bee8dc 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -594,6 +594,8 @@ public: G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, G6DOF_JOINT_LINEAR_RESTITUTION, G6DOF_JOINT_LINEAR_DAMPING, + G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY, + G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT, G6DOF_JOINT_ANGULAR_LOWER_LIMIT, G6DOF_JOINT_ANGULAR_UPPER_LIMIT, G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, @@ -611,6 +613,7 @@ public: G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, G6DOF_JOINT_FLAG_ENABLE_MOTOR, + G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR, G6DOF_JOINT_FLAG_MAX }; |