Department orders credit reward and cooldown time now use a logarithmic scale (#88797)

## About The Pull Request

This PR makes the reward and cooldown for departmental orders scale with
crate cost using a logarithmic scaling, instead of comparing the price
to preset thresholds for time, or awarding the same amount as the
crate's cost.

Previously, to calculate the cooldown time, the code was calculated via
the following manner:

```
credits = clamp(credits, min, max)
time_y = 10 MINUTES * ((credits - min)/(max - min) + 1) 
```
Minimum was 320 credits, max was 3000, thus, all crates slid around
between 10 minutes to 20 minutes.

The reward for delivering the crates was the same as the crate's value.
This meant ordering egregiously expensive crates, far beyond 3000 was
way too desirable.

This PR changes both to use logarithmic scaling.

Cooldown time uses `60* log(price)^2.2`, and reward uses `140 *
log(price)^1.4`.

**Cooldown analysis**
At 320 it's 7.54 minutes, at 1400 it's 12.44 minutes, at 3000 (around
gun crates) it's 15.5 minutes, at 8000 (hat crate) 20 minutes, at 9000
(expensive atmos cans) it's 20.58 minutes, and at the 20k crate it's
24.76 minutes.

**Crate rewards analysis**
At 320 it's 475 credits, at 1400 it's 669 credits, at 3000 (around gun
crates) its 778, at 8000 (hat crate) it's 925 credits, at 9000
(expensive atmos cans) it's 943 credits, and at the 20k crate it's 1070
credits.

Up to 540 credits, you are actually getting a higher reward than what
the crate costs, but this is okay, as its a reward for delivering
simpler orders. A little surplus for you.

For the console UI, I have made items costing 3000 or more display
Moderate, and items costing 8000 or more, Long cooldowns.

## Why It's Good For The Game

Ordering really expensive crates should be a luxury, not a way to
generate money. The money is supposed to be a bonus, in addition to the
free crate to sell. Using a logarithmic scale, the credit bonus is
reigned in more evenly, making it more predictable for economy tweaking,
and avoids players double dipping credits in their free purchase.

Decreasing the rewards also give space for other new ways to generate
rewards in relation to departmental orders, such as stamping the
manifest with the correct head's stamp being worth more money and such.

<details>
  <summary>Old PR Text, which was using a price cap instead</summary>

## About The Pull Request

Departmental orders are a neat feature, but some of the available packs
had problems economywise. The cooldown of range from 10 to 20 minutes,
with 10 minutes being the base for anything costing less than 320
credits, and 20 minutes at 3000 credits. I have no problem with the
lower cap, but the upper cap has issues, as recently, a 20k crate was
added to cargo, which means it is possible to dump quite a large amount
of funds into cargo every 20 minutes.

Departmental orders probably need a bigger overhaul, and this solution
is imperfect, but I have talked with @ArcaneMusic about this as an
interim stop gap measure.

This PR also autodocs a proc, and moves some values to global defines,
for ease of balancing.

This PR affects the following crates, with the uncapped crate values in
brackets.

Armoury

- Combat Shotguns (3500 credits)
- Energy Guns (3600 credits)
- NT BR-38 Crate (20,000 credits)

Engineering

- BSA parts (6000 credits)
- DNA Vault Parts (4800 credits)

Engine Construction
- HFR Crate (4800)
- Supermatter Shard Crate (4000)

Materials

- BZ Crate (9000 credits)
- Nitrous Oxide (9000 credits)
- Water Vapor Crate (3010 credits)

Toys
- Collectible Hats Crate (8000 credits)

## Why It's Good For The Game

Instantly awarding 20k to cargo every 20 minute, in addition to 27k from
the other consoles (if both engineering and science orders BZ, service
orders collectible hats, and medical orders something around 1000), is a
bit too much. The money gained should be along a much more predictable
and expected value. With this chance, the most they can get is 13k every
20 minutes across all departments.
</details>

## Changelog

🆑
balance: Rewards from departmental orders use a logarithmic scale,
resulting in less rewards for high tier crates. The cooldown time is
also logarithmic now, which has slightly decreased cooldown values on
cheaper crates.
/🆑
This commit is contained in:
Profakos
2025-01-15 14:21:24 +01:00
committed by GitHub
parent fe4d384c32
commit 20d0d8827e
4 changed files with 32 additions and 15 deletions

View File

@@ -47,3 +47,17 @@
#define EXPORT_SOLD 1
///Sell the item, but for the love of god, don't delete it, we're handling it in a fancier way.
#define EXPORT_SOLD_DONT_DELETE 2
//At 320 it's 7.5 minutes, at 1400 it's 12.44 minutes, at 3000 (around gun crates) it's 15.5 minutes, at 8000 (hat crate) 20 minutes, at 9000 (expensive atmos cans) it's 20.58 minutes, and at the 20k crate it's 24.76 minutes.
/// Multiplies the logarithmic value calculating the free crate cooldown
#define DEPARTMENTAL_ORDER_COOLDOWN_COEFFICIENT 60
/// Used for the power of the logarithmic value for the free crate cooldown
#define DEPARTMENTAL_ORDER_COOLDOWN_EXPONENT 2.2
//At 320 it's 475 credits, at 1400 it's 669 credits, at 3000 (around gun crates) its 778, at 8000 (hat crate) it's 925 credits, at 9000 (expensive atmos cans) it's 943 credits, and at the 20k crate it's 1070 credits.
/// Multiplies the logarithmic value calculating the free crate delivery reward
#define DEPARTMENTAL_ORDER_REWARD_COEFFICIENT 120
/// Used for the power of the logarithmic value for the free crate delivery reward
#define DEPARTMENTAL_ORDER_REWARD_EXPONENT 1.5

View File

@@ -90,7 +90,8 @@
if(area_check(target))
//noice, delivered!
var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo_account.adjust_money(payment)
cargo_account.adjust_money(DEPARTMENTAL_ORDER_REWARD_COEFFICIENT * (log(10, payment) ** DEPARTMENTAL_ORDER_REWARD_EXPONENT))
remove_lock(target)
///called to remove the element in a flavorful way, either from delivery or from emagging/breaking open the crate

View File

@@ -194,6 +194,7 @@
submit_order(orderer, params["id"])
return TRUE
/// Submits the order with the specified supply pack id as the specified orderer
/datum/computer_file/program/department_order/proc/submit_order(mob/living/orderer, id)
id = text2path(id) || id
@@ -243,21 +244,16 @@
computer.physical.say("Order processed. Cargo will deliver the crate when it comes in on their shuttle. NOTICE: Heads of staff may override the order.")
calculate_cooldown(pack.cost)
///signal when the supply shuttle begins to spawn orders. we forget the current order preventing it from being overridden (since it's already past the point of no return on undoing the order)
/// Signal when the supply shuttle begins to spawn orders. We forget the current order preventing it from being overridden (since it's already past the point of no return on undoing the order)
/datum/computer_file/program/department_order/proc/finalize_department_order(datum/subsystem)
SIGNAL_HANDLER
if(!isnull(department_order) && (department_order in SSshuttle.shopping_list))
department_order = null
UnregisterSignal(subsystem, COMSIG_SUPPLY_SHUTTLE_BUY)
/// Calculates the cooldown it will take for this department's free order, based on its credit cost
/datum/computer_file/program/department_order/proc/calculate_cooldown(credits)
//minimum almost the lowest value of a crate
var/min = CARGO_CRATE_VALUE * 1.6
//maximum fairly expensive crate at 3000
var/max = CARGO_CRATE_VALUE * 15
credits = clamp(credits, min, max)
var/time_y = (credits - min)/(max - min) + 1 //convert to between 1 and 2
time_y = 10 MINUTES * time_y
var/time_y = DEPARTMENTAL_ORDER_COOLDOWN_COEFFICIENT * (log(10, credits) ** DEPARTMENTAL_ORDER_COOLDOWN_EXPONENT) * (1 SECONDS)
department_cooldowns[linked_department] = world.time + time_y
/datum/computer_file/program/department_order/process_tick(seconds_per_tick)

View File

@@ -16,8 +16,12 @@ import { BooleanLike } from 'tgui-core/react';
import { useBackend } from '../backend';
import { NtosWindow } from '../layouts';
// 15x crate value
const COST_UPPER_BOUND = 3000;
// 3.5x crate value, 10 minutes
const COST_MODERATE_BOUND = 700;
// 13.5x crate value, 15 minutes
const COST_LONG_BOUND = 2700;
// 40x crate value, 20 minutes
const COST_VERY_LONG_BOUND = 8000;
type typePath = string;
@@ -44,12 +48,14 @@ type Info = {
const CooldownEstimate = (props) => {
const { cost } = props;
const cooldownColor =
(cost > COST_UPPER_BOUND * 0.75 && 'red') ||
(cost > COST_UPPER_BOUND * 0.25 && 'orange') ||
(cost >= COST_VERY_LONG_BOUND && 'red') ||
(cost >= COST_LONG_BOUND && 'orange') ||
(cost >= COST_MODERATE_BOUND && 'yellow') ||
'green';
const cooldownText =
(cost > COST_UPPER_BOUND * 0.75 && 'long') ||
(cost > COST_UPPER_BOUND * 0.25 && 'moderate') ||
(cost >= COST_VERY_LONG_BOUND && 'very long') ||
(cost >= COST_LONG_BOUND && 'long') ||
(cost >= COST_MODERATE_BOUND && 'moderate') ||
'short';
return (
<Box as="span" textColor={cooldownColor}>