Files
Bubberstation/code/game/objects/structures/gym/weight_machine.dm
necromanceranne ca2ff33292 Rocky DLXIII: Fitness to Athletics, Athletics skill influences Boxing against Boxers, Boxing overhaul, Evil Boxing (#82611)
## About The Pull Request

### Changes Fitness skill to Athletics skill; Removes the sprite growth
as the only meaningful reward

Basically just a rebranding so that it is more broad in its concept. It
isn't just about peak physique. It's about how you can apply it too.

Reaching Legendary rewards you with the Golden Gloves.

### Boxing Overhaul

I started it in this pr here
https://github.com/tgstation/tgstation/pull/80635

But this is a more significant overhaul of boxing from the ground up.
Namely, it now is majorly influenced by the Athletics skill, and also
more thoroughly aligns with conventional punching in its current state.
However, a major component of this is that boxing's breadth of mechanics
is only relevant when used against another boxer. Non-boxer targets are
only ever going to get smacked by a stamina punch, as it is currently
(and more or less the same values too). Additionally, boxers cannot
punch someone who is either unconscious or in stamina crit, so it can't
be used to maintain stamcrit.

- Boxing now has a One-Two Punch mechanic. Swapping between the left and
right mouse buttons to punch and maintaining this causes every second
punch to do more damage (to boxers). Breaking this chain by punching
with the same button twice will cause you to lose damage (against
boxers).

- Boxing now can have crits occur below 50 stamina damage (against
boxers). However, to knock someone out clean, the target must be
staggered from a previous crit first. (Or I guess be staggered at all,
maybe their shoelaces were tied or something). Crit probabilities are
determined by adding your Athletics probability skill bonuses to your
unarmed effectiveness, and then reduced by the targets own Athletics
skill bonuses. It is hard for two athletes to knock each other out in
one blow. It is trivial for a legendary boxer to knock out a novice in a
single punch. Cybernetically enhanced boxers are dangerously effective
at boxing.

- Boxers can block other boxers by using throw mode, but the chances of
doing so is based on Athletics skill bonuses. A successful block causes
the attacker to take stamina damage (as does the blocker, but less so).

- Participating in boxing increases the Athletics skill. Your gains are
probably slower than normal training, but you at least get some reward
for doing so. Only relevant if your opponent is a boxer. Get a sparring
partner!

### Evil Boxing (Admin Only)

So this is all well and good, but what if you're a scoundrel with no
care for the other person in this equation? Well, that's where Evil
Boxing comes in. Evil Boxing has absolutely no restrictions at all, and
can freely use its boxing abilities against anyone. Even non-boxers. You
can even grab people! Wow, what a scumbag. I bet you even king hit
people. Thankfully, only an admin can make you into an Evil Boxer.

### Strength and Stimmed Mutations now benefit Exercising and Boxing
(against boxers)

Both mutations have a flimsy increase to instability of 5 because of how
incredibly particular the bonus is.

Strength now reduces the amount of stamina damage inflicted via
exercising by half. Strength also adds a +2 damage bonus on boxing
punches while boxing against boxers (this doesn't influence normal
unarmed attacks whatsoever, or boxing against nonboxers). Obviously,
evil boxers just get this benefit against everyone because they're
knaves.

Stimmed increases the duration of the exercised status effect by one
factor. (That is, if you have no other modifiers, it will double the
duration on its own)

## Why It's Good For The Game

@Jacquerel was racking their brain trying to come up with an alternative
to the skill rewards of Fitness. At the moment, sprite growth is not
only a really bad bonus (it does literally nothing but make you big, and
thus is actually a detriment due to how intrusive sprite size is with
interaction, and plays weird with other sprite changing effects), but
isn't really all that interesting for all the effort put in.

Problem is, how do you make Fitness meaningful without making it a
powergame mechanic? Simple! You make it applicable to a really niche,
self-contained but still utilized game mechanic that is engaging to
participate in and makes you meaningfully very good in it. AND scratches
that power scaling itch that many players have.

Boxing is reasonably self-contained as is, but pretty bland due to how
old it is. While maintaining that self-contained nature, I hoped to
expand the interactivity of boxing a little bit by giving it a small
mini-game in of itself. And also introduce ways to simulate the idea of
boxing a bit better.

The idea of being an Evil Boxer is funny. For every Rocky, there is a
[Insert Rocky Villain Here that isn't Creed]. But in this case, it's
probably some Syndicate douchebag looking to punch clowns into a coma
and inspire the mime to take up shadowboxing in revenge.

## Changelog
🆑
add: Replaces Fitness with Athletics; same skill, but now more
specifically applicable to boxing.
add: Athletics does not increase sprite size.
balance: Overhauls Boxing to add a lot more depth to the interactions.
Only applicable to other boxers, however. You can still punch the snot
out of non-boxers though. But only up to stamcrit or unconsciousness. No
hitting someone who can't fight back!
balance: Adds Evil Boxing, which is the evil and fucked up version of
boxing that you kill people with and are allowed to flout the sacred
rules of boxing as you please. Everyone is a victim!
/🆑
2024-04-22 16:43:58 +01:00

199 lines
6.4 KiB
Plaintext

#define WORKOUT_XP 5
#define EXERCISE_STATUS_DURATION 15 SECONDS
#define SAFE_DRUNK_LEVEL 39
/obj/structure/weightmachine
name = "chest press machine"
desc = "Just looking at this thing makes you feel tired."
icon = 'icons/obj/fluff/gym_equipment.dmi'
icon_state = "stacklifter"
base_icon_state = "stacklifter"
can_buckle = TRUE
density = TRUE
anchored = TRUE
blocks_emissive = EMISSIVE_BLOCK_UNIQUE
///How much we shift the user's pixel y when using the weight machine.
var/pixel_shift_y = -3
///The weight action we give to people that buckle themselves to us.
var/datum/action/push_weights/weight_action
///message when drunk user fails to use the machine
var/drunk_message = "You try for a new record and pull through! Through a muscle that is."
// the total reps you can do before you hit stamcrit based on fitness level
var/static/list/total_workout_reps = list(3, 4, 4, 5, 6, 6, 7)
///List of messages picked when using the machine.
var/static/list/more_weight = list(
"pushing it to the limit!",
"going into overdrive!",
"burning with determination!",
"rising up to the challenge!",
"getting strong now!",
"getting ripped!",
)
///List of messages picked when finished using the machine.
var/static/list/finished_message = list(
"You feel stronger!",
"You feel like you can take on the world!",
"You feel robust!",
"You feel indestructible!",
)
var/static/list/finished_silicon_message = list(
"You feel nothing!",
"No pain, no gain!",
"Chassis hardness rating... Unchanged.",
"You feel the exact same. Nothing.",
)
/obj/structure/weightmachine/Initialize(mapload)
. = ..()
weight_action = new(src)
weight_action.weightpress = src
var/static/list/tool_behaviors
if(!tool_behaviors)
tool_behaviors = string_assoc_nested_list(list(
TOOL_CROWBAR = list(
SCREENTIP_CONTEXT_RMB = "Deconstruct",
),
TOOL_WRENCH = list(
SCREENTIP_CONTEXT_RMB = "Anchor",
),
))
AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
/obj/structure/weightmachine/Destroy()
QDEL_NULL(weight_action)
return ..()
/obj/structure/weightmachine/buckle_mob(mob/living/buckled, force, check_loc)
. = ..()
weight_action.Grant(buckled)
/obj/structure/weightmachine/unbuckle_mob(mob/living/buckled_mob, force, can_fall)
. = ..()
weight_action.Remove(buckled_mob)
/obj/structure/weightmachine/wrench_act_secondary(mob/living/user, obj/item/tool)
tool.play_tool_sound(src)
balloon_alert(user, anchored ? "unsecured" : "secured")
anchored = !anchored
return TRUE
/obj/structure/weightmachine/crowbar_act_secondary(mob/living/user, obj/item/tool)
if(anchored)
balloon_alert(user, "still secured!")
return FALSE
tool.play_tool_sound(src)
balloon_alert(user, "deconstructing...")
if (!do_after(user, 10 SECONDS, target = src))
return FALSE
new /obj/item/stack/sheet/iron(get_turf(src), 5)
new /obj/item/stack/rods(get_turf(src), 2)
new /obj/item/chair(get_turf(src))
qdel(src)
return TRUE
/obj/structure/weightmachine/proc/perform_workout(mob/living/user)
if(user.nutrition <= NUTRITION_LEVEL_STARVING)
user.balloon_alert(user, "too hungry to workout!")
return
user.balloon_alert_to_viewers("[pick(more_weight)]")
START_PROCESSING(SSobj, src)
if(do_after(user, 8 SECONDS, src) && user.has_gravity())
// with enough dedication, even clowns can overcome their handicaps
var/clumsy_chance = 30 - (user.mind.get_skill_level(/datum/skill/athletics) * 5)
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(clumsy_chance))
playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
to_chat(user, span_warning("Your hand slips, causing the [name] to smash you!"))
user.take_bodypart_damage(rand(2, 5))
end_workout()
return
// awlways a chance for a person not to fail horribly when drunk
if(user.get_drunk_amount() > SAFE_DRUNK_LEVEL && prob(min(user.get_drunk_amount(), 99)))
playsound(src,'sound/effects/bang.ogg', 50, TRUE)
to_chat(user, span_warning(drunk_message))
user.take_bodypart_damage(rand(5, 10), wound_bonus = 10)
end_workout()
return
if(issilicon(user))
user.balloon_alert(user, pick(finished_silicon_message))
else
user.balloon_alert(user, pick(finished_message))
user.adjust_nutrition(-5) // feel the burn
if(iscarbon(user))
var/gravity_modifier = user.has_gravity() > STANDARD_GRAVITY ? 2 : 1
// remember the real xp gain is from sleeping after working out
user.mind.adjust_experience(/datum/skill/athletics, WORKOUT_XP * gravity_modifier)
user.apply_status_effect(/datum/status_effect/exercised, EXERCISE_STATUS_DURATION)
end_workout()
/obj/structure/weightmachine/proc/end_workout()
playsound(src, 'sound/machines/click.ogg', 60, TRUE)
STOP_PROCESSING(SSobj, src)
icon_state = initial(icon_state)
/// roughly 8 seconds for 1 workout rep
#define WORKOUT_LENGTH 8
/obj/structure/weightmachine/process(seconds_per_tick)
if(!has_buckled_mobs())
end_workout()
return FALSE
var/mutable_appearance/workout = mutable_appearance(icon, "[base_icon_state]-o", ABOVE_MOB_LAYER)
flick_overlay_view(workout, 0.8 SECONDS)
flick("[base_icon_state]-u", src)
var/mob/living/user = buckled_mobs[1]
animate(user, pixel_y = pixel_shift_y, time = WORKOUT_LENGTH * 0.5)
playsound(user, 'sound/machines/creak.ogg', 60, TRUE)
animate(pixel_y = user.base_pixel_y, time = WORKOUT_LENGTH * 0.5)
if(!iscarbon(user) || isnull(user.mind))
return TRUE
var/affected_gravity = user.has_gravity()
if (!affected_gravity)
return TRUE // No weight? I could do this all day
var/gravity_modifier = affected_gravity > STANDARD_GRAVITY ? 0.75 : 1
// the amount of workouts you can do before you hit stamcrit
var/workout_reps = total_workout_reps[user.mind.get_skill_level(/datum/skill/athletics)] * gravity_modifier
// total stamina drain of 1 workout calculated based on the workout length
var/stamina_exhaustion = FLOOR(user.maxHealth / workout_reps / WORKOUT_LENGTH, 0.1)
if(HAS_TRAIT(user, TRAIT_STRENGTH)) //The strong get reductions to stamina damage taken while exercising
stamina_exhaustion *= 0.5
user.adjustStaminaLoss(stamina_exhaustion * seconds_per_tick)
return TRUE
#undef WORKOUT_LENGTH
/**
* Weight lifter subtype
*/
/obj/structure/weightmachine/weightlifter
name = "incline bench press"
icon_state = "benchpress"
base_icon_state = "benchpress"
pixel_shift_y = 5
drunk_message = "You raise the bar over you trying to balance it with one hand, keyword tried."
#undef WORKOUT_XP
#undef EXERCISE_STATUS_DURATION
#undef SAFE_DRUNK_LEVEL