80 lines
2.0 KiB
GDScript
80 lines
2.0 KiB
GDScript
extends Object
|
|
class_name WeightedChoice
|
|
## This class provide a static [method pick] method you can call to
|
|
## choose an item from a dictionary based on weights.
|
|
## [br]
|
|
## Example:
|
|
## [codeblock]
|
|
## # Define weights for ease of use
|
|
## const Rarity = {
|
|
## "COMMON":0.85,
|
|
## "UNCOMMON": 0.10,
|
|
## "RARE": 0.05
|
|
## }
|
|
##
|
|
## var items = {
|
|
## "copper": {"name": "Copper item", "rarity": Rarity.COMMON},
|
|
## "silver": {"name": "Silver item", "rarity": Rarity.UNCOMMON},
|
|
## "gold": {"name": "Gold item", "rarity": Rarity.RARE}
|
|
## }
|
|
##
|
|
## var key = WeightedChoice.pick(items, "rarity") # "copper", "silver", or "gold"
|
|
## [/codeblock]
|
|
|
|
|
|
## Takes a dictionary, and picks one item based based on weights.
|
|
## [br]
|
|
## If [param weight_key] is set, the function will search for weights in
|
|
## a nested dictionary
|
|
## [br][br]
|
|
## Returns a key.
|
|
## [br][br]
|
|
## [b]Usage[/b]:
|
|
## [br][br]
|
|
## [i]Non-nested weights:[/i]
|
|
## [codeblock]
|
|
## var dict = {
|
|
## "A": 0.85,
|
|
## "B": 0.10,
|
|
## "C": 0.05
|
|
## }
|
|
##
|
|
## var key = WeightedChoice.pick(dict) # "A", "B", or "C"
|
|
## [/codeblock]
|
|
##
|
|
## [i]Nested weights[/i]
|
|
## [codeblock]
|
|
## var items = {
|
|
## "copper": {"name": "Copper item", "weight": 0.85},
|
|
## "silver": {"name": "Silver item", "weight": 0.10},
|
|
## "gold": {"name": "Gold item", "weight": 0.05}
|
|
## }
|
|
##
|
|
## var key = WeightedChoice.pick(items, "weight") # "copper", "silver", or "gold"
|
|
## [/codeblock]
|
|
static func pick(dict: Dictionary, weight_key = null) -> Variant:
|
|
|
|
var weights_sum := 0.0
|
|
var keys = dict.keys()
|
|
# only relevant for non-nested weights
|
|
var weights = null
|
|
# weights are in a innested dictionary
|
|
if weight_key:
|
|
for item_key in dict:
|
|
weights_sum += dict[item_key][weight_key]
|
|
else:
|
|
weights = dict.values()
|
|
for weight in weights:
|
|
weights_sum += weight
|
|
|
|
var remaining_distance := randf() * weights_sum
|
|
for i in dict.size():
|
|
if weight_key:
|
|
remaining_distance -= dict[keys[i]][weight_key]
|
|
else:
|
|
remaining_distance -= weights[i]
|
|
if remaining_distance < 0:
|
|
return keys[i]
|
|
|
|
return keys[0]
|