mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-14 20:23:26 +00:00
Largely ported from the work done at Baystation in https://github.com/Baystation12/Baystation12/pull/17460 and later commits. - Shuttles no longer require a separate area for each location they jump to. Instead destinations are indicated by landmark objects, which are not necessarily exclusive to that shuttle. This means that more than one shuttle could use the same docking port (not at the same time of course). - Enhanced shuttle control computers to use nanoui if they didn't. - Organizes shuttle datum code a bit better so there is less re-inventing the wheel in subtypes. - Allows the possibility of shuttles (or destinations) that start on late-loaded maps. - Deprecate the "extra" shuttle areas that are no longer needed and update shuttle areas in unit tests This all required a bit of infrastructure improvements. - ChangeArea proc, for changing the area of a turf. - Fixed lighting overlays actually being able to be destroyed. - Added a few utility macros and procs. - Added "turf translation" procs which are like move_contents_to but more flexible.
399 lines
14 KiB
Plaintext
399 lines
14 KiB
Plaintext
|
|
// TODO - Refactor to use the Supply Subsystem (SSsupply)
|
|
|
|
//Supply packs are in /code/datums/supplypacks
|
|
//Computers are in /code/game/machinery/computer/supply.dm
|
|
|
|
/datum/supply_order
|
|
var/ordernum // Unfabricatable index
|
|
var/index // Fabricatable index
|
|
var/datum/supply_pack/object = null
|
|
var/cost // Cost of the supply pack (Fabricatable) (Changes not reflected when purchasing supply packs, this is cosmetic only)
|
|
var/name // Name of the supply pack datum (Fabricatable)
|
|
var/ordered_by = null // Who requested the order
|
|
var/comment = null // What reason was given for the order
|
|
var/approved_by = null // Who approved the order
|
|
var/ordered_at // Date and time the order was requested at
|
|
var/approved_at // Date and time the order was approved at
|
|
var/status // [Requested, Accepted, Denied, Shipped]
|
|
|
|
/datum/exported_crate
|
|
var/name
|
|
var/value
|
|
var/list/contents
|
|
|
|
var/datum/controller/supply/supply_controller = new()
|
|
|
|
/datum/controller/supply
|
|
//supply points
|
|
var/points = 50
|
|
var/points_per_process = 1.5
|
|
var/points_per_slip = 2
|
|
var/points_per_money = 0.02 // 1 point for $50
|
|
//control
|
|
var/ordernum
|
|
var/list/shoppinglist = list() // Approved orders
|
|
var/list/supply_pack = list() // All supply packs
|
|
var/list/exported_crates = list() // Crates sent from the station
|
|
var/list/order_history = list() // History of orders, showing edits made by users
|
|
var/list/adm_order_history = list() // Complete history of all orders, for admin use
|
|
var/list/adm_export_history = list() // Complete history of all crates sent back on the shuttle, for admin use
|
|
//shuttle movement
|
|
var/movetime = 1200
|
|
var/datum/shuttle/autodock/ferry/supply/shuttle
|
|
var/list/material_points_conversion = list( // Any materials not named in this list are worth 0 points
|
|
"phoron" = 5,
|
|
"platinum" = 5
|
|
)
|
|
|
|
/datum/controller/supply/New()
|
|
ordernum = rand(1,9000)
|
|
|
|
for(var/typepath in subtypesof(/datum/supply_pack))
|
|
var/datum/supply_pack/P = new typepath()
|
|
if(P.name)
|
|
supply_pack[P.name] = P
|
|
else
|
|
qdel(P)
|
|
|
|
/datum/controller/process/supply/setup()
|
|
name = "supply controller"
|
|
schedule_interval = 300 // every 30 seconds
|
|
|
|
/datum/controller/process/supply/doWork()
|
|
supply_controller.process()
|
|
|
|
// Supply shuttle ticker - handles supply point regeneration
|
|
// This is called by the process scheduler every thirty seconds
|
|
/datum/controller/supply/process()
|
|
points += points_per_process
|
|
|
|
//To stop things being sent to CentCom which should not be sent to centcomm. Recursively checks for these types.
|
|
/datum/controller/supply/proc/forbidden_atoms_check(atom/A)
|
|
if(isliving(A))
|
|
return 1
|
|
if(istype(A,/obj/item/weapon/disk/nuclear))
|
|
return 1
|
|
if(istype(A,/obj/machinery/nuclearbomb))
|
|
return 1
|
|
if(istype(A,/obj/item/device/radio/beacon))
|
|
return 1
|
|
if(istype(A,/obj/item/device/perfect_tele_beacon)) //VOREStation Addition: Translocator beacons
|
|
return 1 //VOREStation Addition: Translocator beacons
|
|
|
|
for(var/atom/B in A.contents)
|
|
if(.(B))
|
|
return 1
|
|
|
|
//Selling
|
|
/datum/controller/supply/proc/sell()
|
|
// Loop over each area in the supply shuttle
|
|
for(var/area/subarea in shuttle.shuttle_area)
|
|
callHook("sell_shuttle", list(subarea));
|
|
for(var/atom/movable/MA in subarea)
|
|
if(MA.anchored)
|
|
continue
|
|
|
|
var/datum/exported_crate/EC = new /datum/exported_crate()
|
|
EC.name = "\proper[MA.name]"
|
|
EC.value = 0
|
|
EC.contents = list()
|
|
var/base_value = 0
|
|
|
|
// Must be in a crate!
|
|
if(istype(MA,/obj/structure/closet/crate))
|
|
var/obj/structure/closet/crate/CR = MA
|
|
callHook("sell_crate", list(CR, subarea))
|
|
|
|
points += CR.points_per_crate
|
|
if(CR.points_per_crate)
|
|
base_value = CR.points_per_crate
|
|
var/find_slip = 1
|
|
|
|
for(var/atom/A in CR)
|
|
EC.contents[++EC.contents.len] = list(
|
|
"object" = "\proper[A.name]",
|
|
"value" = 0,
|
|
"quantity" = 1
|
|
)
|
|
|
|
// Sell manifests
|
|
if(find_slip && istype(A,/obj/item/weapon/paper/manifest))
|
|
var/obj/item/weapon/paper/manifest/slip = A
|
|
if(!slip.is_copy && slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
|
|
points += points_per_slip
|
|
EC.contents[EC.contents.len]["value"] = points_per_slip
|
|
find_slip = 0
|
|
continue
|
|
|
|
// Sell phoron and platinum
|
|
if(istype(A, /obj/item/stack))
|
|
var/obj/item/stack/P = A
|
|
if(material_points_conversion[P.get_material_name()])
|
|
EC.contents[EC.contents.len]["value"] = P.get_amount() * material_points_conversion[P.get_material_name()]
|
|
EC.contents[EC.contents.len]["quantity"] = P.get_amount()
|
|
EC.value += EC.contents[EC.contents.len]["value"]
|
|
|
|
//Sell spacebucks
|
|
if(istype(A, /obj/item/weapon/spacecash))
|
|
var/obj/item/weapon/spacecash/cashmoney = A
|
|
EC.contents[EC.contents.len]["value"] = cashmoney.worth * points_per_money
|
|
EC.contents[EC.contents.len]["quantity"] = cashmoney.worth
|
|
EC.value += EC.contents[EC.contents.len]["value"]
|
|
|
|
|
|
|
|
// Make a log of it, but it wasn't shipped properly, and so isn't worth anything
|
|
else
|
|
EC.contents = list(
|
|
"error" = "Error: Product was improperly packaged. Payment rendered null under terms of agreement."
|
|
)
|
|
|
|
exported_crates += EC
|
|
points += EC.value
|
|
EC.value += base_value
|
|
|
|
// Duplicate the receipt for the admin-side log
|
|
var/datum/exported_crate/adm = new()
|
|
adm.name = EC.name
|
|
adm.value = EC.value
|
|
adm.contents = deepCopyList(EC.contents)
|
|
adm_export_history += adm
|
|
|
|
qdel(MA)
|
|
|
|
/datum/controller/supply/proc/get_clear_turfs()
|
|
var/list/clear_turfs = list()
|
|
|
|
for(var/area/subarea in shuttle.shuttle_area)
|
|
for(var/turf/T in subarea)
|
|
if(T.density)
|
|
continue
|
|
var/occupied = 0
|
|
for(var/atom/A in T.contents)
|
|
if(!A.simulated)
|
|
continue
|
|
occupied = 1
|
|
break
|
|
if(!occupied)
|
|
clear_turfs += T
|
|
|
|
return clear_turfs
|
|
|
|
//Buying
|
|
/datum/controller/supply/proc/buy()
|
|
var/list/shoppinglist = list()
|
|
for(var/datum/supply_order/SO in order_history)
|
|
if(SO.status == SUP_ORDER_APPROVED)
|
|
shoppinglist += SO
|
|
|
|
if(!shoppinglist.len)
|
|
return
|
|
var/orderedamount = shoppinglist.len
|
|
|
|
var/list/clear_turfs = get_clear_turfs()
|
|
|
|
for(var/datum/supply_order/SO in shoppinglist)
|
|
if(!clear_turfs.len)
|
|
break
|
|
|
|
var/i = rand(1,clear_turfs.len)
|
|
var/turf/pickedloc = clear_turfs[i]
|
|
clear_turfs.Cut(i,i+1)
|
|
|
|
SO.status = SUP_ORDER_SHIPPED
|
|
var/datum/supply_pack/SP = SO.object
|
|
|
|
var/obj/A = new SP.containertype(pickedloc)
|
|
A.name = "[SP.containername] [SO.comment ? "([SO.comment])":"" ]"
|
|
|
|
//supply manifest generation begin
|
|
var/obj/item/weapon/paper/manifest/slip
|
|
if(!SP.contraband)
|
|
slip = new /obj/item/weapon/paper/manifest(A)
|
|
slip.is_copy = 0
|
|
slip.info = "<h3>[command_name()] Shipping Manifest</h3><hr><br>"
|
|
slip.info +="Order #[SO.ordernum]<br>"
|
|
slip.info +="Destination: [station_name()]<br>"
|
|
slip.info +="[orderedamount] PACKAGES IN THIS SHIPMENT<br>"
|
|
slip.info +="CONTENTS:<br><ul>"
|
|
|
|
//spawn the stuff, finish generating the manifest while you're at it
|
|
if(SP.access)
|
|
if(isnum(SP.access))
|
|
A.req_access = list(SP.access)
|
|
else if(islist(SP.access) && SP.one_access)
|
|
var/list/L = SP.access // access var is a plain var, we need a list
|
|
A.req_one_access = L.Copy()
|
|
A.req_access.Cut()
|
|
else if(islist(SP.access) && !SP.one_access)
|
|
var/list/L = SP.access
|
|
A.req_access = L.Copy()
|
|
else
|
|
log_debug("<span class='danger'>Supply pack with invalid access restriction [SP.access] encountered!</span>")
|
|
|
|
var/list/contains
|
|
if(istype(SP,/datum/supply_pack/randomised))
|
|
var/datum/supply_pack/randomised/SPR = SP
|
|
contains = list()
|
|
if(SPR.contains.len)
|
|
for(var/j=1,j<=SPR.num_contained,j++)
|
|
contains += pick(SPR.contains)
|
|
else
|
|
contains = SP.contains
|
|
|
|
for(var/typepath in contains)
|
|
if(!typepath)
|
|
continue
|
|
|
|
var/number_of_items = max(1, contains[typepath])
|
|
for(var/j = 1 to number_of_items)
|
|
var/atom/B2 = new typepath(A)
|
|
if(slip)
|
|
slip.info += "<li>[B2.name]</li>" //add the item to the manifest
|
|
|
|
//manifest finalisation
|
|
if(slip)
|
|
slip.info += "</ul><br>"
|
|
slip.info += "CHECK CONTENTS AND STAMP BELOW THE LINE TO CONFIRM RECEIPT OF GOODS<hr>"
|
|
|
|
return
|
|
|
|
// Will attempt to purchase the specified order, returning TRUE on success, FALSE on failure
|
|
/datum/controller/supply/proc/approve_order(var/datum/supply_order/O, var/mob/user)
|
|
// Not enough points to purchase the crate
|
|
if(supply_controller.points <= O.object.cost)
|
|
return FALSE
|
|
|
|
// Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history
|
|
var/datum/supply_order/adm_order
|
|
for(var/datum/supply_order/temp in adm_order_history)
|
|
if(temp.ordernum == O.ordernum)
|
|
adm_order = temp
|
|
break
|
|
|
|
var/idname = "*None Provided*"
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
idname = H.get_authentification_name()
|
|
else if(issilicon(user))
|
|
idname = user.real_name
|
|
|
|
// Update order status
|
|
O.status = SUP_ORDER_APPROVED
|
|
O.approved_by = idname
|
|
O.approved_at = stationdate2text() + " - " + stationtime2text()
|
|
// Update admin-side mirror
|
|
adm_order.status = SUP_ORDER_APPROVED
|
|
adm_order.approved_by = idname
|
|
adm_order.approved_at = stationdate2text() + " - " + stationtime2text()
|
|
|
|
// Deduct cost
|
|
supply_controller.points -= O.object.cost
|
|
return TRUE
|
|
|
|
// Will deny the specified order. Only useful if the order is currently requested, but available at any status
|
|
/datum/controller/supply/proc/deny_order(var/datum/supply_order/O, var/mob/user)
|
|
// Based on the current model, there shouldn't be any entries in order_history, requestlist, or shoppinglist, that aren't matched in adm_order_history
|
|
var/datum/supply_order/adm_order
|
|
for(var/datum/supply_order/temp in adm_order_history)
|
|
if(temp.ordernum == O.ordernum)
|
|
adm_order = temp
|
|
break
|
|
|
|
var/idname = "*None Provided*"
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
idname = H.get_authentification_name()
|
|
else if(issilicon(user))
|
|
idname = user.real_name
|
|
|
|
// Update order status
|
|
O.status = SUP_ORDER_DENIED
|
|
O.approved_by = idname
|
|
O.approved_at = stationdate2text() + " - " + stationtime2text()
|
|
// Update admin-side mirror
|
|
adm_order.status = SUP_ORDER_DENIED
|
|
adm_order.approved_by = idname
|
|
adm_order.approved_at = stationdate2text() + " - " + stationtime2text()
|
|
return
|
|
|
|
// Will deny all requested orders
|
|
/datum/controller/supply/proc/deny_all_pending(var/mob/user)
|
|
for(var/datum/supply_order/O in order_history)
|
|
if(O.status == SUP_ORDER_REQUESTED)
|
|
deny_order(O, user)
|
|
|
|
// Will delete the specified order from the user-side list
|
|
/datum/controller/supply/proc/delete_order(var/datum/supply_order/O, var/mob/user)
|
|
// Making sure they know what they're doing
|
|
if(alert(user, "Are you sure you want to delete this record? If it has been approved, cargo points will NOT be refunded!", "Delete Record","No","Yes") == "Yes")
|
|
if(alert(user, "Are you really sure? There is no way to recover the order once deleted.", "Delete Record", "No", "Yes") == "Yes")
|
|
log_admin("[key_name(user)] has deleted supply order \ref[O] [O] from the user-side order history.")
|
|
supply_controller.order_history -= O
|
|
return
|
|
|
|
// Will generate a new, requested order, for the given supply pack type
|
|
/datum/controller/supply/proc/create_order(var/datum/supply_pack/S, var/mob/user, var/reason)
|
|
var/datum/supply_order/new_order = new()
|
|
var/datum/supply_order/adm_order = new() // Admin-recorded order must be a separate copy in memory, or user-made edits will corrupt it
|
|
|
|
var/idname = "*None Provided*"
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
idname = H.get_authentification_name()
|
|
else if(issilicon(user))
|
|
idname = user.real_name
|
|
|
|
new_order.ordernum = ++ordernum // Ordernum is used to track the order between the playerside list of orders and the adminside list
|
|
new_order.index = new_order.ordernum // Index can be fabricated, or falsified. Ordernum is a permanent marker used to track the order
|
|
new_order.object = S
|
|
new_order.name = S.name
|
|
new_order.cost = S.cost
|
|
new_order.ordered_by = idname
|
|
new_order.comment = reason
|
|
new_order.ordered_at = stationdate2text() + " - " + stationtime2text()
|
|
new_order.status = SUP_ORDER_REQUESTED
|
|
|
|
adm_order.ordernum = new_order.ordernum
|
|
adm_order.index = new_order.index
|
|
adm_order.object = new_order.object
|
|
adm_order.name = new_order.name
|
|
adm_order.cost = new_order.cost
|
|
adm_order.ordered_by = new_order.ordered_by
|
|
adm_order.comment = new_order.comment
|
|
adm_order.ordered_at = new_order.ordered_at
|
|
adm_order.status = new_order.status
|
|
|
|
order_history += new_order
|
|
adm_order_history += adm_order
|
|
|
|
// Will delete the specified export receipt from the user-side list
|
|
/datum/controller/supply/proc/delete_export(var/datum/exported_crate/E, var/mob/user)
|
|
// Making sure they know what they're doing
|
|
if(alert(user, "Are you sure you want to delete this record?", "Delete Record","No","Yes") == "Yes")
|
|
if(alert(user, "Are you really sure? There is no way to recover the receipt once deleted.", "Delete Record", "No", "Yes") == "Yes")
|
|
log_admin("[key_name(user)] has deleted export receipt \ref[E] [E] from the user-side export history.")
|
|
supply_controller.exported_crates -= E
|
|
return
|
|
|
|
// Will add an item entry to the specified export receipt on the user-side list
|
|
/datum/controller/supply/proc/add_export_item(var/datum/exported_crate/E, var/mob/user)
|
|
var/new_name = input(user, "Name", "Please enter the name of the item.") as null|text
|
|
if(!new_name)
|
|
return
|
|
|
|
var/new_quantity = input(user, "Name", "Please enter the quantity of the item.") as null|num
|
|
if(!new_quantity)
|
|
return
|
|
|
|
var/new_value = input(user, "Name", "Please enter the value of the item.") as null|num
|
|
if(!new_value)
|
|
return
|
|
|
|
E.contents[++E.contents.len] = list(
|
|
"object" = new_name,
|
|
"quantity" = new_quantity,
|
|
"value" = new_value
|
|
)
|