Files
Bubberstation/code/modules/mining/boulder_processing/boulder.dm
necromanceranne ab3cac746e Herculean/Atlas implants get a few additional BOULDER/Equipment related benefits. Boulders are more robust. Natural Athletics gains. (#87469)
## About The Pull Request

Herculean/Atlas spinal implants allow you to break boulders to process
them manually. Doing so also grants athletics experience.

They also increase the speed at which you throw objects, and also
increase the range further. The Atlas version also allows you to reduce
equipment movespeed penalties. (Fikou wanted me to do this for
specifically modsuits, but I don't see any reason this can't apply to
more than that, modsuits are just generally the slowest equipment you
could wear)

Boulders have 30 throw force, are by default HUGE, but have no throw
range at all and really weak tk throw range. You need a throwing booster
to throw boulders around. (They do extra damage to SPECIAL mobs, like
lavaland bosses, because it's funny to kill a god-like being with a huge
rock while slow as shit)

You naturally get athletics experience by doing some actions. Fireman
carrying, climbing tables/crates, climbing rope and climbing ladders
give you a slow and steady stream of experience. Rope gives a larger
amount if only because the process for doing so is destructive in some
way (you have to get more climbing rope eventually, so there is an
associated cost). Spinal implants now improve climbing speed for
climbing over objects.

## Why It's Good For The Game

I just had this strange compulsion to make the spinal implant make you
good at throwing boulders at lethal speeds. It already has some cool
rock-based special effects around you. Having mastery over rock throwing
just kind of made sense. Additionally, I thought it would be a funny
method for someone to be able to kill a boss via way of thrown boulders.
Or some hapless nuclear operative unprepared for a bunch of assistants
lobbing boulders at them.

Additionally, I also think it letting you break boulders for ore is a
fairly minor but fun addition. Pull boulders out of the vents, smash
them. Good work out. Particularly for miners.

I talked a while ago with I think Jacq about adding some natural methods
for accumulating athletics experience over the course of a round that
doesn't involve the dedicated weight lifting machinery/boxing bag. I
thought any method for doing that should probably be relatively slow and
definitely nonviable for significant athletics gains beyond novice
level. Mooost people will hit novice on a multi-z map if they go up and
down ladders enough. Or climb a lot of tables/crates. The results aren't
going to have much noticeable impact, however, as you only gain
appreciable benefits above novice. Probably just greater visibility of
fitness in a round. (Rather nice for the RD, who gets to show off how
swole they are more regularly).

<details>
  <summary>Spoiler warning</summary>


![CES8b4XWEAAIakb](https://github.com/user-attachments/assets/8baaa012-8048-419f-b936-2dc6621db3c2)

</details>

## Changelog
🆑
balance: Herculean/Atlas spinal implants make you better at climbing
tables, throw objects much faster and throw them much further. You can
also break boulders with your bare hands.
balance: Atlas implants let you offset the weight of worn/carried
equipment (like boulders).
balance: Boulders have no throw range, but if you can throw them via
throwing range boosters (such as the spinal implant), they deal fairly
large amounts of damage. Especially to powerful mobs like lavlaand
bosses.
balance: You naturally gain a small amount of athletics experience doing
some tasks, such as climbing rope, ladders and tables/crates, fireman
carrying, or smashing open boulders.
/🆑
2024-11-07 12:58:32 +00:00

183 lines
7.9 KiB
Plaintext

/**
* The objects that ore vents produce, which is refined into minerals.
*/
/obj/item/boulder
name = "boulder"
desc = "This rocks."
icon_state = "ore"
icon = 'icons/obj/ore.dmi'
item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND
w_class = WEIGHT_CLASS_HUGE
throwforce = 30 // Under normal circumstances, pretty much nobody can throw this.
throw_range = 0
tk_throw_range = 1 // Sorry, this is too cheesy, but maybe you can smash down doors with it.
throw_speed = 0.5
slowdown = 1.5
drag_slowdown = 1.5 // It's still a big rock.
///When a refinery machine is working on this boulder, we'll set this. Re reset when the process is finished, but the boulder may still be refined/operated on further.
var/obj/machinery/processed_by = null
/// How many steps of refinement this boulder has gone through. Starts at 5-8, goes down one each machine process.
var/durability = 5
/// What was the size of the boulder when it was spawned? This is used for inheiriting the icon_state.
var/boulder_size = BOULDER_SIZE_SMALL
/// Used in inheriting the icon_state from our parent vent in update_icon.
var/boulder_string = "boulder"
/obj/item/boulder/Initialize(mapload)
. = ..()
register_context()
AddComponent(/datum/component/two_handed, require_twohands = TRUE, force_unwielded = 0, force_wielded = 5) //Heavy as all hell, it's a boulder, dude.
AddComponent(/datum/component/sisyphus_awarder)
AddElement(/datum/element/bane, mob_biotypes = MOB_SPECIAL, added_damage = 20, requires_combat_mode = FALSE)
/obj/item/boulder/Destroy(force)
SSore_generation.available_boulders -= src
processed_by = null
return ..()
/obj/item/boulder/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(held_item?.tool_behaviour == TOOL_MINING || HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
context[SCREENTIP_CONTEXT_RMB] = "Crush boulder into ore"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/boulder/examine(mob/user)
. = ..()
. += span_notice("This boulder would take [durability] more steps to refine or break.")
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
. += span_notice("You can crush this boulder with your bare hands.")
/obj/item/boulder/examine_more(mob/user)
. = ..()
. += span_notice("[span_bold("Boulders")] can either be cracked open by [span_bold("mining tools")], or processed into sheets with [span_bold("refineries or smelters")]. Undisturbed boulders can be collected by the [span_bold("BRM")].")
/obj/item/boulder/update_icon_state()
. = ..()
switch(boulder_size)
if(BOULDER_SIZE_SMALL)
icon_state = "[boulder_string]_small"
if(BOULDER_SIZE_MEDIUM)
icon_state = "[boulder_string]_medium"
if(BOULDER_SIZE_LARGE)
icon_state = "[boulder_string]_large"
else
icon_state = "[boulder_string]_small"
/obj/item/boulder/CanAllowThrough(atom/movable/mover, border_dir)
if(istype(mover, /obj/item/boulder)) //This way, boulders can only go one at a time on conveyor belts, but everyone else can go through.
return FALSE
return ..()
/obj/item/boulder/attack_self(mob/user, list/modifiers)
. = ..()
if(.)
return
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
manual_process(null, user, INATE_BOULDER_SPEED_MULTIPLIER)
return
/obj/item/boulder/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
return
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
manual_process(null, user, INATE_BOULDER_SPEED_MULTIPLIER)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/boulder/attackby_secondary(obj/item/weapon, mob/user, params)
. = ..()
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER) || HAS_TRAIT(weapon, TRAIT_BOULDER_BREAKER))
manual_process(weapon, user, INATE_BOULDER_SPEED_MULTIPLIER)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(weapon.tool_behaviour == TOOL_MINING)
manual_process(weapon, user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return ..()
/obj/item/boulder/attack_basic_mob(mob/user, list/modifiers)
. = ..()
if(.)
return
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
manual_process(null, user, INATE_BOULDER_SPEED_MULTIPLIER) //A little hacky but it works around the speed of the blackboard task selection process for now.
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/**
* This is called when a boulder is processed by a mob or tool, and reduces the durability of the boulder.
* @param obj/item/weapon The weapon that is being used to process the boulder, that we pull toolspeed from. If null, we use the override_speed_multiplier instead.
* @param mob/living/user The mob that is processing the boulder.
* @param override_speed_multiplier The speed multiplier to use if weapon is null. The do_after will take 2 * this value seconds to complete.
* @param continued Whether or not this is a continued process, or the first one. If true, we don't play the "You swing at the boulder" message.
*/
/obj/item/boulder/proc/manual_process(obj/item/weapon, mob/living/user, override_speed_multiplier, continued = FALSE)
var/process_speed = 0
//Handle weapon conditions.
var/skill_modifier = user.mind?.get_skill_modifier(/datum/skill/mining, SKILL_SPEED_MODIFIER) || 1
if(weapon)
if(HAS_TRAIT(weapon, TRAIT_INSTANTLY_PROCESSES_BOULDERS))
durability = 0
process_speed = weapon.toolspeed
weapon.play_tool_sound(src, 50)
if(!continued)
to_chat(user, span_notice("You swing at \the [src]..."))
// Handle user conditions/override conditions.
else if (override_speed_multiplier || HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
if(HAS_TRAIT(user, TRAIT_INSTANTLY_PROCESSES_BOULDERS))
durability = 0
else if(override_speed_multiplier)
process_speed = override_speed_multiplier
else
process_speed = INATE_BOULDER_SPEED_MULTIPLIER
playsound(src, 'sound/effects/rock/rocktap1.ogg', 50)
if(!continued)
to_chat(user, span_notice("You scrape away at \the [src]..."))
else
CRASH("No weapon, acceptable user, or override speed multiplier passed to manual_process()")
if(durability > 0)
if(!do_after(user, (2 * process_speed * skill_modifier SECONDS), target = src))
return
if(!user.Adjacent(src))
return
durability--
user.apply_damage(4 * skill_modifier, STAMINA)
if(durability <= 0)
convert_to_ore()
to_chat(user, span_notice("You finish working on \the [src], and it crumbles into ore."))
playsound(src, 'sound/effects/rock/rock_break.ogg', 50)
user.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * 0.2)
user.mind?.adjust_experience(/datum/skill/athletics, MINING_SKILL_BOULDER_SIZE_XP * 0.2)
qdel(src)
return
var/msg = (durability == 1 ? "is crumbling!" : "looks weaker!")
to_chat(user, span_notice("\The [src] [msg]"))
manual_process(weapon, user, override_speed_multiplier, continued = TRUE)
/**
* This function is called while breaking boulders manually, and drops ore based on the boulder's mineral content.
* Quantity of ore spawned here is 1 less than if the boulder was processed by a machine, but clamped at 10 maximum, 1 minimum.
*/
/obj/item/boulder/proc/convert_to_ore()
for(var/datum/material/picked in custom_materials)
var/obj/item/stack/ore/cracked_ore // Take the associated value and convert it into ore stacks...
var/quantity = clamp(round((custom_materials[picked] - SHEET_MATERIAL_AMOUNT)/SHEET_MATERIAL_AMOUNT), 1, 10) //but less resources than if they processed it by hand.
var/cracked_ore_type = picked.ore_type
if(isnull(cracked_ore_type))
stack_trace("boulder found containing material type [picked.type] with no set ore_type")
continue
cracked_ore = new cracked_ore_type (drop_location(), quantity)
SSblackbox.record_feedback("tally", "ore_mined", quantity, cracked_ore.type)
///Moves boulder contents to the drop location, and then deletes the boulder.
/obj/item/boulder/proc/break_apart()
if(length(contents))
var/list/quips = list("Clang!", "Crack!", "Bang!", "Clunk!", "Clank!")
visible_message(span_notice("[pick(quips)] Something falls out of \the [src]!"))
playsound(loc, 'sound/effects/pickaxe/picaxe1.ogg', 60, FALSE)
for(var/obj/item/content as anything in contents)
content.forceMove(get_turf(src))
qdel(src)