Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Used pressure crystals: credit value scaling with explosion power #15933

Merged
40 changes: 40 additions & 0 deletions code/modules/economy/shippingmarket.dm
@@ -1,6 +1,13 @@
#define SUPPLY_OPEN_TIME 1 SECOND //Time it takes to open supply door in seconds.
#define SUPPLY_CLOSE_TIME 15 SECONDS //Time it takes to close supply door in seconds.

#define PRESSURE_CRYSTAL_VALUATION_FORMULA ** 1.1 * 400 /// The full explosion-power-to-credits conversion formula
kbsmilk marked this conversation as resolved.
Show resolved Hide resolved
#define PRESSURE_CRYSTAL_SALES_RANGE_LENGTH 5 /// The span size of each range for the pressure crystal sales list. Measured in explosion power
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 power either side is very hard to get right, it'd make more sense to scale it by proximity rather than have a sharp cutoff

#define PC_VALUE_DIMINISH_MULTIPLIER (rand(30, 60) / 100) // Every sale will multiply the current multiplier for its range by this value
#define PC_HIGH_VALUE_RANGE_QUANTITY 7 // How many ranges will be given credit multipliers in New()?
kbsmilk marked this conversation as resolved.
Show resolved Hide resolved
#define PC_BONUS_MULTIPLIER (rand(300, 600) / 100) // The multiplier to give to high value ranges. Bonus ranges are reset to 1x upon sale
#define PC_MAX_POWER_FOR_BONUS_GENERATION 230 // Bonuses won't be given to ranges above this explosion power value

//Codes for requisition post-transaction returns handling
///Requisition was not used, conduct a standard QM sale
#define RET_IGNORE 0
Expand Down Expand Up @@ -35,6 +42,8 @@
var/list/supply_requests = list() // Pending requests, of type /datum/supply_order
var/list/supply_history = list() // History of all approved requests, of type string

var/list/pressure_crystal_sales = list()

var/points_per_crate = 10

var/list/datum/req_contract/complete_orders = list()
Expand Down Expand Up @@ -92,6 +101,14 @@

src.launch_distance = get_dist(spawnpoint, target)

var/pcmi = PC_HIGH_VALUE_RANGE_QUANTITY
while (pcmi > 0) // Set up the high value pressure crystal targets
var/index = "[rand(1, ceil(PC_MAX_POWER_FOR_BONUS_GENERATION / PRESSURE_CRYSTAL_SALES_RANGE_LENGTH))]"
if (src.pressure_crystal_sales[index])
continue
src.pressure_crystal_sales[index] = PC_BONUS_MULTIPLIER
pcmi--

proc/add_commodity(var/datum/commodity/new_c)
src.commodities["[new_c.comtype]"] = new_c

Expand Down Expand Up @@ -366,6 +383,11 @@
if (sell)
qdel(O)
break
else if (istype(O, /obj/item/pressure_crystal))
duckets += src.appraise_pressure_crystal(O, sell)
if (sell)
qdel(O)
break
else if (O.artifact && sell)
src.sell_artifact(O, O.artifact)
else // Please excuse this duplicate code, I'm gonna change trader commodity lists into associative ones later I swear
Expand All @@ -392,6 +414,20 @@

return duckets

proc/appraise_pressure_crystal(var/obj/item/pressure_crystal/pc, var/sell = 0)
if (pc.pressure <= 0)
return
var/index = "[ceil(pc.pressure / PRESSURE_CRYSTAL_SALES_RANGE_LENGTH)]"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure what this index system is supposed to be doing, but it's it doesn't seem like it's going to prevent slightly diluting the mix a few times to get different enough values to trick the system. I suggest making it gradually scale by proximity instead, so a lot of similar values will all dilute any more sales in the approximate area.

Shitty MSPaint drawing to illustrate:
image

Copy link
Contributor Author

@kbsmilk kbsmilk Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The index takes a measured pressure and converts it into an integer, which represents a range. e.g. pressure of 27.4 gets fit into the range of 25-30. Then if it gets sold, the index of the 25-30 range gets an entry in the sales list as a key, with its value as a multiplier (rand 0.3 to 0.6)
After that, credit value for pressures in that range receives that multiplier. Which stacks multiplicatively with further sales.

It's not perfect: has a hard cutoff, like you say. But I don't have the knowledge to make the system you're describing.

edit: I should mention, this does have an upside! Concrete ranges w/ values that are visible to players is pretty user-friendly.

var/value = pc.pressure PRESSURE_CRYSTAL_VALUATION_FORMULA
if (src.pressure_crystal_sales[index])
value *= src.pressure_crystal_sales[index]
if (sell)
if (src.pressure_crystal_sales[index])
src.pressure_crystal_sales[index] = clamp(src.pressure_crystal_sales[index], 0.01, 1) * PC_VALUE_DIMINISH_MULTIPLIER
else
src.pressure_crystal_sales[index] = PC_VALUE_DIMINISH_MULTIPLIER
return value

proc/handle_returns(obj/storage/crate/sold_crate,var/return_code)
if(return_code == RET_INSUFFICIENT) //clarifies purpose for crate return
sold_crate.name = "Returned Requisitions Crate"
Expand Down Expand Up @@ -645,3 +681,7 @@

#undef SUPPLY_OPEN_TIME
#undef SUPPLY_CLOSE_TIME
#undef PC_HIGH_VALUE_RANGE_QUANTITY
#undef PC_BONUS_MULTIPLIER
#undef PC_MAX_POWER_FOR_BONUS_GENERATION
#undef PC_VALUE_DIMINISH_MULTIPLIER
9 changes: 9 additions & 0 deletions code/modules/economy/supply_packs.dm
Expand Up @@ -1436,6 +1436,15 @@ ABSTRACT_TYPE(/datum/supply_packs)
containername = "AI Law Rack ManuDrive Crate (Cardlocked \[Heads])"
access = access_heads

/datum/supply_packs/pressure_crystals_qt5
name = "Pressure Crystal Resupply"
desc = "Five (5) pressure crystals used in high-energy research."
category = "Research Department"
contains = list(/obj/item/pressure_crystal = 5)
cost = 2500
containertype = /obj/storage/crate
containername = "Pressure Crystal Crate"

/* ================================================= */
/* -------------------- Complex -------------------- */
/* ================================================= */
Expand Down
1 change: 1 addition & 0 deletions code/obj/item/device/pda2/cartridges.dm
Expand Up @@ -237,6 +237,7 @@ TYPEINFO(/obj/item/disk/data/cartridge/syndicate)
..()
src.root.add_file( new /datum/computer/file/pda_program/scan/reagent_scan(src))
src.root.add_file( new /datum/computer/file/pda_program/signaler(src))
src.root.add_file( new /datum/computer/file/pda_program/pressure_crystal_shopper(src))
src.read_only = 1

bartender
Expand Down
25 changes: 25 additions & 0 deletions code/obj/item/device/pda2/smallprogs.dm
Expand Up @@ -1676,3 +1676,28 @@ Using electronic "Detomatix" SELF-DESTRUCT program is perhaps less simple!<br>
if(product.desc)
. += product.desc
. += "<br>"

/datum/computer/file/pda_program/pressure_crystal_shopper
name = "Crystal Bazaar"
size = 2

return_text()
if(..())
return

. = src.return_text_header()

. += "<h4>The Pressure Crystal Market</h4> \
Pressure crystals! Researchers everywhere love them. But selling a crystal reduces demand. Here you can see what \
calibers of crystal are more or less valued than usual.<br><br>\
A few well-funded organizations have offered superior rates for crystals within certain measurement ranges. \
<br><br>"
for (var/i in shippingmarket.pressure_crystal_sales)
var/index = text2num(i)
var/range_min = (index - 1) * PRESSURE_CRYSTAL_SALES_RANGE_LENGTH
var/range_max = index * PRESSURE_CRYSTAL_SALES_RANGE_LENGTH
var/mult = shippingmarket.pressure_crystal_sales[i]
. += "[range_min] to [range_max] kiloblast: \
[mult > 1 ? "<B>" : ""]worth [round(mult * 100, 0.01)]% of normal. \
[mult > 1 ? "Maximum estimated value: [mult * range_max PRESSURE_CRYSTAL_VALUATION_FORMULA]</B> credits." : ""]<br>"
. += "<br>"
51 changes: 36 additions & 15 deletions code/obj/item/device/transfer_valve.dm
Expand Up @@ -509,17 +509,41 @@ TYPEINFO(/obj/item/device/transfer_valve/briefcase)
/obj/item/pressure_crystal
icon = 'icons/obj/items/assemblies.dmi'
icon_state = "pressure_3"
var/pressure = 0
var/total_pressure = 0
desc = "A pressure crystal. We're not really sure how it works, but it does. Place this near where the epicenter of a bomb would be, then detonate the bomb. Afterwards, place the crystal in a tester to determine the strength."
name = "Pressure Crystal"
w_class = W_CLASS_SMALL
var/pressure = 0 // used to calculate credit value, in shippingmarket.dm proc/appraise_value
var/last_explode_time = 0
var/static/explosion_id = 0
name = "pressure crystal"
desc = "A mysterious gadget that measures the power of bombs detonated over it. \
High measurements within the crystal can be very valuable on the shipping market."
HELP_MESSAGE_OVERRIDE("Place this where the epicenter of a bomb would be, then detonate the bomb. \
Afterwards, place the crystal in a pressure sensor to determine the explosion power.<br>\
Spent pressure crystals can be sold to researchers on the shipping market, for a credit sum depending on the measured power.")

examine()
. = ..()
if (src.pressure)
. += "<br><span class='notice'>This crystal has already measured something. Another explosion will overwrite the previous results.</span>"

ex_act(var/ex, var/inf, var/factor)
pressure = factor || (4-clamp(ex, 1, 3))*2
total_pressure += pressure
pressure += (rand()-0.5) * (pressure/1000)//its not extremely accurate.
icon_state = "pressure_[clamp(ex, 1, 3)]"
var/exp_power = (factor / 2) ** 2 || (4-clamp(ex, 1, 3))*2 // we made it extremely accurate

if (src.explosion_id == exp_power * world.time)
return // we don't want peeps stacking 50 crystals on 1 explosion
// this only stops stacking, or making rings of crystals - you can still make a line of valuable crystals from the epicenter

if (src.last_explode_time < world.time)
src.pressure = exp_power
else // sum the power of multiple explosions at roughly the same instant, but diminishingly
// preferring stronger explosions, too
src.pressure = max(src.pressure, exp_power) + sqrt(min(src.pressure, exp_power))

src.icon_state = "pressure_[clamp(ex, 1, 3)]"
src.last_explode_time = world.time
src.explosion_id = exp_power * world.time

/obj/item/device/pressure_sensor
name = "Pressure Sensor"
name = "pressure sensor"
icon = 'icons/obj/items/assemblies.dmi'
icon_state = "pressure_tester"
desc = "Put in a pressure crystal to determine the strength of the explosion."
Expand All @@ -529,9 +553,10 @@ TYPEINFO(/obj/item/device/transfer_valve/briefcase)
boutput( user, "<b>There's no crystal in this here device!</b>")
else
if(crystal.pressure)
boutput( user, "The reader reads <b>[crystal.pressure/25]</b> kilojoules." )
boutput( user, "The reader reads <b>[crystal.pressure]</b> kiloblast." )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a kiloblast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nonexistent unit (just like joules in _units.dm) :)
Anyway I looked into explosion power unit -> Joule conversion, but it is not worth it. So kiloblast is just 1:1 with an explosion's power value instead

else
boutput( user, "The reader reads a firm 0. It guilts you into trying to read an unexploded pressure crystal, and seems to have succeeded. You feel ashamed for being so compelled by a device that has nothing more than a slot and a number display.")
boutput( user, "The reader reads a firm 0. It guilts you into trying to read an unexploded pressure crystal, and seems to have \
succeeded. You feel ashamed for being so compelled by a device that has nothing more than a slot and a number display.")
Comment on lines +556 to +559
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know these didn't have them before but they probably should have style classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

planning to atomize the bulk of the pressure sensor stuff into another pr

ex_act()
qdel(src)
attackby(obj/item/thing, mob/user)
Expand All @@ -552,10 +577,6 @@ TYPEINFO(/obj/item/device/transfer_valve/briefcase)
overlays = list()
wear_image.overlays = list()
boutput( user, "You pry out the crystal." )
if(prob(src.crystal.total_pressure / 45))
boutput( user, "<b class='alert'>It shatters!</b>" )
qdel(src.crystal)
return
src.crystal.set_loc(user.loc)
src.crystal = null
return
Expand Down