mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 01:51:46 +00:00
 ## About The Pull Request Made a new UI and refactored some mech code in the process. Fixes #66048 Fixes #77051 Fixes #65958 ??? if it was broken Fixes #73051 - see details below Fixes other undocumented things, see changelog. ## Why It's Good For The Game The UI was too bulky and Mechs were too complex for no reason. Now they follow some general rules shared between other SS13 machinery, and there is less magic happening under the hood. ## Detailed Changes ### New Mech UI, Air Tank and Radio as separate modules Previous UI for comparison: <img alt="9SScrXAOjy" src="https://github.com/tgstation/tgstation/assets/3625094/904e3d07-e7d7-4d3a-a062-93e0e35b4b66"> Previously mechs came with radio pre-installed and air canisters magically pre-filled with air even when you build one in fab. Radio and Air Tanks are now both utility modules that are optional to install. Gas RCS thrusters still require Air Tank module to operate. This made the Mechs more barebones when built, giving you only the basic functionality. <img alt="5SDMlXTJxv" src="https://github.com/tgstation/tgstation/assets/3625094/b9364230-49ac-416b-aa70-e851fbf2050e"> To compensate this change, all mechs got two extra utility module slots. All other modules got new UI. And ore box now shows the list of ores inside. <img alt="SRX5FjvsHA" src="https://github.com/tgstation/tgstation/assets/3625094/cbe2e98d-1cd4-4667-8dae-2f9227b4b265"> ### Mounted Radio Works as a normal radio, but with subspace transmission. Available from the basic mech research node and can be printed in fab. ### Cabin Sealing To compensate for the lack of air tank by default, mechs with enclosed cabin (e.g. all except Ripley) got an ability to toggle cabin exposure to the outside air. Exiting the mech makes cabin air automatically exposed. When you seal the cabin, it traps some of the outside air inside the cabin and you can breathe with this air to perform a short space trips. But the oxygen will run out quickly and CO2 will build up. Sealing the cabin in space will make the cabin filled with vacuum, and it will stay there until you return to air environment and unseal the cabin, letting the breathable air to enter. There are temperature and pressure sensors that turn yellow/red when the corresponding warning thresholds are reached. You could also use personal internals in combination with cabin sealing for long space travels, so Air Tank is completely optional now and mostly needed when you need RCS thruster. ### RCS thrusters They are now available earlier in tech tree and consume reasonable amount of air (5 times more than human jetpacks), and they don't work without Mounted Air Tank, unless it's an Ion thruster variant. ### Mounted Air Tank Available from the basic mech research node and can be printed in fab. Built model comes empty, and syndicate mechs come with one full of oxygen. <img alt="GrFDrH5Hwe" src="https://github.com/tgstation/tgstation/assets/3625094/b677b705-bda0-4c8c-96c7-d32bf7bf9f28"> Can be switched to pressurize or not pressurize the cabin. Releases gas only when the cabin is sealed shut. Starts releasing automatically, but can be toggled to not release if you want to use it just as a portable canister. Cabin pressure can now be configured in the module UI instead of Maintenance UI. Can be attached to a pipe network when the mech is parked above a connection port. Comes with a pump that works similarly to the portable pump. It lets you vent the air tank contents outside, or suck air from the room to fill the air tank. Intended to provide an ability to fill the air tank without the need to bother with pipes. Also has gas sensors that display gas mix data of the tank and the cabin (when sealed). ### Stock part changes All mechs now require a servo motor and they reduce mech movement power consumption instead of scanning module. Scanning modules are optional for mech operation (still required to build) and the lack of one disables the following UI elements: - Display of mech integrity (you can still see the alerts or examine the mech to get rough idea) - Display of mech status on internal damage (and you can't repair what you can't diagnose) The rating of scanning module doesn't have any effect as of now. Cargo mech comes without it roundstart. <img alt="2vDtt99oqb" src="https://github.com/tgstation/tgstation/assets/3625094/147144ca-824e-4501-acf5-6ee104f309e7"> Capacitors now also reduce light power usage and raise the overclocking temperature thresholds (see below). ### Maintenance Maintenance UI removed, and its logic migrated to other places. Access modification now managed inside the mech, and anyone who can control the mech, can adjust the access in the same way as they can set DNA key. To open the maintenance panel you just need a screwdriver. It is instant when the mech is empty and it has a 5 second delay when there is an occupant to avoid in-combat hacking and part removal. It will alert the occupant that someone is trying to tinker with their mech.  Once the panel is open, you can see the part ratings:  With open panel you can hack the mech wires (roboticists can now see them): <img alt="mj205G2qDa" src="https://github.com/tgstation/tgstation/assets/3625094/44cea0d1-44b4-4b50-b1d3-a97c0056bab3"> There are wires for: - Enabling/Disabling ID and DNA locks - Toggling mech lights - Toggling mech circuits malfunction (battery drain, sparks) - Toggling mech equipment malfunction (to repair after EMP or cause EMP-like effect, disarming mech) - 3 dud wires that do nothing The hacker may be shocked if the mech power cell allows. When the panel is open and the user has access to the mech, they can remove parts with a crowbar: <img alt="jR40gyTWtJ" src="https://github.com/tgstation/tgstation/assets/3625094/b573f5b9-b8ea-412e-b3e0-c872e01e0c23"> Hitting the mech with an ID from outside now toggles the ID Lock on/off if the ID has sufficient access. ### Power consumption and overclocking Rebalanced mech power consumption. T4 parts were not working in Syndicate Mechs, as their effect was not calculated until you manipulate parts manually. Constructed mechs with t1 parts even had their energy drain reduced with upgrade to t1. Now all mechs apply their base step power usage correctly don't ignore the stock parts. Servo tier now reduces base power consumption by 0% at t1, 50% at t2, 33% at t3 and 25% at t4 Capacitor tier now reduces base power consumption of mele attacks, phasing and light by the same amounts. Gygax leg actuators overload replaced with mech overclocking. Any mech can be overclocked by hacking wires, but only Gygax has a button for toggling it from the Cabin. Now there is an overclock coefficient. 1.5 for Gygax and other mechs, 2 for Dark Gygax. When overclocked, mechs moves N times faster, but consumes N times more power.  While overclocked, mech heats up every second, regardless of movement, and starts receiving internal and integrity damage after a certain temperature threshold. The chance is 0% at the threshold, and 100% at thresholds * 2. The roll happens every tick. Capacitor upgrades this threshold, letting you overclock safely for longer periods.  When you stop overclock, the temperature goes back down. ### Other changes and fixes Concealed weapon bay now doesn't show up when you examine the mech, so it's actually concealed now. New radio module can properly change its frequency, as it didn't work for previous radio. Launcher type weapons were ignoring cooldowns and power usage, so you could spam explosive banana peals, while they should have a 2 second cooldown: <img alt="q5GjUsHwGr" src="https://github.com/tgstation/tgstation/assets/3625094/d102725d-e9e1-4588-9d6c-b9e38b7a6535"> Now this is fixed and all launcher type weapons properly use power and have their cooldowns working. And now they have the kickback effect working (when it pushes you in the opposite direction in zero gravity on throw). Thermoregulator now heats/cools considering heat capacity instead of adding/reducing flat 10 degrees. So you can heat up cabin air quicker if the pressure is low. There were some other sloppy mistakes in mech code, like some functions returning too early, blocking other functionality unintentionally. Fixed these and made some other minor changes and improvements. ## Changelog 🆑 refactor: Refactored Mech UI refactor: Refactored mech radio into a utility module, adding extra slot to all mechs refactor: Refactored mech air tank into a utility module with an air pump, adding extra slot to all mechs refactor: Refactored mech cabin air - there is now a button to seal or unseal cabin to make it airtight or exchanging gases with the environment refactor: Removed mech maintenance UI Access is set in mech UI, and parts are ejected with a crowbar add: Mech now has wires and can be hacked qol: Roboticists now can see MOD suit and mech wires add: Mechs now require servo motor stock part and it affects movement power usage instead of scanning module add: Scanning module absence doesnt block mech movement and hides some UI data instead. Big Bess starts without one. qol: Hitting mech with ID card now toggles ID lock on/off if the card has required access fix: Fixed concealed weapon bay not being concealed on mech examine fix: Fixed mech radio not changing frequency fix: Fixed mech launcher type weapons ignoring specified cooldown fix: Fixed mech launcher type weapons not using specified power amount fix: Fixed mech temperature regulator ignoring gas heat capacity fix: Fixed mech stopping processing other things while not heating internal air fix: Fixed mech being able to leave transit tube in transit fix: Fixed mech internal damage flags working incorrectly fix: Fixed Gygax leg overloading being useless fix: Fixed mechs ignoring their stock parts on creation. Syndicate mechs now stronger against lasers and consume less energy on move. Upgrading from tier 1 to tier 2 doesn't make mech consume MORE energy than before the upgrade. balance: Rebalanced mech energy drain with part upgrades. Base energy drain reduced by 50%, 33%, 25% with upgrades and applies to movement (Servo rating), phasing, punching, light (Capacitor rating). balance: Hydraulic clamp now can force open airlocks balance: Made mech RCS pack consume reasonable amount of gas code: Fixed some other minor bugs and made some minor changes in the mech code /🆑 --------- Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: Sealed101 <cool.bullseye@yandex.ru> Co-authored-by: Jacquerel <hnevard@gmail.com>
624 lines
20 KiB
Plaintext
624 lines
20 KiB
Plaintext
|
|
#define GIBTONITE_QUALITY_HIGH 3
|
|
#define GIBTONITE_QUALITY_MEDIUM 2
|
|
#define GIBTONITE_QUALITY_LOW 1
|
|
|
|
#define ORESTACK_OVERLAYS_MAX 10
|
|
|
|
/**********************Mineral ores**************************/
|
|
|
|
/obj/item/stack/ore
|
|
name = "rock"
|
|
icon = 'icons/obj/ore.dmi'
|
|
icon_state = "ore"
|
|
inhand_icon_state = null
|
|
full_w_class = WEIGHT_CLASS_BULKY
|
|
singular_name = "ore chunk"
|
|
material_flags = MATERIAL_EFFECTS
|
|
var/points = 0 //How many points this ore gets you from the ore redemption machine
|
|
var/refined_type = null //What this ore defaults to being refined into
|
|
var/mine_experience = 5 //How much experience do you get for mining this ore?
|
|
novariants = TRUE // Ore stacks handle their icon updates themselves to keep the illusion that there's more going
|
|
var/list/stack_overlays
|
|
var/scan_state = "" //Used by mineral turfs for their scan overlay.
|
|
var/spreadChance = 0 //Also used by mineral turfs for spreading veins
|
|
|
|
/obj/item/stack/ore/update_overlays()
|
|
. = ..()
|
|
var/difference = min(ORESTACK_OVERLAYS_MAX, amount) - (LAZYLEN(stack_overlays)+1)
|
|
if(!difference)
|
|
return
|
|
|
|
if(difference < 0 && LAZYLEN(stack_overlays)) //amount < stack_overlays, remove excess.
|
|
if(LAZYLEN(stack_overlays)-difference <= 0)
|
|
stack_overlays = null
|
|
return
|
|
stack_overlays.len += difference
|
|
|
|
else //amount > stack_overlays, add some.
|
|
for(var/i in 1 to difference)
|
|
var/mutable_appearance/newore = mutable_appearance(icon, icon_state)
|
|
newore.pixel_x = rand(-8,8)
|
|
newore.pixel_y = rand(-8,8)
|
|
LAZYADD(stack_overlays, newore)
|
|
|
|
if(stack_overlays)
|
|
. += stack_overlays
|
|
|
|
/obj/item/stack/ore/welder_act(mob/living/user, obj/item/I)
|
|
..()
|
|
if(!refined_type)
|
|
return TRUE
|
|
|
|
if(I.use_tool(src, user, 0, volume=50))
|
|
new refined_type(drop_location())
|
|
use(1)
|
|
|
|
return TRUE
|
|
|
|
/obj/item/stack/ore/fire_act(exposed_temperature, exposed_volume)
|
|
. = ..()
|
|
if(isnull(refined_type))
|
|
return
|
|
else
|
|
var/probability = (rand(0,100))/100
|
|
var/burn_value = probability*amount
|
|
var/amountrefined = round(burn_value, 1)
|
|
if(amountrefined < 1)
|
|
qdel(src)
|
|
else
|
|
new refined_type(drop_location(),amountrefined)
|
|
qdel(src)
|
|
|
|
/obj/item/stack/ore/uranium
|
|
name = "uranium ore"
|
|
icon_state = "uranium"
|
|
singular_name = "uranium ore chunk"
|
|
points = 30
|
|
material_flags = NONE
|
|
mats_per_unit = list(/datum/material/uranium=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/uranium
|
|
mine_experience = 6
|
|
scan_state = "rock_Uranium"
|
|
spreadChance = 5
|
|
merge_type = /obj/item/stack/ore/uranium
|
|
|
|
/obj/item/stack/ore/iron
|
|
name = "iron ore"
|
|
icon_state = "iron"
|
|
singular_name = "iron ore chunk"
|
|
points = 1
|
|
mats_per_unit = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/iron
|
|
mine_experience = 1
|
|
scan_state = "rock_Iron"
|
|
spreadChance = 20
|
|
merge_type = /obj/item/stack/ore/iron
|
|
|
|
/obj/item/stack/ore/glass
|
|
name = "sand pile"
|
|
icon_state = "glass"
|
|
singular_name = "sand pile"
|
|
points = 1
|
|
mats_per_unit = list(/datum/material/glass=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/glass
|
|
w_class = WEIGHT_CLASS_TINY
|
|
mine_experience = 0 //its sand
|
|
merge_type = /obj/item/stack/ore/glass
|
|
|
|
GLOBAL_LIST_INIT(sand_recipes, list(\
|
|
new /datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_TOOLS), \
|
|
new /datum/stack_recipe("sandstone", /obj/item/stack/sheet/mineral/sandstone, 1, 1, 50, check_density = FALSE, category = CAT_MISC),\
|
|
new /datum/stack_recipe("aesthetic volcanic floor tile", /obj/item/stack/tile/basalt, 2, 1, 50, check_density = FALSE, category = CAT_TILES)\
|
|
))
|
|
|
|
/obj/item/stack/ore/glass/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
|
|
. = ..()
|
|
AddComponent(/datum/component/storm_hating)
|
|
|
|
/obj/item/stack/ore/glass/get_main_recipes()
|
|
. = ..()
|
|
. += GLOB.sand_recipes
|
|
|
|
/obj/item/stack/ore/glass/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
if(..() || !ishuman(hit_atom))
|
|
return
|
|
var/mob/living/carbon/human/C = hit_atom
|
|
if(C.is_eyes_covered())
|
|
C.visible_message(span_danger("[C]'s eye protection blocks the sand!"), span_warning("Your eye protection blocks the sand!"))
|
|
return
|
|
C.adjust_eye_blur(12 SECONDS)
|
|
C.adjustStaminaLoss(15)//the pain from your eyes burning does stamina damage
|
|
C.adjust_confusion(5 SECONDS)
|
|
to_chat(C, span_userdanger("\The [src] gets into your eyes! The pain, it burns!"))
|
|
qdel(src)
|
|
|
|
/obj/item/stack/ore/glass/ex_act(severity, target)
|
|
if(severity)
|
|
qdel(src)
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
/obj/item/stack/ore/glass/basalt
|
|
name = "volcanic ash"
|
|
icon_state = "volcanic_sand"
|
|
singular_name = "volcanic ash pile"
|
|
mine_experience = 0
|
|
merge_type = /obj/item/stack/ore/glass/basalt
|
|
|
|
/obj/item/stack/ore/plasma
|
|
name = "plasma ore"
|
|
icon_state = "plasma"
|
|
singular_name = "plasma ore chunk"
|
|
points = 15
|
|
mats_per_unit = list(/datum/material/plasma=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/plasma
|
|
mine_experience = 5
|
|
scan_state = "rock_Plasma"
|
|
spreadChance = 8
|
|
merge_type = /obj/item/stack/ore/plasma
|
|
|
|
/obj/item/stack/ore/plasma/welder_act(mob/living/user, obj/item/I)
|
|
to_chat(user, span_warning("You can't hit a high enough temperature to smelt [src] properly!"))
|
|
return TRUE
|
|
|
|
/obj/item/stack/ore/silver
|
|
name = "silver ore"
|
|
icon_state = "silver"
|
|
singular_name = "silver ore chunk"
|
|
points = 16
|
|
mine_experience = 3
|
|
mats_per_unit = list(/datum/material/silver=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/silver
|
|
scan_state = "rock_Silver"
|
|
spreadChance = 5
|
|
merge_type = /obj/item/stack/ore/silver
|
|
|
|
/obj/item/stack/ore/gold
|
|
name = "gold ore"
|
|
icon_state = "gold"
|
|
singular_name = "gold ore chunk"
|
|
points = 18
|
|
mine_experience = 5
|
|
mats_per_unit = list(/datum/material/gold=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/gold
|
|
scan_state = "rock_Gold"
|
|
spreadChance = 5
|
|
merge_type = /obj/item/stack/ore/gold
|
|
|
|
/obj/item/stack/ore/diamond
|
|
name = "diamond ore"
|
|
icon_state = "diamond"
|
|
singular_name = "diamond ore chunk"
|
|
points = 50
|
|
mats_per_unit = list(/datum/material/diamond=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/diamond
|
|
mine_experience = 10
|
|
scan_state = "rock_Diamond"
|
|
merge_type = /obj/item/stack/ore/diamond
|
|
|
|
/obj/item/stack/ore/bananium
|
|
name = "bananium ore"
|
|
icon_state = "bananium"
|
|
singular_name = "bananium ore chunk"
|
|
points = 60
|
|
mats_per_unit = list(/datum/material/bananium=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/bananium
|
|
mine_experience = 15
|
|
scan_state = "rock_Bananium"
|
|
merge_type = /obj/item/stack/ore/bananium
|
|
|
|
/obj/item/stack/ore/titanium
|
|
name = "titanium ore"
|
|
icon_state = "titanium"
|
|
singular_name = "titanium ore chunk"
|
|
points = 50
|
|
mats_per_unit = list(/datum/material/titanium=SHEET_MATERIAL_AMOUNT)
|
|
refined_type = /obj/item/stack/sheet/mineral/titanium
|
|
mine_experience = 3
|
|
scan_state = "rock_Titanium"
|
|
spreadChance = 5
|
|
merge_type = /obj/item/stack/ore/titanium
|
|
|
|
/obj/item/stack/ore/slag
|
|
name = "slag"
|
|
desc = "Completely useless."
|
|
icon_state = "slag"
|
|
singular_name = "slag chunk"
|
|
merge_type = /obj/item/stack/ore/slag
|
|
|
|
/obj/item/gibtonite
|
|
name = "gibtonite ore"
|
|
desc = "Extremely explosive if struck with mining equipment, Gibtonite is often used by miners to speed up their work by using it as a mining charge. This material is illegal to possess by unauthorized personnel under space law."
|
|
icon = 'icons/obj/ore.dmi'
|
|
icon_state = "gibtonite"
|
|
inhand_icon_state = "Gibtonite ore"
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
throw_range = 0
|
|
var/primed = FALSE
|
|
var/det_time = 100
|
|
var/quality = GIBTONITE_QUALITY_LOW //How pure this gibtonite is, determines the explosion produced by it and is derived from the det_time of the rock wall it was taken from, higher value = better
|
|
var/attacher = "UNKNOWN"
|
|
var/det_timer
|
|
|
|
/obj/item/gibtonite/Initialize(mapload)
|
|
. = ..()
|
|
AddComponent(/datum/component/two_handed, require_twohands=TRUE)
|
|
AddComponent(/datum/component/golem_food, consume_on_eat = FALSE, golem_food_key = /obj/item/gibtonite)
|
|
|
|
/obj/item/gibtonite/Destroy()
|
|
qdel(wires)
|
|
set_wires(null)
|
|
return ..()
|
|
|
|
/obj/item/gibtonite/attackby(obj/item/I, mob/user, params)
|
|
if(!wires && isigniter(I))
|
|
user.visible_message(span_notice("[user] attaches [I] to [src]."), span_notice("You attach [I] to [src]."))
|
|
set_wires(new /datum/wires/explosive/gibtonite(src))
|
|
attacher = key_name(user)
|
|
qdel(I)
|
|
add_overlay("Gibtonite_igniter")
|
|
return
|
|
|
|
if(wires && !primed)
|
|
if(is_wire_tool(I))
|
|
wires.interact(user)
|
|
return
|
|
|
|
if(I.tool_behaviour == TOOL_MINING || istype(I, /obj/item/resonator) || I.force >= 10)
|
|
GibtoniteReaction(user)
|
|
return
|
|
|
|
if(istype(I, /obj/item/mining_scanner) || istype(I, /obj/item/t_scanner/adv_mining_scanner) || I.tool_behaviour == TOOL_MULTITOOL)
|
|
defuse(user)
|
|
return
|
|
|
|
return ..()
|
|
|
|
/// Stop the reaction and reduce ore explosive power
|
|
/obj/item/gibtonite/proc/defuse(mob/defuser)
|
|
if (!primed)
|
|
return
|
|
primed = FALSE
|
|
if(det_timer)
|
|
deltimer(det_timer)
|
|
defuser?.visible_message(span_notice("The chain reaction stopped! ...The ore's quality looks diminished."), span_notice("You stopped the chain reaction. ...The ore's quality looks diminished."))
|
|
icon_state = "gibtonite"
|
|
quality = GIBTONITE_QUALITY_LOW
|
|
|
|
/obj/item/gibtonite/attack_self(user)
|
|
if(wires)
|
|
wires.interact(user)
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/gibtonite/bullet_act(obj/projectile/P)
|
|
GibtoniteReaction(P.firer)
|
|
return ..()
|
|
|
|
/obj/item/gibtonite/ex_act()
|
|
GibtoniteReaction(null, 1)
|
|
return TRUE
|
|
|
|
/obj/item/gibtonite/proc/GibtoniteReaction(mob/user, triggered_by = 0)
|
|
if(primed)
|
|
return
|
|
primed = TRUE
|
|
playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,TRUE)
|
|
icon_state = "gibtonite_active"
|
|
var/notify_admins = FALSE
|
|
if(!is_mining_level(z))//Only annoy the admins ingame if we're triggered off the mining zlevel
|
|
notify_admins = TRUE
|
|
|
|
if(triggered_by == 1)
|
|
log_bomber(null, "An explosion has primed a", src, "for detonation", notify_admins)
|
|
else if(triggered_by == 2)
|
|
var/turf/bombturf = get_turf(src)
|
|
if(notify_admins)
|
|
message_admins("A signal has triggered a [name] to detonate at [ADMIN_VERBOSEJMP(bombturf)]. Igniter attacher: [ADMIN_LOOKUPFLW(attacher)]")
|
|
var/bomb_message = "A signal has primed a [name] for detonation at [AREACOORD(bombturf)]. Igniter attacher: [key_name(attacher)]."
|
|
log_game(bomb_message)
|
|
GLOB.bombers += bomb_message
|
|
else
|
|
user.visible_message(span_warning("[user] strikes \the [src], causing a chain reaction!"), span_danger("You strike \the [src], causing a chain reaction."))
|
|
log_bomber(user, "has primed a", src, "for detonation", notify_admins)
|
|
det_timer = addtimer(CALLBACK(src, PROC_REF(detonate), notify_admins), det_time, TIMER_STOPPABLE)
|
|
|
|
/obj/item/gibtonite/proc/detonate(notify_admins)
|
|
if(primed)
|
|
switch(quality)
|
|
if(GIBTONITE_QUALITY_HIGH)
|
|
explosion(src, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 9, flame_range = 0, flash_range = 0, adminlog = notify_admins)
|
|
if(GIBTONITE_QUALITY_MEDIUM)
|
|
explosion(src, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 5, flame_range = 0, flash_range = 0, adminlog = notify_admins)
|
|
if(GIBTONITE_QUALITY_LOW)
|
|
explosion(src, heavy_impact_range = 1, light_impact_range = 3, flame_range = 0, flash_range = 0, adminlog = notify_admins)
|
|
qdel(src)
|
|
|
|
/obj/item/gibtonite/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
|
|
. = ..()
|
|
if (throwingdatum.dist_travelled < 2 || !isliving(hit_atom))
|
|
return
|
|
var/mob/living/hit_mob = hit_atom
|
|
hit_mob.Paralyze(1.5 SECONDS)
|
|
hit_mob.Knockdown(8 SECONDS)
|
|
|
|
/obj/item/stack/ore/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
|
|
. = ..()
|
|
pixel_x = base_pixel_x + rand(0, 16) - 8
|
|
pixel_y = base_pixel_y + rand(0, 8) - 8
|
|
|
|
/obj/item/stack/ore/ex_act(severity, target)
|
|
if(severity >= EXPLODE_DEVASTATE)
|
|
qdel(src)
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
|
|
/*****************************Coin********************************/
|
|
|
|
// The coin's value is a value of it's materials.
|
|
// Yes, the gold standard makes a come-back!
|
|
// This is the only way to make coins that are possible to produce on station actually worth anything.
|
|
/obj/item/coin
|
|
icon = 'icons/obj/economy.dmi'
|
|
name = "coin"
|
|
icon_state = "coin"
|
|
flags_1 = CONDUCT_1
|
|
force = 1
|
|
throwforce = 2
|
|
w_class = WEIGHT_CLASS_TINY
|
|
custom_materials = list(/datum/material/iron = COIN_MATERIAL_AMOUNT)
|
|
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
|
var/string_attached
|
|
var/list/sideslist = list("heads","tails")
|
|
var/cooldown = 0
|
|
var/value = 0
|
|
var/coinflip
|
|
item_flags = NO_MAT_REDEMPTION //You know, it's kind of a problem that money is worth more extrinsicly than intrinsically in this universe.
|
|
///If you do not want this coin to be valued based on its materials and instead set a custom value set this to TRUE and set value to the desired value.
|
|
var/override_material_worth = FALSE
|
|
/// The name of the heads side of the coin
|
|
var/heads_name = "heads"
|
|
/// If the coin has an action or not
|
|
var/has_action = FALSE
|
|
|
|
/obj/item/coin/Initialize(mapload)
|
|
. = ..()
|
|
coinflip = pick(sideslist)
|
|
icon_state = "coin_[coinflip]"
|
|
pixel_x = base_pixel_x + rand(0, 16) - 8
|
|
pixel_y = base_pixel_y + rand(0, 8) - 8
|
|
|
|
/obj/item/coin/set_custom_materials(list/materials, multiplier = 1)
|
|
. = ..()
|
|
if(override_material_worth)
|
|
return
|
|
value = 0
|
|
for(var/i in custom_materials)
|
|
var/datum/material/M = i
|
|
value += M.value_per_unit * custom_materials[M]
|
|
|
|
/obj/item/coin/get_item_credit_value()
|
|
return value
|
|
|
|
/obj/item/coin/suicide_act(mob/living/user)
|
|
user.visible_message(span_suicide("[user] contemplates suicide with \the [src]!"))
|
|
if (!attack_self(user))
|
|
user.visible_message(span_suicide("[user] couldn't flip \the [src]!"))
|
|
return SHAME
|
|
addtimer(CALLBACK(src, PROC_REF(manual_suicide), user), 10)//10 = time takes for flip animation
|
|
return MANUAL_SUICIDE_NONLETHAL
|
|
|
|
/obj/item/coin/proc/manual_suicide(mob/living/user)
|
|
var/index = sideslist.Find(coinflip)
|
|
if (index == 2)//tails
|
|
user.visible_message(span_suicide("\the [src] lands on [coinflip]! [user] promptly falls over, dead!"))
|
|
user.adjustOxyLoss(200)
|
|
user.death(FALSE)
|
|
user.set_suicide(TRUE)
|
|
user.suicide_log()
|
|
else
|
|
user.visible_message(span_suicide("\the [src] lands on [coinflip]! [user] keeps on living!"))
|
|
|
|
/obj/item/coin/examine(mob/user)
|
|
. = ..()
|
|
. += span_info("It's worth [value] credit\s.")
|
|
|
|
/obj/item/coin/attackby(obj/item/W, mob/user, params)
|
|
if(istype(W, /obj/item/stack/cable_coil))
|
|
var/obj/item/stack/cable_coil/CC = W
|
|
if(string_attached)
|
|
to_chat(user, span_warning("There already is a string attached to this coin!"))
|
|
return
|
|
|
|
if (CC.use(1))
|
|
add_overlay("coin_string_overlay")
|
|
string_attached = 1
|
|
to_chat(user, span_notice("You attach a string to the coin."))
|
|
else
|
|
to_chat(user, span_warning("You need one length of cable to attach a string to the coin!"))
|
|
return
|
|
else
|
|
..()
|
|
|
|
/obj/item/coin/wirecutter_act(mob/living/user, obj/item/I)
|
|
..()
|
|
if(!string_attached)
|
|
return TRUE
|
|
|
|
new /obj/item/stack/cable_coil(drop_location(), 1)
|
|
overlays = list()
|
|
string_attached = null
|
|
to_chat(user, span_notice("You detach the string from the coin."))
|
|
return TRUE
|
|
|
|
/obj/item/coin/attack_self(mob/user)
|
|
if(cooldown < world.time)
|
|
if(string_attached) //does the coin have a wire attached
|
|
to_chat(user, span_warning("The coin won't flip very well with something attached!") )
|
|
return FALSE//do not flip the coin
|
|
cooldown = world.time + 15
|
|
flick("coin_[coinflip]_flip", src)
|
|
coinflip = pick(sideslist)
|
|
icon_state = "coin_[coinflip]"
|
|
playsound(user.loc, 'sound/items/coinflip.ogg', 50, TRUE)
|
|
var/oldloc = loc
|
|
sleep(1.5 SECONDS)
|
|
if(loc == oldloc && user && !user.incapacitated())
|
|
user.visible_message(span_notice("[user] flips [src]. It lands on [coinflip]."), \
|
|
span_notice("You flip [src]. It lands on [coinflip]."), \
|
|
span_hear("You hear the clattering of loose change."))
|
|
if(has_action)
|
|
if(coinflip == heads_name)
|
|
heads_action(user)
|
|
else
|
|
tails_action(user)
|
|
return TRUE//did the coin flip? useful for suicide_act
|
|
|
|
/obj/item/coin/proc/heads_action(mob/user)
|
|
return
|
|
|
|
/obj/item/coin/proc/tails_action(mob/user)
|
|
return
|
|
|
|
/obj/item/coin/gold
|
|
custom_materials = list(/datum/material/gold = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/silver
|
|
custom_materials = list(/datum/material/silver = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/diamond
|
|
custom_materials = list(/datum/material/diamond = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/plasma
|
|
custom_materials = list(/datum/material/plasma = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/uranium
|
|
custom_materials = list(/datum/material/uranium = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/titanium
|
|
custom_materials = list(/datum/material/titanium = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/bananium
|
|
custom_materials = list(/datum/material/bananium = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/adamantine
|
|
custom_materials = list(/datum/material/adamantine = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/mythril
|
|
custom_materials = list(/datum/material/mythril = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/plastic
|
|
custom_materials = list(/datum/material/plastic = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/runite
|
|
custom_materials = list(/datum/material/runite = COIN_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/coin/twoheaded
|
|
desc = "Hey, this coin's the same on both sides!"
|
|
sideslist = list("heads")
|
|
|
|
/obj/item/coin/antagtoken
|
|
name = "antag token"
|
|
desc = "A novelty coin that helps the heart know what hard evidence cannot prove."
|
|
icon_state = "coin_valid"
|
|
custom_materials = list(/datum/material/plastic = COIN_MATERIAL_AMOUNT)
|
|
sideslist = list("valid", "salad")
|
|
heads_name = "valid"
|
|
material_flags = NONE
|
|
override_material_worth = TRUE
|
|
|
|
/obj/item/coin/iron
|
|
|
|
/obj/item/coin/gold/debug
|
|
custom_materials = list(/datum/material/gold = COIN_MATERIAL_AMOUNT)
|
|
desc = "If you got this somehow, be aware that it will dust you. Almost certainly."
|
|
|
|
/obj/item/coin/gold/debug/attack_self(mob/user)
|
|
if(cooldown < world.time)
|
|
if(string_attached) //does the coin have a wire attached
|
|
to_chat(user, span_warning("The coin won't flip very well with something attached!") )
|
|
return FALSE//do not flip the coin
|
|
cooldown = world.time + 15
|
|
flick("coin_[coinflip]_flip", src)
|
|
coinflip = pick(sideslist)
|
|
icon_state = "coin_[coinflip]"
|
|
playsound(user.loc, 'sound/items/coinflip.ogg', 50, TRUE)
|
|
var/oldloc = loc
|
|
sleep(1.5 SECONDS)
|
|
if(loc == oldloc && user && !user.incapacitated())
|
|
user.visible_message(span_notice("[user] flips [src]. It lands on [coinflip]."), \
|
|
span_notice("You flip [src]. It lands on [coinflip]."), \
|
|
span_hear("You hear the clattering of loose change."))
|
|
SSeconomy.fire()
|
|
to_chat(user,"<span class='bounty'>[SSeconomy.inflation_value()] is the inflation value.</span>")
|
|
return TRUE//did the coin flip? useful for suicide_act
|
|
|
|
|
|
///Coins used in the dutchmen money bag.
|
|
/obj/item/coin/silver/doubloon
|
|
name = "doubloon"
|
|
|
|
/obj/item/coin/gold/doubloon
|
|
name = "doubloon"
|
|
|
|
/obj/item/coin/adamantine/doubloon
|
|
name = "doubloon"
|
|
|
|
/obj/item/coin/eldritch
|
|
name = "eldritch coin"
|
|
desc = "Everytime it lands it bolts or opens doors, except for you."
|
|
icon_state = "coin_heretic"
|
|
custom_materials = list(/datum/material/diamond =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/plasma =HALF_SHEET_MATERIAL_AMOUNT)
|
|
sideslist = list("heretic", "blade")
|
|
heads_name = "heretic"
|
|
has_action = TRUE
|
|
material_flags = NONE
|
|
/// The range at which airlocks are effected.
|
|
var/airlock_range = 5
|
|
|
|
/obj/item/coin/eldritch/heads_action(mob/user)
|
|
var/mob/living/living_user = user
|
|
if(!IS_HERETIC(user))
|
|
living_user.adjustBruteLoss(5)
|
|
return
|
|
for(var/obj/machinery/door/airlock/target_airlock in range(airlock_range, user))
|
|
if(target_airlock.density)
|
|
target_airlock.open()
|
|
continue
|
|
target_airlock.close(force_crush = TRUE)
|
|
|
|
/obj/item/coin/eldritch/tails_action(mob/user)
|
|
var/mob/living/living_user = user
|
|
if(!IS_HERETIC(user))
|
|
living_user.adjustFireLoss(5)
|
|
return
|
|
for(var/obj/machinery/door/airlock/target_airlock in range(airlock_range, user))
|
|
if(target_airlock.locked)
|
|
target_airlock.unlock()
|
|
continue
|
|
target_airlock.lock()
|
|
|
|
/obj/item/coin/eldritch/afterattack(atom/target_atom, mob/user, proximity)
|
|
. = ..()
|
|
if(!proximity)
|
|
return
|
|
if(!IS_HERETIC(user))
|
|
var/mob/living/living_user = user
|
|
living_user.adjustBruteLoss(5)
|
|
living_user.adjustFireLoss(5)
|
|
return
|
|
if(istype(target_atom, /obj/machinery/door/airlock))
|
|
var/obj/machinery/door/airlock/target_airlock = target_atom
|
|
to_chat(user, span_warning("You insert [src] into the airlock."))
|
|
target_airlock.emag_act(user, src)
|
|
qdel(src)
|
|
|
|
#undef GIBTONITE_QUALITY_HIGH
|
|
#undef GIBTONITE_QUALITY_LOW
|
|
#undef GIBTONITE_QUALITY_MEDIUM
|
|
#undef ORESTACK_OVERLAYS_MAX
|