diff --git a/core/variant/array.cpp b/core/variant/array.cpp index ab0315ae3456..f50f049223e6 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -298,6 +298,21 @@ Error Array::insert(int p_pos, const Variant &p_value) { ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state."); Variant value = p_value; ERR_FAIL_COND_V(!_p->typed.validate(value, "insert"), ERR_INVALID_PARAMETER); + + if (p_pos < 0) { + // Relative offset from the end. + p_pos = _p->array.size() + p_pos; + } + + ERR_FAIL_INDEX_V_MSG( + p_pos, + _p->array.size(), + ERR_INVALID_PARAMETER, + vformat( + "The calculated index %i is out of bounds (the array has %i elements). Leaving the array untouched.", + p_pos, + _p->array.size())); + return _p->array.insert(p_pos, value); } @@ -404,6 +419,20 @@ bool Array::has(const Variant &p_value) const { void Array::remove_at(int p_pos) { ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state."); + + if (p_pos < 0) { + // Relative offset from the end. + p_pos = _p->array.size() + p_pos; + } + + ERR_FAIL_INDEX_MSG( + p_pos, + _p->array.size(), + vformat( + "The calculated index %i is out of bounds (the array has %i elements). Leaving the array untouched.", + p_pos, + _p->array.size())); + _p->array.remove_at(p_pos); } diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index fccf16fcaa77..6df30bc9ee0b 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -383,7 +383,7 @@ - Inserts a new element at a given position in the array. The position must be valid, or at the end of the array ([code]pos == size()[/code]). Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed. + Inserts a new element at a given position in the array. The position must be valid, or at the end of the array ([code]pos == size()[/code]). If negative, [param position] is considered relative to the end of the array. Returns [constant OK] on success, or one of the other [enum Error] values if the operation failed. [b]Note:[/b] This method acts in-place and doesn't return a modified array. [b]Note:[/b] On large arrays, this method will be slower if the inserted element is close to the beginning of the array (index 0). This is because all elements placed after the newly inserted element have to be reindexed. @@ -533,10 +533,9 @@ - Removes an element from the array by index. If the index does not exist in the array, nothing happens. To remove an element by searching for its value, use [method erase] instead. + Removes an element from the array by index. If the index does not exist in the array, nothing happens. If negative, [param position] is considered relative to the end of the array. To remove an element by searching for its value, use [method erase] instead. [b]Note:[/b] This method acts in-place and doesn't return a modified array. [b]Note:[/b] On large arrays, this method will be slower if the removed element is close to the beginning of the array (index 0). This is because all elements placed after the removed element have to be reindexed. - [b]Note:[/b] [param position] cannot be negative. To remove an element relative to the end of the array, use [code]arr.remove_at(arr.size() - (i + 1))[/code]. To remove the last element from the array without returning the value, use [code]arr.resize(arr.size() - 1)[/code]. diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h index 228d77b3b508..3629f7c404d2 100644 --- a/tests/core/variant/test_array.h +++ b/tests/core/variant/test_array.h @@ -107,6 +107,12 @@ TEST_CASE("[Array] resize(), insert(), and erase()") { CHECK(int(arr[0]) == 2); arr.erase(2); CHECK(int(arr[0]) == 1); + + // Negative index on insert. + CHECK(arr.size() == 3); + arr.insert(-1, 3); + CHECK(int(arr[2]) == 3); + CHECK(arr.size() == 4); } TEST_CASE("[Array] front() and back()") { @@ -139,6 +145,15 @@ TEST_CASE("[Array] remove_at()") { arr.remove_at(0); CHECK(arr.size() == 0); + // Negative index. + arr.push_back(3); + arr.push_back(4); + arr.remove_at(-1); + CHECK(arr.size() == 1); + CHECK(int(arr[0]) == 3); + arr.remove_at(-1); + CHECK(arr.size() == 0); + // The array is now empty; try to use `remove_at()` again. // Normally, this prints an error message so we silence it. ERR_PRINT_OFF;