On version 0.2.1, there is an unintuitive and rather unexpected behavior on all the skills that take some inflation from one bodypart and then transfers it to other bodyparts or transforms it into other things. These skills that deflates from a part, and then inflates to another part or convert it into other things, doesnt take into account how much change is actually applied after clamping (mainly due to Rank base minimum, spirit-induced minimum or "Lactation Lock" status).
For example, the "Redirect Presure" Skill says that it "Takes half of the most inflated part and redirects it to the least inflated part".
- Expected behavior 1: takes half of the current inflation level from the most inflated part, from the minimum level to the current level, and redirects it to the least inflated part.
- Expected behavior 2: takes half of the current inflation level from the most inflated part, from 0 to the current level, takes that amount from the inflation level between minimum level and current level, and redirects it to the least inflated part.
- Actual behavior: tries to take half of the current inflation level from the most inflated part, from 0 to the current level, clamps the most inflated part to the minimum level, and adds the mistaken amount to the least inflated part, creating more unbalanced inflation than before.
This makes the skill unviable at any other Rank than "B Rank", while inhabited by Helium Spirits or under the "Lactation Lock" status (that is, with all minimums at 0 and no deflation-locking status). Other skills that deflate and inflate specific amounts of inflation also could lead to creating more inflation than before.
The Solution I came out with was to add a returning feature to the modify_inflation funtion, to make it return the total effective amount of inflation/deflation dealt (absolute value). And then, update the code in all these inflation-transfer/transform skills to take into account the effective inflated/deflated amount.
The following list show all the places affected by the minor bug, and the quick fixes I applied (all code line numbers based on original v0.2.1 unaltered files):
1.1) Take into account the modified amount in the "Air Control" Skill.
# 5 inflation from most-part to least-part.
[code]
File "game/init.rpy", lines 3537-3551, inside skill_use_air_control:
def skill_use_air_control():
amount_to_shift = 5
part_from = get_most_likely_to_pop_part()
if not part_from:
notify("Air Control: No parts have inflation to shift from.")
return False # <--
part_to = get_least_likely_to_pop_part(exclude_part=part_from) # <-- Get the part to inflate excluding the part to deflate.
if not part_to:
notify("Air Control: No parts have room to shift inflation to.")
return False # <--
initial_context = renpy.game.context().current
modified_amount = modify_inflation(part_from, -amount_to_shift) # <-- Get the effectively deflated amount.
if modified_amount == 0: # <-- When the most likely to pop cannot be deflated.
notify("Air Control: Cannot deflate part to shift from.") # <--
return False # <--
if renpy.game.context().current is initial_context:
modify_inflation(part_to, modified_amount) # <-- Inflates the effectively deflated amount.
notify(f"Air Control: Shifted {modified_amount} inflation from {part_from} to {part_to}.")# <--
return True # <--
[/code]
1.2) Take into account the modified amount in the "Pressure Shift" Skill.
# 10 inflation from most-part to least-part.
[code]
File "game/init.rpy", lines 3602-279, inside skill_use_pressure_shift:
def skill_use_pressure_shift():
amount_to_shift = 10
part_from = get_most_likely_to_pop_part()
part_to = get_least_likely_to_pop_part(exclude_part=part_from) # <-- Get the part to inflate excluding the part to deflate.
if not part_from or not part_to: # <--
notify("Cannot effectively shift pressure.")
return False # <--
initial_context = renpy.game.context().current
modified_amount = modify_inflation(part_from, -amount_to_shift) # <-- Get the effectively deflated amount.
if modified_amount == 0: # <-- When the most likely to pop cannot be deflated.
notify("Cannot effectively shift pressure.") # <--
return False # <--
if renpy.game.context().current is initial_context:
modify_inflation(part_to, modified_amount) # <-- Inflates the effectively deflated amount.
notify(f"Shifted {modified_amount} inflation from {part_from} to {part_to}.")# <--
return True # <--
[/code]
1.3) Update legacy code for most/least parts and take into account the modified amount in the "Redirect Pressure" Skill.
# halft inflation from most-part to least-part.
#For the Expected behavior 1, use "net_level" to calc the dynamic half (minimum to current level).
#For the Expected behavior 2, use "gross_level" to calc the total half (zero to current level).
[code]
File "game/init.rpy", lines 3511-3535, inside skill_use_redirect_pressure:
def skill_use_redirect_pressure():
part_from = get_most_likely_to_pop_part() # <--
if not part_from: # <--
notify("Cannot redirect pressure: No valid parts to shift from.") # <--
return False # <--
part_to = get_least_likely_to_pop_part(exclude_part=part_from) # <-- Get the part to inflate excluding the part to deflate.
if not part_to: # <--
no_part_to_message = "Cannot redirect pressure effectively: Target and source are the same." if get_least_likely_to_pop_part() is not None else "Cannot redirect pressure: No valid parts to shift to." # <-- If there is a part_to target when not using exclusion, then it is the excluded one
notify(no_part_to_message) # <--
return False # <--
gross_level = getattr(store, f"yuki_{part_from}_level") # <-- Total current level (including the Rank base minimum and/or the spirit-induced minimum)
net_level = gross_level - _clamp_level_minimum(0, part_from) # <-- Dynamic current level (excluding the Rank base minimum and/or the spirit-induced minimum)
amount_to_redirect = int(round(net_level / 2.0)) # <-- Choose espected behavior (total level or dynamic level)
if amount_to_redirect <= 0:
notify("Not enough inflation in the largest part to redirect.")
return False
initial_context = renpy.game.context().current
modified_amount = modify_inflation(part_from, -amount_to_redirect) # <-- Get the effectively deflated amount.
if modified_amount == 0: # <-- When the most likely to pop cannot be deflated.
notify("Cannot deflate the largest part to redirect.") # <--
return False # <--
if renpy.game.context().current is initial_context:
modify_inflation(part_to, modified_amount) # <-- Inflates the effectively deflated amount.
notify(f"Shifted {modified_amount} inflation from {part_from} to {part_to}.")# <--
return True
[/code]
1.4) Take into account the modified amount in the "Udderly Balanced" Skill.
# 15 deflation from chest, 7 inflation to belly and 8 inflation to butt.
[code]
File "game/init.rpy", lines 3472-3478, inside skill_use_udderly_balanced:
def skill_use_udderly_balanced():
initial_context = renpy.game.context().current
modified_amount = modify_inflation("chest", -15) # <-- Effective amount deflated from "chest"
if modified_amount == 0: return False # <-- Cannot deflate chest (maybe lactation lock is active)
if renpy.game.context().current is initial_context: # Check if pop occurred
modify_inflation("belly", (modified_amount // 2)) # <-- The floor half
if renpy.game.context().current is initial_context: # Check if pop occurred again
modify_inflation("butt", (modified_amount // 2) + (modified_amount % 2)) # <-- The tie breaking half (odd case)
return True # <--
[/code]
1.5) Take into account the modified amount in "Milk Conversion" Skill.
# 10 deflation from chest and +2 lactation stacks.
[code]
File "game/init.rpy", lines 3499-3509, inside skill_use_milk_conversion:
def skill_use_milk_conversion():
if store.yuki_chest_level >= 10:
initial_context = renpy.game.context().current
modified_amount = modify_inflation("chest", -10) # <-- Effective amount deflated from "chest"
if modified_amount != 10: # <-- Cannot deflate enough of chest
notify("Cannot convert 10 chest inflation to milk!") # <-- (maybe lactation lock is active)
return False # <--
if renpy.game.context().current is initial_context:
store.lactation_stacks += 2
notify(f"Converted 10 chest inflation into 2 Lactation Stacks! (Total: {store.lactation_stacks})")
return True
else:
notify("Not enough chest inflation to convert to milk!")
return False
[/code]
2) Refactor the return behavior of the modify_inflation Funtion, to return the absolute value of the total inflation levels modified.
[code]
File "game/init.rpy", lines 306-380, inside modify_inflation:
def modify_inflation(part, amount, _is_recursive_call=False):
if part not in ["chest", "belly", "butt"]:
renpy.log(f"Error: Invalid part '{part}' passed to modify_inflation.")
return 0 # <-- No inflation level was modified.
...
current_level = getattr(store, current_level_attr, 0)
new_level_unclamped = current_level + amount
current_total_modified_amount = 0 # <-- Modified amount on this call.
...
if part == "belly" and amount > 0 and yuki_has_item("equip_tight_rubber_belt"):
...
if new_level_unclamped > effective_belly_cap_with_belt:
excess_belly_inflation_to_redirect = new_level_unclamped - effective_belly_cap_with_belt
new_level_unclamped = effective_belly_cap_with_belt
current_total_modified_amount += abs(effective_belly_cap_with_belt - current_level) # <-- Modified on belly from current_level to effective_belly_cap_with_belt (clamped).
...
if amount > 0: # Inflation
shake_duration_scale = max(min_shake_scale, min(float(abs(amount)) / base_inflation_for_shake, max_shake_scale))
actual_change_this_part = new_level_unclamped - current_level
current_total_modified_amount += abs(actual_change_this_part) # <-- Modified on part from current_level to new_level_unclamped (unclamped).
...
elif amount < 0: # Deflation
if part == "chest" and has_status_effect("status_lactation_lock"):
if store.in_dungeon: notify("Lactation Lock prevents chest deflation!")
else:
new_level_clamped_deflate = _clamp_level_minimum(new_level_unclamped, part)
actual_decrease_this_part = current_level - new_level_clamped_deflate
current_total_modified_amount += abs(actual_decrease_this_part) # <-- Modified on part from current_level to new_level_clamped_deflate (clamped)
...
if excess_belly_inflation_to_redirect > 0 and redirection_target_part:
current_total_modified_amount += modify_inflation(redirection_target_part, excess_belly_inflation_to_redirect, _is_recursive_call=True) # <-- Modified on redirection_target_part recursively.
if not _is_recursive_call:
check_game_state_after_inflation()
return current_total_modified_amount # <-- Total absolute-value modified amount (including recursion)
[/code]
;)