diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 04d0c615ddb..55b5c3d0f17 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -22546,6 +22546,27 @@
Return the global position of the 2D node.
+
+
+
+
+ Return the global rotation in radians of the 2D node.
+
+
+
+
+
+
+ Return the global rotation in degrees of the 2D node.
+
+
+
+
+
+
+ Return the global scale of the 2D node.
+
+
@@ -22649,6 +22670,27 @@
Set the global position of the 2D node to 'pos'.
+
+
+
+
+ Set the global rotation in radians of the 2D node.
+
+
+
+
+
+
+ Set the global rotation in degrees of the 2D node.
+
+
+
+
+
+
+ Set the global scale of the 2D node.
+
+
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 3bbd8e8f04e..40bcce91ce9 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -661,6 +661,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<arguments[0],p_stack_level);
+ if (res<0)
+ return res;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+
+ res = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<arguments[2],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<& p_args,bool p_stat
tokenizer->advance();
} else {
+ parenthesis ++;
int argidx=0;
while(true) {
@@ -165,6 +166,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector& p_args,bool p_stat
}
}
+ parenthesis --;
}
return true;
@@ -360,18 +362,23 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *yield = alloc_node();
yield->op=OperatorNode::OP_YIELD;
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
expr=yield;
tokenizer->advance();
} else {
+ parenthesis ++;
+
Node *object = _parse_and_reduce_expression(p_parent,p_static);
if (!object)
return NULL;
yield->arguments.push_back(object);
if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
-
_set_error("Expected ',' after first argument of 'yield'");
return NULL;
}
@@ -384,11 +391,12 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
yield->arguments.push_back(signal);
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
-
_set_error("Expected ')' after second argument of 'yield'");
return NULL;
}
+ parenthesis --;
+
tokenizer->advance();
expr=yield;
@@ -921,6 +929,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break;
case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break;
case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break;
+ case GDTokenizer::TK_CF_IF: op=OperatorNode::OP_TERNARY_IF; break;
+ case GDTokenizer::TK_CF_ELSE: op=OperatorNode::OP_TERNARY_ELSE; break;
default: valid=false; break;
}
@@ -943,6 +953,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int next_op=-1;
int min_priority=0xFFFFF;
bool is_unary=false;
+ bool is_ternary=false;
for(int i=0;i=(expression.size()-1)) {
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if(next_op>=(expression.size()-2) || expression[next_op+2].op != OperatorNode::OP_TERNARY_ELSE) {
+ _set_error("Expected else after ternary if.");
+ ERR_FAIL_V(NULL);
+ }
+ if(next_op>=(expression.size()-3)) {
+ _set_error("Expected value after ternary else.");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node();
+ op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
+
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary if.");
+ return NULL;
+ }
+
+ if (expression[next_op+3].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary else.");
+ return NULL;
+ }
+
+
+ op->arguments.push_back(expression[next_op+1].node); //next expression goes as first
+ op->arguments.push_back(expression[next_op-1].node); //left expression goes as when-true
+ op->arguments.push_back(expression[next_op+3].node); //expression after next goes as when-false
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
} else {
if (next_op <1 || next_op>=(expression.size()-1)) {
@@ -1618,6 +1694,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
case GDTokenizer::TK_CF_IF: {
tokenizer->advance();
+
Node *condition = _parse_and_reduce_expression(p_block,p_static);
if (!condition) {
if (_recover_from_completion()) {
@@ -2217,6 +2294,11 @@ void GDParser::_parse_class(ClassNode *p_class) {
bool defaulting=false;
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PR_VAR) {
tokenizer->advance(); //var before the identifier is allowed
@@ -2269,6 +2351,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
default_values.push_back(on);
}
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
continue;
@@ -2310,6 +2396,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
//has arguments
+ parenthesis ++;
while(true) {
Node *arg = _parse_and_reduce_expression(p_class,_static);
@@ -2327,6 +2414,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
+ parenthesis --;
}
tokenizer->advance();
@@ -2387,6 +2475,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2402,6 +2494,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 4afc534a8cf..f20dffa483e 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -244,6 +244,9 @@ public:
OP_BIT_AND,
OP_BIT_OR,
OP_BIT_XOR,
+ //ternary operators
+ OP_TERNARY_IF,
+ OP_TERNARY_ELSE,
};
Operator op;
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 6ff8a9b9250..b3f925cb14e 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -295,6 +295,53 @@ void Node2D::set_global_pos(const Point2& p_pos) {
}
}
+
+float Node2D::get_global_rot() const {
+
+ return get_global_transform().get_rotation();
+}
+
+void Node2D::set_global_rot(float p_radians) {
+
+ CanvasItem *pi = get_parent_item();
+ if (pi) {
+ const float parent_global_rot = pi->get_global_transform().get_rotation();
+ set_rot(p_radians - parent_global_rot);
+ } else {
+ set_rot(p_radians);
+ }
+}
+
+
+float Node2D::get_global_rotd() const {
+
+ return Math::rad2deg(get_global_rot());
+}
+
+void Node2D::set_global_rotd(float p_degrees) {
+
+ set_global_rot(Math::deg2rad(p_degrees));
+}
+
+
+Size2 Node2D::get_global_scale() const {
+
+ return get_global_transform().get_scale();
+}
+
+void Node2D::set_global_scale(const Size2& p_scale) {
+
+ CanvasItem *pi = get_parent_item();
+ if (pi) {
+ const Size2 parent_global_scale = pi->get_global_transform().get_scale();
+ set_scale(p_scale - parent_global_scale);
+ } else {
+ set_scale(p_scale);
+ }
+
+}
+
+
void Node2D::set_transform(const Matrix32& p_transform) {
_mat=p_transform;
@@ -398,6 +445,12 @@ void Node2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_global_pos","pos"),&Node2D::set_global_pos);
ObjectTypeDB::bind_method(_MD("get_global_pos"),&Node2D::get_global_pos);
+ ObjectTypeDB::bind_method(_MD("set_global_rot","radians"),&Node2D::set_global_rot);
+ ObjectTypeDB::bind_method(_MD("get_global_rot"),&Node2D::get_global_rot);
+ ObjectTypeDB::bind_method(_MD("set_global_rotd","degrees"),&Node2D::set_global_rotd);
+ ObjectTypeDB::bind_method(_MD("get_global_rotd"),&Node2D::get_global_rotd);
+ ObjectTypeDB::bind_method(_MD("set_global_scale","scale"),&Node2D::set_global_scale);
+ ObjectTypeDB::bind_method(_MD("get_global_scale"),&Node2D::get_global_scale);
ObjectTypeDB::bind_method(_MD("set_transform","xform"),&Node2D::set_transform);
ObjectTypeDB::bind_method(_MD("set_global_transform","xform"),&Node2D::set_global_transform);
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 7bfe38d6a73..b31ee08af68 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -87,11 +87,17 @@ public:
Size2 get_scale() const;
Point2 get_global_pos() const;
+ float get_global_rot() const;
+ float get_global_rotd() const;
+ Size2 get_global_scale() const;
virtual Rect2 get_item_rect() const;
void set_transform(const Matrix32& p_transform);
void set_global_transform(const Matrix32& p_transform);
void set_global_pos(const Point2& p_pos);
+ void set_global_rot(float p_radians);
+ void set_global_rotd(float p_degrees);
+ void set_global_scale(const Size2& p_scale);
void set_z(int p_z);
int get_z() const;