Skip to content

Commit

Permalink
stained
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Atlas committed Nov 19, 2024
1 parent 538e0b1 commit 4a9a9a4
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 24 deletions.
9 changes: 8 additions & 1 deletion code/datums/skills/_skills.dm
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
/singleton/skill
/// The displayed name of the skill.
var/name
/// A description of this skill's effects.
/// A description of what this skill entails.
var/description
/// A detailed description of what a character should expect with their current level in this skill. Assoc list of skill level to string.
var/list/skill_level_descriptions = list(
SKILL_LEVEL_UNFAMILIAR = "You are clueless.",
SKILL_LEVEL_FAMILIAR = "You have read up on the subject or have prior real experience.",
SKILL_LEVEL_TRAINED = "You have received some degree of official training on the subject, whether through certifications or courses.",
SKILL_LEVEL_PROFESSIONAL = "You are an expert in this field, devoting many years of study or practice to it."
)
/// The maximum level someone with no education can reach in this skill. Typically, this should be FAMILIAR on occupational skills.
/// If null, then there is no cap.
var/uneducated_skill_cap
Expand Down
10 changes: 9 additions & 1 deletion code/datums/skills/combat/combat.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
/singleton/skill/unarmed_combat
name = "Unarmed Combat"
description = "punching things"
description = "Unarmed combat represents your training in hand-to-hand combat, or without a weapon."
skill_level_descriptions = list(
SKILL_LEVEL_UNFAMILIAR = "You have rarely, if ever, fought someone in your life. You are likely to crack under pressure, not land punches, and are physically \
a pushover in a real fight. Being shoved, grabbed, or moved is likely to be very dangerous for you.",
SKILL_LEVEL_FAMILIAR = "You have some experience throwing punches. You are no stranger to a close-quarters fight, though anyone with real training is likely to \
overwhelm you. You are not as likely to miss punches, and you are physically less likely to suffer being shoved, grabbed, or moved.",
SKILL_LEVEL_TRAINED = "You have been trained, whether by being in the military, taking close-quarters-combat classes or simply through experience, to both keep \
calm in a close-quarters fight and also fight well. You suffer no maluses to your close-quarters combat.",
)
category = /singleton/skill_category/combat
subcategory = SKILL_SUBCATEGORY_MELEE

Expand Down
3 changes: 3 additions & 0 deletions code/datums/skills/everyday/service.dm
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
/singleton/skill/mixing
name = "Mixing"
description = "valhalla skill"
maximum_level = SKILL_LEVEL_PROFESSIONAL
category = /singleton/skill_category/everyday
subcategory = SKILL_SUBCATEGORY_SERVICE

/singleton/skill/cooking
name = "Cooking"
description = "Cooking Mama"
maximum_level = SKILL_LEVEL_PROFESSIONAL
category = /singleton/skill_category/everyday
subcategory = SKILL_SUBCATEGORY_SERVICE

/singleton/skill/gardening
name = "Gardening"
description = "this is boring as shit"
maximum_level = SKILL_LEVEL_PROFESSIONAL
category = /singleton/skill_category/everyday
subcategory = SKILL_SUBCATEGORY_SERVICE
4 changes: 4 additions & 0 deletions code/datums/skills/occupational/engineering.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "Electrical Engineering"
description = "Electrical engineering has to do with anything that involves wires and electricity. This includes things such as hacking doors, machines, but also \
laying down wires, or repairing wiring damage in prosthetics."
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_ENGINEERING
Expand All @@ -10,6 +11,7 @@
name = "Mechanical Engineering"
description = "Mechanical engineering has to do with general construction of objects, walls, windows, and so on. It is also necessary for the usage of heavy machinery \
such as emitters."
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_ENGINEERING
Expand All @@ -18,6 +20,7 @@
name = "Atmospherics Systems"
description = "Atmospherics systems involves the usage of atmospherics tooling and machinery, such as powered pumps, certain settings on air alarms, pipe wrenches, \
pipe layers, and pipe construction."
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_ENGINEERING
Expand All @@ -26,6 +29,7 @@
name = "Reactor Systems"
description = "Reactor systems envelops anything used for reactors, such as the computers and gyrotrons for the INDRA. It is also necessary to correctly interpret information \
from reactor monitoring programs."
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_ENGINEERING
5 changes: 3 additions & 2 deletions code/datums/skills/occupational/medical.dm
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/singleton/skill/medicine
name = "Medicine"
description ="how to use health analyzers, ATKs, syringes"
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_MEDICAL

/singleton/skill/surgery
name = "Surgery"
description = "the one everyone wants to do in medical to be the protag"
uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_MEDICAL

Expand All @@ -24,6 +24,7 @@
/singleton/skill/anatomy
name = "Anatomy"
description = "this one lets you know what's wrong with people"
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_MEDICAL
Expand Down
1 change: 1 addition & 0 deletions code/datums/skills/occupational/science.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/singleton/skill/robotics
name = "Robotics"
description = "Robotics is well you know what the fuck it is man"
maximum_level = SKILL_LEVEL_PROFESSIONAL
uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
category = /singleton/skill_category/occupational
subcategory = SKILL_SUBCATEGORY_SCIENCE
Expand Down
9 changes: 9 additions & 0 deletions code/modules/background/education/security.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@
/singleton/skill/anatomy = SKILL_LEVEL_FAMILIAR,
/singleton/skill/forensics = SKILL_LEVEL_PROFESSIONAL
)

/singleton/education/protagonist
name = "Protagonist Degree"
description = "you are the protagonist of aurora"
skills = list(
/singleton/skill/unarmed_combat = SKILL_LEVEL_TRAINED,
/singleton/skill/armed_combat = SKILL_LEVEL_TRAINED,
/singleton/skill/firearms = SKILL_LEVEL_TRAINED
)
10 changes: 7 additions & 3 deletions code/modules/client/preference_setup/skills/skills.dm
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
var/current_level = pref.skills[skill.type]
var/maximum_skill_level = get_maximum_skill_level(skill, education)

for(var/i = SKILL_LEVEL_UNFAMILIAR, i <= SKILL_LEVEL_PROFESSIONAL, i++)
for(var/i = SKILL_LEVEL_UNFAMILIAR, i <= skill.maximum_level, i++)
dat += skill_to_button(skill, education, current_level, i, maximum_skill_level)

return JOINTEXT(dat)
Expand All @@ -136,10 +136,10 @@
var/base_maximum_level = skill.get_maximum_level(education)
var/remaining_skill_points = calculate_remaining_skill_points(GET_SINGLETON(skill.category))

for(var/skill_level = SKILL_LEVEL_UNFAMILIAR to base_maximum_level)
for(var/skill_level = 0 to base_maximum_level)
. = skill_level

var/skill_cost = skill.get_cost(skill_level)
var/skill_cost = skill.get_cost(skill_level + 1)
if(skill_cost > remaining_skill_points)
break

Expand Down Expand Up @@ -225,6 +225,10 @@
dat += "<hr>[skill_to_show.description]<br>"
if(skill_to_show.uneducated_skill_cap)
dat += "Without the relevant education, you may only reach the <b>[SSskills.skill_level_map[skill_to_show.uneducated_skill_cap]]</b> level.<br>"
dat += "<hr>"
var/skill_level = (skill_to_show.type in pref.skills) ? pref.skills[skill_to_show.type] : SKILL_LEVEL_UNFAMILIAR
dat += "Your current level in this skill is [SPAN_BOLD(SSskills.skill_level_map[skill_level])].<br>"
dat += SPAN_NOTICE("[skill_to_show.skill_level_descriptions[skill_level]]")
dat += "</html>"
skill_window.set_content(dat)
skill_window.open()
Expand Down
85 changes: 68 additions & 17 deletions code/modules/mob/living/carbon/human/human_attackhand.dm
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,19 @@
*/
if(prob(80))
hit_zone = ran_zone(hit_zone)
if(prob(15) && hit_zone != BP_CHEST) // Missed!

var/melee_skill_level = H.get_skill_level(/singleton/skill/unarmed_combat)
var/miss_chance = 15
switch(melee_skill_level)
if(SKILL_LEVEL_UNFAMILIAR)
miss_chance = 25
if(SKILL_LEVEL_FAMILIAR)
miss_chance = 20

var/skill_difference = H.get_skill_difference(src, /singleton/skill/unarmed_combat)
if(prob(miss_chance) && hit_zone != BP_CHEST) // Missed!
if(!src.lying)
attack_message = "[H] attempted to strike [src], but missed!"
attack_message = "[H] attempted to strike [src], [skill_difference < -1 ? "but [src] [pick("dodged", "ducked")] out of the way!" : "but missed!"]"
else
attack_message = "[H] attempted to strike [src], but [src.get_pronoun("he")] rolled out of the way!"
src.set_dir(pick(GLOB.cardinals))
Expand Down Expand Up @@ -305,6 +315,8 @@
to_chat(M, SPAN_NOTICE("You don't want to risk hurting [src]!"))
return FALSE

var/melee_skill_level = H.get_skill_level(/singleton/skill/unarmed_combat)
var/skill_difference = H.get_skill_difference(src, /singleton/skill/unarmed_combat)
var/disarm_cost
var/obj/item/organ/internal/cell/cell = M.internal_organs_by_name[BP_CELL]
var/obj/item/cell/potato
Expand All @@ -316,6 +328,9 @@
if(potato.charge < disarm_cost)
to_chat(M, SPAN_DANGER("You don't have enough charge to disarm someone!"))
return FALSE
// Skill difference will be negative if the opponent is stronger than us.
if(skill_difference < 0)
disarm_cost += -(skill_difference * 100)
potato.use(disarm_cost)
else
if(M.max_stamina > 0)
Expand All @@ -327,6 +342,9 @@
if(M.stamina <= disarm_cost)
to_chat(M, SPAN_DANGER("You're too tired to disarm someone!"))
return FALSE
// Skill difference will be negative if the opponent is stronger than us.
if(skill_difference < 0)
disarm_cost += -(skill_difference * 10)
M.stamina = clamp(M.stamina - disarm_cost, 0, M.max_stamina) // attempting to knock something out of someone's hands, or pushing them over, is exhausting!
else if(M.max_stamina <= 0)
disarm_cost = M.max_nutrition / 6
Expand All @@ -350,14 +368,18 @@
//See if they have any weapons to retaliate with
if(src.a_intent != I_HELP)
for(var/obj/item/W in holding)
if(W && prob(holding[W]))
if(!W)
continue

var/misfire_chance = holding[W] + -(skill_difference * 10)
if(W && prob(misfire_chance))
if(istype(W, /obj/item/grab))
var/obj/item/grab/G = W
if(G.affecting && G.affecting != M)
visible_message(SPAN_WARNING("[src] repositions \the [G.affecting] to block \the [M]'s disarm attempt!"), SPAN_NOTICE("You reposition \the [G.affecting] to block \the [M]'s disarm attempt!"))
G.attack_hand(M)
return
if(istype(W,/obj/item/gun))
if(istype(W, /obj/item/gun))
var/list/turfs = list()
for(var/turf/T in view())
turfs += T
Expand All @@ -367,7 +389,7 @@
return W.afterattack(target,src)
else
if(M.Adjacent(src))
visible_message(SPAN_DANGER("[src] retaliates against [M]'s disarm attempt with [W]!"))
visible_message(SPAN_DANGER("[src] retaliates against [M]'s [melee_skill_level == SKILL_LEVEL_UNFAMILIAR ? "inexperienced shoving" : "disarm attempt"] with [W]!"))
return M.attackby(W,src)

var/randn = rand(1, 100)
Expand Down Expand Up @@ -399,7 +421,9 @@
forceMove(GET_TURF_ABOVE(current_turf)) //We use GET_TURF_ABOVE so people can't cheese it by turning their sprite.
return

if(randn <= 25)
var/push_chance = 25
push_chance += -(skill_difference * 5)
if(randn <= push_chance)
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves/force))
apply_effect(6, WEAKEN)
playsound(loc, 'sound/weapons/push_connect.ogg', 50, 1, -1)
Expand All @@ -420,8 +444,10 @@
playsound(loc, 'sound/weapons/push.ogg', 50, 1, -1)
return

if(randn <= 60)
if(H.gloves && istype(H.gloves,/obj/item/clothing/gloves/force))
var/disarm_chance = 25
disarm_chance += -(skill_difference * 5)
if(randn <= disarm_chance)
if(H.gloves && istype(H.gloves, /obj/item/clothing/gloves/force))
playsound(loc, 'sound/weapons/push_connect.ogg', 50, 1, -1)
visible_message(SPAN_DANGER("[M] shoves, sending [src] flying!"))
step_away(src,M,15)
Expand Down Expand Up @@ -451,12 +477,13 @@
//No return here is intentional, as it will then try to disarm other items, and/or play a failed disarm message

playsound(loc, /singleton/sound_category/punchmiss_sound, 25, 1, -1)
visible_message(SPAN_DANGER("[M] attempted to disarm [src]!"))
visible_message(SPAN_DANGER("[M] [melee_skill_level == SKILL_LEVEL_UNFAMILIAR ? "[pick("inexpertly", "clumsily")] disarm" : "disarm"] attempted to disarm [src]!"))
return

/mob/living/carbon/human/proc/cpr(mob/living/carbon/human/H, var/starting = FALSE, var/cpr_mode)
var/obj/item/main_hand = H.get_active_hand()
var/obj/item/off_hand = H.get_inactive_hand()
var/medicine_skill = H.get_skill_level(/singleton/skill/medicine)
if(istype(main_hand) || istype(off_hand))
cpr = FALSE
to_chat(H, SPAN_NOTICE("You cannot perform CPR with anything in your hands."))
Expand All @@ -475,7 +502,12 @@
if(!cpr_mode)
cpr = FALSE
return
to_chat(H, SPAN_NOTICE("You begin performing [cpr_mode] on \the [src]."))

var/cpr_attempt_message = SPAN_DANGER("You have no idea how to perform CPR on \the [src]... but you're going to try your best!")
if(medicine_skill > SKILL_LEVEL_UNFAMILIAR)
cpr_attempt_message = SPAN_NOTICE("You begin performing [cpr_mode] on \the [src].")

to_chat(H, cpr_attempt_message)

H.do_attack_animation(src, null, image('icons/mob/screen/generic.dmi', src, "cpr", src.layer + 1))
var/starting_pixel_y = pixel_y
Expand All @@ -487,23 +519,31 @@
cpr = FALSE //If it cancelled, cancel it. Simple.

if(cpr_mode == "Full CPR")
cpr_compressions(H)
cpr_ventilation(H)
cpr_compressions(H, medicine_skill)
cpr_ventilation(H, medicine_skill)

if(cpr_mode == "Compressions")
cpr_compressions(H)
cpr_compressions(H, medicine_skill)

if(cpr_mode == "Mouth-to-Mouth")
cpr_ventilation(H)
cpr_ventilation(H, medicine_skill)

cpr(H, FALSE, cpr_mode) //Again.

/mob/living/carbon/human/proc/cpr_compressions(mob/living/carbon/human/H)
/mob/living/carbon/human/proc/cpr_compressions(mob/living/carbon/human/H, medicine_skill)
if(is_asystole())
if(prob(5 * rand(2, 3)))
var/break_probability = 5 * rand(2,3)
if(medicine_skill < SKILL_LEVEL_FAMILIAR)
break_probability *= 3
else if(medicine_skill < SKILL_LEVEL_TRAINED)
break_probability *= 1.5

if(prob(break_probability))
var/obj/item/organ/external/chest = get_organ(BP_CHEST)
if(chest)
chest.fracture()
if(medicine_skill < SKILL_LEVEL_FAMILIAR)
to_chat(H, FONT_HUGE(SPAN_DANGER("Something crunches under your hands...!")))

var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
if(heart)
Expand All @@ -512,7 +552,7 @@
if(stat != DEAD && prob(10 * rand(0.5, 1)))
resuscitate()

/mob/living/carbon/human/proc/cpr_ventilation(mob/living/carbon/human/H)
/mob/living/carbon/human/proc/cpr_ventilation(mob/living/carbon/human/H, medicine_skill)
if(!H.check_has_mouth())
to_chat(H, SPAN_WARNING("You don't have a mouth, you cannot do mouth-to-mouth resuscitation!"))
return
Expand All @@ -534,6 +574,17 @@
if(L)
var/datum/gas_mixture/breath = H.get_breath_from_environment()
var/fail = L.handle_breath(breath, 1)

if(medicine_skill == SKILL_LEVEL_UNFAMILIAR)
fail = prob(80)
if(fail)
to_chat(H, SPAN_DANGER("No... that wasn't how you do it!"))

else if(medicine_skill == SKILL_LEVEL_UNFAMILIAR)
fail = prob(90)
if(fail)
to_chat(H, SPAN_NOTICE("You were just slightly off!"))

if(!fail)
if(!L.is_bruised() || (L.is_bruised() && L.rescued))
losebreath = 0
Expand Down
6 changes: 6 additions & 0 deletions code/modules/mob/skills/skills.dm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
/mob/proc/skill_check(skill_type, skill_level_needed)
return get_skill_level(skill_type) >= skill_level_needed

/**
* Gets the skill difference in a given skill between two mobs.
*/
/mob/proc/get_skill_difference(mob/opponent, skill_type)
return get_skill_level(skill_type) - opponent.get_skill_level(skill_type)

/**
* Returns a multiplier based on the mob's skill. Takes a skill type and a minimum skill floor at least.
* Bonus and malus are the modifiers added or removed for each skill level of difference from the required skill floor.
Expand Down

0 comments on commit 4a9a9a4

Please sign in to comment.