Honor authored barrier health bands
This commit is contained in:
parent
41192e4ef5
commit
24b9570463
10 changed files with 73 additions and 91 deletions
|
|
@ -23,16 +23,11 @@ const MINNOW_ROLL_WEIGHTS: Array[float] = [34.0, 27.0, 20.0, 13.0, 6.0]
|
||||||
const SARDINE_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.0]
|
const SARDINE_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.0]
|
||||||
const LUMINOUS_ROE_ROLL_WEIGHTS: Array[float] = [16.0, 26.0, 26.0, 20.0, 12.0]
|
const LUMINOUS_ROE_ROLL_WEIGHTS: Array[float] = [16.0, 26.0, 26.0, 20.0, 12.0]
|
||||||
const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0]
|
const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0]
|
||||||
# Legacy profile multiplier retained for serialized/profile compatibility.
|
# Legacy multiplier helpers remain available for compatibility. New encounters
|
||||||
# New encounters use the weighted quality/rarity/weight bands below.
|
# roll directly within the authored quality bands below.
|
||||||
const BARRIER_HEALTH_MULTIPLIERS: Array[float] = [1.0, 1.25, 1.6, 2.2, 3.25]
|
const BARRIER_HEALTH_MULTIPLIERS: Array[float] = [1.0, 1.25, 1.6, 2.2, 3.25]
|
||||||
# Barrier health is weighted 70% by quality, 20% by rarity, and 10% by the
|
# Inclusive per-barrier health bands authored for each quality tier. Encounter
|
||||||
# catch's position in its authored weight range. The upper bounds define the
|
# generation rolls uniformly across the selected tier's complete band.
|
||||||
# intended per-barrier challenge bands; a small seeded variance keeps barriers
|
|
||||||
# from feeling identical without crossing those bands.
|
|
||||||
const BARRIER_QUALITY_WEIGHT: float = 0.70
|
|
||||||
const BARRIER_RARITY_WEIGHT: float = 0.20
|
|
||||||
const BARRIER_WEIGHT_WEIGHT: float = 0.10
|
|
||||||
const BARRIER_HEALTH_MINIMUMS: Array[int] = [1, 10, 50, 100, 200]
|
const BARRIER_HEALTH_MINIMUMS: Array[int] = [1, 10, 50, 100, 200]
|
||||||
const BARRIER_HEALTH_MAXIMUMS: Array[int] = [9, 50, 100, 200, 400]
|
const BARRIER_HEALTH_MAXIMUMS: Array[int] = [9, 50, 100, 200, 400]
|
||||||
const DISPLAY_NAMES: PackedStringArray = [
|
const DISPLAY_NAMES: PackedStringArray = [
|
||||||
|
|
@ -89,32 +84,26 @@ static func apply_barrier_health(base_health: int, quality: int) -> int:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
static func barrier_health_for_catch(
|
static func barrier_health_range(quality: int) -> Vector2i:
|
||||||
quality: int,
|
|
||||||
rarity: int,
|
|
||||||
weight_percentile: float,
|
|
||||||
variance: float = 1.0,
|
|
||||||
) -> int:
|
|
||||||
var safe_quality: int = (
|
var safe_quality: int = (
|
||||||
quality if is_valid(quality) else Tier.BORING
|
quality if is_valid(quality) else Tier.BORING
|
||||||
)
|
)
|
||||||
var safe_rarity: float = clampf(float(rarity) / 4.0, 0.0, 1.0)
|
return Vector2i(
|
||||||
var safe_weight: float = clampf(weight_percentile, 0.0, 1.0)
|
|
||||||
var weighted_health: float = float(BARRIER_HEALTH_MAXIMUMS[safe_quality]) * (
|
|
||||||
BARRIER_QUALITY_WEIGHT
|
|
||||||
+ BARRIER_RARITY_WEIGHT * safe_rarity
|
|
||||||
+ BARRIER_WEIGHT_WEIGHT * safe_weight
|
|
||||||
)
|
|
||||||
var varied_health: int = roundi(
|
|
||||||
weighted_health * clampf(variance, 0.8, 1.2)
|
|
||||||
)
|
|
||||||
return clampi(
|
|
||||||
varied_health,
|
|
||||||
BARRIER_HEALTH_MINIMUMS[safe_quality],
|
BARRIER_HEALTH_MINIMUMS[safe_quality],
|
||||||
BARRIER_HEALTH_MAXIMUMS[safe_quality],
|
BARRIER_HEALTH_MAXIMUMS[safe_quality],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
static func roll_barrier_health(
|
||||||
|
rng: RandomNumberGenerator,
|
||||||
|
quality: int,
|
||||||
|
) -> int:
|
||||||
|
var health_range: Vector2i = barrier_health_range(quality)
|
||||||
|
if rng == null:
|
||||||
|
return health_range.x
|
||||||
|
return rng.randi_range(health_range.x, health_range.y)
|
||||||
|
|
||||||
|
|
||||||
static func roll(
|
static func roll(
|
||||||
rng: RandomNumberGenerator,
|
rng: RandomNumberGenerator,
|
||||||
weight_multipliers: Array[float] = [],
|
weight_multipliers: Array[float] = [],
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
script = ExtResource("1_profile")
|
script = ExtResource("1_profile")
|
||||||
barrier_count_min = 3
|
barrier_count_min = 3
|
||||||
barrier_count_max = 4
|
barrier_count_max = 4
|
||||||
barrier_health_min = 4
|
|
||||||
barrier_health_max = 7
|
|
||||||
first_barrier_margin = 0.14
|
first_barrier_margin = 0.14
|
||||||
final_barrier_margin = 0.1
|
final_barrier_margin = 0.1
|
||||||
minimum_barrier_spacing = 0.14
|
minimum_barrier_spacing = 0.14
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
script = ExtResource("1_profile")
|
script = ExtResource("1_profile")
|
||||||
barrier_count_min = 3
|
barrier_count_min = 3
|
||||||
barrier_count_max = 4
|
barrier_count_max = 4
|
||||||
barrier_health_min = 5
|
|
||||||
barrier_health_max = 8
|
|
||||||
first_barrier_margin = 0.13
|
first_barrier_margin = 0.13
|
||||||
final_barrier_margin = 0.09
|
final_barrier_margin = 0.09
|
||||||
minimum_barrier_spacing = 0.14
|
minimum_barrier_spacing = 0.14
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,6 @@ var _reel_input_held: bool = false
|
||||||
var _reel_speed: float = 0.0
|
var _reel_speed: float = 0.0
|
||||||
var _click_power: int = 1
|
var _click_power: int = 1
|
||||||
var _fish_quality: int = FishQualityType.Tier.BORING
|
var _fish_quality: int = FishQualityType.Tier.BORING
|
||||||
var _fish_rarity: int = 0
|
|
||||||
var _fish_weight_percentile: float = 0.0
|
|
||||||
var _chase_delay_remaining: float = 0.0
|
var _chase_delay_remaining: float = 0.0
|
||||||
var _failure_epsilon: float = 0.0001
|
var _failure_epsilon: float = 0.0001
|
||||||
var _auto_click_accumulator: float = 0.0
|
var _auto_click_accumulator: float = 0.0
|
||||||
|
|
@ -113,8 +111,6 @@ func start_encounter(
|
||||||
reel_speed: float,
|
reel_speed: float,
|
||||||
click_power: int,
|
click_power: int,
|
||||||
fish_quality: int = FishQualityType.Tier.BORING,
|
fish_quality: int = FishQualityType.Tier.BORING,
|
||||||
fish_rarity: int = 0,
|
|
||||||
fish_weight_percentile: float = 0.0,
|
|
||||||
) -> void:
|
) -> void:
|
||||||
reset()
|
reset()
|
||||||
if profile == null:
|
if profile == null:
|
||||||
|
|
@ -128,8 +124,6 @@ func start_encounter(
|
||||||
if FishQualityType.is_valid(fish_quality)
|
if FishQualityType.is_valid(fish_quality)
|
||||||
else FishQualityType.Tier.BORING
|
else FishQualityType.Tier.BORING
|
||||||
)
|
)
|
||||||
_fish_rarity = clampi(fish_rarity, 0, 4)
|
|
||||||
_fish_weight_percentile = clampf(fish_weight_percentile, 0.0, 1.0)
|
|
||||||
_chase_delay_remaining = CHASE_START_DELAY
|
_chase_delay_remaining = CHASE_START_DELAY
|
||||||
chase_progress = -CHASE_START_OFFSET
|
chase_progress = -CHASE_START_OFFSET
|
||||||
_seed_encounter_rng()
|
_seed_encounter_rng()
|
||||||
|
|
@ -145,8 +139,6 @@ func start_authoritative_encounter(
|
||||||
click_power: int,
|
click_power: int,
|
||||||
encounter_seed_value: int,
|
encounter_seed_value: int,
|
||||||
fish_quality: int = FishQualityType.Tier.BORING,
|
fish_quality: int = FishQualityType.Tier.BORING,
|
||||||
fish_rarity: int = 0,
|
|
||||||
fish_weight_percentile: float = 0.0,
|
|
||||||
) -> void:
|
) -> void:
|
||||||
var previous_test_mode: bool = use_deterministic_test_seed
|
var previous_test_mode: bool = use_deterministic_test_seed
|
||||||
var previous_seed: int = deterministic_test_seed
|
var previous_seed: int = deterministic_test_seed
|
||||||
|
|
@ -157,8 +149,6 @@ func start_authoritative_encounter(
|
||||||
reel_speed,
|
reel_speed,
|
||||||
click_power,
|
click_power,
|
||||||
fish_quality,
|
fish_quality,
|
||||||
fish_rarity,
|
|
||||||
fish_weight_percentile,
|
|
||||||
)
|
)
|
||||||
use_deterministic_test_seed = previous_test_mode
|
use_deterministic_test_seed = previous_test_mode
|
||||||
deterministic_test_seed = previous_seed
|
deterministic_test_seed = previous_seed
|
||||||
|
|
@ -327,11 +317,8 @@ func _generate_barriers(profile: CatchDifficultyProfileType) -> void:
|
||||||
+ spacing * float(barrier_index)
|
+ spacing * float(barrier_index)
|
||||||
+ random_offsets[barrier_index] * random_slack
|
+ random_offsets[barrier_index] * random_slack
|
||||||
)
|
)
|
||||||
var health: int = FishQualityType.barrier_health_for_catch(
|
var health: int = FishQualityType.roll_barrier_health(
|
||||||
_fish_quality,
|
_rng, _fish_quality
|
||||||
_fish_rarity,
|
|
||||||
_fish_weight_percentile,
|
|
||||||
_rng.randf_range(0.9, 1.1),
|
|
||||||
)
|
)
|
||||||
_barriers.append(Barrier.new(position, health))
|
_barriers.append(Barrier.new(position, health))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ extends Resource
|
||||||
|
|
||||||
@export_range(0, 12, 1) var barrier_count_min: int = 2
|
@export_range(0, 12, 1) var barrier_count_min: int = 2
|
||||||
@export_range(0, 12, 1) var barrier_count_max: int = 4
|
@export_range(0, 12, 1) var barrier_count_max: int = 4
|
||||||
@export_range(1, 100, 1) var barrier_health_min: int = 3
|
|
||||||
@export_range(1, 100, 1) var barrier_health_max: int = 7
|
|
||||||
@export_range(0.0, 0.45, 0.01) var first_barrier_margin: float = 0.15
|
@export_range(0.0, 0.45, 0.01) var first_barrier_margin: float = 0.15
|
||||||
@export_range(0.0, 0.45, 0.01) var final_barrier_margin: float = 0.10
|
@export_range(0.0, 0.45, 0.01) var final_barrier_margin: float = 0.10
|
||||||
@export_range(0.01, 1.0, 0.01) var minimum_barrier_spacing: float = 0.15
|
@export_range(0.01, 1.0, 0.01) var minimum_barrier_spacing: float = 0.15
|
||||||
|
|
@ -15,11 +13,6 @@ func get_barrier_count_range() -> Vector2i:
|
||||||
return Vector2i(minimum, maxi(minimum, barrier_count_max))
|
return Vector2i(minimum, maxi(minimum, barrier_count_max))
|
||||||
|
|
||||||
|
|
||||||
func get_barrier_health_range() -> Vector2i:
|
|
||||||
var minimum: int = maxi(1, barrier_health_min)
|
|
||||||
return Vector2i(minimum, maxi(minimum, barrier_health_max))
|
|
||||||
|
|
||||||
|
|
||||||
func get_generation_interval() -> Vector2:
|
func get_generation_interval() -> Vector2:
|
||||||
var start: float = clampf(first_barrier_margin, 0.0, 0.95)
|
var start: float = clampf(first_barrier_margin, 0.0, 0.95)
|
||||||
var finish: float = clampf(1.0 - final_barrier_margin, start, 1.0)
|
var finish: float = clampf(1.0 - final_barrier_margin, start, 1.0)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
script = ExtResource("1_profile")
|
script = ExtResource("1_profile")
|
||||||
barrier_count_min = 2
|
barrier_count_min = 2
|
||||||
barrier_count_max = 4
|
barrier_count_max = 4
|
||||||
barrier_health_min = 3
|
|
||||||
barrier_health_max = 7
|
|
||||||
first_barrier_margin = 0.15
|
first_barrier_margin = 0.15
|
||||||
final_barrier_margin = 0.1
|
final_barrier_margin = 0.1
|
||||||
minimum_barrier_spacing = 0.15
|
minimum_barrier_spacing = 0.15
|
||||||
|
|
|
||||||
|
|
@ -1045,8 +1045,6 @@ func _activate_bite(confirmation_override: bool = false) -> void:
|
||||||
_get_effective_reel_speed(),
|
_get_effective_reel_speed(),
|
||||||
_get_effective_barrier_damage(),
|
_get_effective_barrier_damage(),
|
||||||
_pending_catch.quality,
|
_pending_catch.quality,
|
||||||
int(_pending_catch.fish.rarity),
|
|
||||||
_pending_catch.fish.get_weight_percentile(_pending_catch.weight_lb),
|
|
||||||
)
|
)
|
||||||
_catch_controller.set_reel_input(
|
_catch_controller.set_reel_input(
|
||||||
Input.is_action_pressed("fish_primary")
|
Input.is_action_pressed("fish_primary")
|
||||||
|
|
|
||||||
|
|
@ -548,8 +548,6 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void:
|
||||||
attempt.barrier_damage,
|
attempt.barrier_damage,
|
||||||
attempt.encounter_seed,
|
attempt.encounter_seed,
|
||||||
fish_catch.quality,
|
fish_catch.quality,
|
||||||
int(fish.rarity),
|
|
||||||
fish.get_weight_percentile(fish_catch.weight_lb),
|
|
||||||
)
|
)
|
||||||
var data: Dictionary = {
|
var data: Dictionary = {
|
||||||
"attempt_id": attempt.attempt_id,
|
"attempt_id": attempt.attempt_id,
|
||||||
|
|
|
||||||
|
|
@ -83,40 +83,68 @@ func _validate_barrier_challenge_curve() -> void:
|
||||||
var expected_maximums: Array[int] = [9, 50, 100, 200, 400]
|
var expected_maximums: Array[int] = [9, 50, 100, 200, 400]
|
||||||
var previous_health: int = 0
|
var previous_health: int = 0
|
||||||
for quality: int in FishQualityType.TIER_COUNT:
|
for quality: int in FishQualityType.TIER_COUNT:
|
||||||
|
var expected_range := Vector2i(
|
||||||
|
expected_minimums[quality], expected_maximums[quality]
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
FishQualityType.barrier_health_range(quality)
|
||||||
|
== expected_range
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
FishQualityType.roll_barrier_health(null, quality)
|
||||||
|
== expected_range.x
|
||||||
|
)
|
||||||
|
var rng := RandomNumberGenerator.new()
|
||||||
|
rng.seed = 73013 + quality
|
||||||
|
var observed_counts: Dictionary[int, int] = {}
|
||||||
|
var observed_minimum: int = expected_range.y
|
||||||
|
var observed_maximum: int = expected_range.x
|
||||||
|
var observed_total: int = 0
|
||||||
|
var band_size: int = expected_range.y - expected_range.x + 1
|
||||||
|
var sample_count: int = maxi(10000, band_size * 250)
|
||||||
|
for _sample: int in sample_count:
|
||||||
|
var rolled_health: int = FishQualityType.roll_barrier_health(
|
||||||
|
rng, quality
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
rolled_health >= expected_range.x
|
||||||
|
and rolled_health <= expected_range.y
|
||||||
|
)
|
||||||
|
observed_counts[rolled_health] = (
|
||||||
|
int(observed_counts.get(rolled_health, 0)) + 1
|
||||||
|
)
|
||||||
|
observed_minimum = mini(observed_minimum, rolled_health)
|
||||||
|
observed_maximum = maxi(observed_maximum, rolled_health)
|
||||||
|
observed_total += rolled_health
|
||||||
|
assert(observed_minimum == expected_range.x)
|
||||||
|
assert(observed_maximum == expected_range.y)
|
||||||
|
for authored_health: int in range(
|
||||||
|
expected_range.x, expected_range.y + 1
|
||||||
|
):
|
||||||
|
assert(observed_counts.has(authored_health))
|
||||||
|
var observed_average: float = (
|
||||||
|
float(observed_total) / float(sample_count)
|
||||||
|
)
|
||||||
|
var authored_midpoint: float = (
|
||||||
|
float(expected_range.x + expected_range.y) / 2.0
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
absf(observed_average - authored_midpoint)
|
||||||
|
< maxf(0.15, float(band_size) * 0.01)
|
||||||
|
)
|
||||||
var health: int = FishQualityType.apply_barrier_health(8, quality)
|
var health: int = FishQualityType.apply_barrier_health(8, quality)
|
||||||
assert(health > previous_health)
|
assert(health > previous_health)
|
||||||
previous_health = health
|
previous_health = health
|
||||||
assert(FishQualityType.apply_barrier_health(8, -1) == 8)
|
assert(FishQualityType.apply_barrier_health(8, -1) == 8)
|
||||||
assert(FishQualityType.apply_barrier_health(0, FishQualityType.Tier.SHINY) == 4)
|
assert(FishQualityType.apply_barrier_health(0, FishQualityType.Tier.SHINY) == 4)
|
||||||
var impressive_low_input: int = FishQualityType.barrier_health_for_catch(
|
|
||||||
FishQualityType.Tier.IMPRESSIVE,
|
|
||||||
0,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
)
|
|
||||||
var impressive_high_input: int = FishQualityType.barrier_health_for_catch(
|
|
||||||
FishQualityType.Tier.IMPRESSIVE,
|
|
||||||
4,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
)
|
|
||||||
assert(impressive_low_input >= 50 and impressive_low_input <= 100)
|
|
||||||
assert(impressive_high_input > impressive_low_input)
|
|
||||||
assert(impressive_high_input <= 100)
|
|
||||||
assert(
|
assert(
|
||||||
FishQualityType.barrier_health_for_catch(
|
FishQualityType.barrier_health_range(-1)
|
||||||
FishQualityType.Tier.BORING,
|
== Vector2i(1, 9)
|
||||||
4,
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
) <= 9
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var profile := CatchDifficultyProfile.new()
|
var profile := CatchDifficultyProfile.new()
|
||||||
profile.barrier_count_min = 1
|
profile.barrier_count_min = 1
|
||||||
profile.barrier_count_max = 1
|
profile.barrier_count_max = 1
|
||||||
profile.barrier_health_min = 8
|
|
||||||
profile.barrier_health_max = 8
|
|
||||||
profile.first_barrier_margin = 0.2
|
profile.first_barrier_margin = 0.2
|
||||||
profile.final_barrier_margin = 0.2
|
profile.final_barrier_margin = 0.2
|
||||||
profile.minimum_barrier_spacing = 0.1
|
profile.minimum_barrier_spacing = 0.1
|
||||||
|
|
@ -141,12 +169,9 @@ func _validate_barrier_challenge_curve() -> void:
|
||||||
assert(health <= expected_maximums[quality])
|
assert(health <= expected_maximums[quality])
|
||||||
controller.queue_free()
|
controller.queue_free()
|
||||||
|
|
||||||
var shiny_health: int = FishQualityType.barrier_health_for_catch(
|
var shiny_health: int = FishQualityType.barrier_health_range(
|
||||||
FishQualityType.Tier.SHINY,
|
FishQualityType.Tier.SHINY
|
||||||
4,
|
).y
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
)
|
|
||||||
var base_power_clicks: int = ceili(float(shiny_health) / 1.0)
|
var base_power_clicks: int = ceili(float(shiny_health) / 1.0)
|
||||||
var max_power_clicks: int = ceili(
|
var max_power_clicks: int = ceili(
|
||||||
float(shiny_health)
|
float(shiny_health)
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ func _run() -> void:
|
||||||
assert(not bobber.visible)
|
assert(not bobber.visible)
|
||||||
|
|
||||||
# A private host rolls and retains the authoritative catch before the fight
|
# A private host rolls and retains the authoritative catch before the fight
|
||||||
# so its quality can scale the same barriers clients receive in snapshots.
|
# so its quality selects the barrier band clients receive in snapshots.
|
||||||
fishing_spot.call("_begin_aiming", player)
|
fishing_spot.call("_begin_aiming", player)
|
||||||
fishing_spot.set("_cast_charge", 0.32)
|
fishing_spot.set("_cast_charge", 0.32)
|
||||||
fishing_spot.call("_update_cast_charge", 0.0)
|
fishing_spot.call("_update_cast_charge", 0.0)
|
||||||
|
|
@ -138,8 +138,6 @@ func _run() -> void:
|
||||||
attempt.barrier_damage,
|
attempt.barrier_damage,
|
||||||
attempt.encounter_seed,
|
attempt.encounter_seed,
|
||||||
fish_catch.quality,
|
fish_catch.quality,
|
||||||
int(fish.rarity),
|
|
||||||
fish.get_weight_percentile(fish_catch.weight_lb),
|
|
||||||
)
|
)
|
||||||
var quality_barriers: Array = attempt.controller.get("_barriers")
|
var quality_barriers: Array = attempt.controller.get("_barriers")
|
||||||
var reference_barriers: Array = reference_controller.get("_barriers")
|
var reference_barriers: Array = reference_controller.get("_barriers")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue