diff --git a/CHANGELOG.md b/CHANGELOG.md index 44dcd741..130ad2fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,13 @@ - New `Rand` singleton that allows generating completely random values from anywhere. `Rand` auto randomizes every time the engine starts and since it extends `RandomNumberGenerator` you can also call `randomize()` or `set_seed()` manually. * `shuffle(Array)` shuffles an Array * `choice(Variant)` picks a random value from an Array or Dictionary or random character from a string + * `choices(Array, count, weights)` picks count number of random values from Array based on weights + * `color()` generates a completely random color * `decision(double)` helps generates a random decisions based on a probability from `0.0` to `1.0` (0% to 100%) * `roll(count,side)` simulates a random dice roll using count and side and returns an Dictionary with sum and rolls. * `roll_notation(dice_notation`) similar to a roll ecept it uses dice notation such as `2d6`, `2x(3d6!U)`. Als returns a Dictionary but contains additional information. * `i(from, to)` same as `rand_range` but shorter and slightly faster. * `f(from, to)` same as `randf_range` but shorter. - * `color()` generates a completely random color * `uuid_v4()` generated a random UUID hex bytes 8-4-4-4-12 using version 4 of the spec(also known a GUID). - Added autotile auto-transforms pr found [here](https://github.com/godotengine/godot/pull/39046) to Goblin. The proposal is [here](https://github.com/godotengine/godot-proposals/issues/893). The idea here is to allow specific transforms on autotiles so that when looking up a specific bitmask the autotile is esentially transformed dynamically, based on allowed transformations. Allows for less manual tile work in some situations and smaller texture file. The drawback is the tiles resulting from transforms are repetitive. - Added `eval("expression")` function in `@GDScript` which parses a string expression and outputs the result or null if couldn't parse. It does not take inputs like Expression but can be added since it actually uses Expression class in the backend. This is a common function in many interpeted languages. diff --git a/README.md b/README.md index 1b24d3fc..a6a30e90 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,11 @@ Goblin will not provide any Mono builds since the goal is to keep the engine lea ## Community and contributing There is no community as of yet but PRs welcome. There are a number of features that I am still looking to add: -- Make the engine leander and faster optimizing where necessary -- Add custom GDScript functionality +- Optimize engine size and speed +- More efficient implementations of custom code +- Custom GDScript functionality - Porting to more platforms +- Implementing features that are beyond my understanding ## Documentation and demos diff --git a/modules/goblin/doc_classes/Rand.xml b/modules/goblin/doc_classes/Rand.xml index b0fddaf1..21fc28ca 100644 --- a/modules/goblin/doc_classes/Rand.xml +++ b/modules/goblin/doc_classes/Rand.xml @@ -41,6 +41,21 @@ Returns a random element from a container or indexable sequence, such as [Array], [Dictionary], [String]. If container is empty, prints an error and returns [code]null[/code]. + + + + + + + + Returns random elements from an [Array] based on count and weights. This is somewhat similar to [method choice] but it allows you to specify weights for each element. Weights must be an [Array] of positive integers and of the same size as the original [Array]. If no weights are specified, all elements are equally likely. + + + + + + Returns a random [Color]. + diff --git a/modules/goblin/rand.cpp b/modules/goblin/rand.cpp index e0f505b0..a255651f 100644 --- a/modules/goblin/rand.cpp +++ b/modules/goblin/rand.cpp @@ -63,31 +63,43 @@ Variant Rand::choices(const Variant &p_from, int count, const Array &p_weights) case Variant::POOL_VECTOR3_ARRAY: case Variant::POOL_COLOR_ARRAY: case Variant::ARRAY: { + ERR_FAIL_COND_V_MSG(count < 1, Array(), "Count must be positive"); + Array arr = p_from; ERR_FAIL_COND_V_MSG(arr.empty(), Array(), "Array is empty."); - ERR_FAIL_COND_V_MSG(arr.size() != p_weights.size(), Array(), "Array and weights unequal size."); - - int weights_sum = 0; - for (int i = 0; i < p_weights.size(); i++) { - if (p_weights.get(i).get_type() == Variant::INT) { - weights_sum += (int)p_weights.get(i); - } else { - ERR_FAIL_V_MSG(Array(), "Weights are not integers."); - } + + if (p_weights.size() > 0) { + ERR_FAIL_COND_V_MSG(arr.size() != p_weights.size(), Array(), "Array and weights unequal size."); } Array choices = Array(); - while(choices.size() < count) { - float remaining_distance = randf() * weights_sum; + if (p_weights.empty()) { //no weights array then everything is weighted randomly + for (int j = 0; j < count; j++) { + choices.append(choice(p_from)); + } + } else { //with weights + //calculate weights sum + int weights_sum = 0; for (int i = 0; i < p_weights.size(); i++) { - remaining_distance -= (int)p_weights.get(i); - if (remaining_distance < 0) { - choices.append(p_from.get(i)); - break; + if (p_weights.get(i).get_type() == Variant::INT && (int)p_weights.get(i) >= 0) { + weights_sum += (int)p_weights.get(i); + } else { + ERR_FAIL_V_MSG(Array(), "Weights must be positive integers."); } } - } + //loop through based on weights and add until you reach count + while(choices.size() < count) { + float remaining_distance = randf() * weights_sum; + for (int i = 0; i < p_weights.size(); i++) { + remaining_distance -= (int)p_weights.get(i); + if (remaining_distance < 0) { + choices.append(arr.get(i)); + break; + } + } + } + } return choices; } break; default: { @@ -302,7 +314,7 @@ void Rand::_bind_methods() { ClassDB::bind_method(D_METHOD("f", "from", "to"), &Rand::f); ClassDB::bind_method(D_METHOD("choice", "from"), &Rand::choice); - ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Rand::choices); + ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Rand::choices, DEFVAL(1), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("shuffle", "array"), &Rand::shuffle); ClassDB::bind_method(D_METHOD("decision", "probability"), &Rand::decision); ClassDB::bind_method(D_METHOD("roll", "count", "faces"), &Rand::roll);