diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm
index f8f33c46644..96f0a7e023f 100644
--- a/code/__DEFINES/achievements.dm
+++ b/code/__DEFINES/achievements.dm
@@ -122,6 +122,9 @@
// DB ID for intento score
#define INTENTO_SCORE "Intento Score"
+// DB ID for style point count
+#define STYLE_SCORE "Style Score"
+
// Tourist related achievements and scores
//centcom grades (achievement)
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index 06f1850353c..68e7cdd11e2 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -139,3 +139,25 @@
/// From /mob/living/unfriend() : (mob/living/old_friend)
#define COMSIG_LIVING_UNFRIENDED "living_unfriended"
+
+/// From /obj/effect/temp_visual/resonance/burst() : (mob/creator, mob/living/hit_living)
+#define COMSIG_LIVING_RESONATOR_BURST "living_resonator_burst"
+
+/// From /obj/projectile/attempt_parry() : (obj/projectile/parried_projectile)
+#define COMSIG_LIVING_PROJECTILE_PARRYING "living_projectile_parrying"
+ /// Return to allow the parry to happen
+ #define ALLOW_PARRY (1<<0)
+
+/// From /obj/projectile/on_parry() : (obj/projectile/parried_projectile)
+#define COMSIG_LIVING_PROJECTILE_PARRIED "living_projectile_parried"
+ /// Return to prevent the projectile from executing any code in on_parry()
+ #define INTERCEPT_PARRY_EFFECTS (1<<0)
+
+/// From /turf/closed/mineral/gibtonite/defuse() : (det_time)
+#define COMSIG_LIVING_DEFUSED_GIBTONITE "living_defused_gibtonite"
+
+/// From /obj/item/kinetic_crusher/afterattack() : (mob/living/target, obj/item/kinetic_crusher/crusher, backstabbed)
+#define COMSIG_LIVING_CRUSHER_DETONATE "living_crusher_detonate"
+
+/// From /obj/structure/geyser/attackby() : (obj/structure/geyser/geyser)
+#define COMSIG_LIVING_DISCOVERED_GEYSER "living_discovered_geyser"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index 1c289acbf69..f652f75a341 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -173,6 +173,8 @@
#define COMSIG_MOB_AUTOMUTE_CHECK "client_automute_check" // The check is performed by the client.
/// Prevents the automute system checking this client for repeated messages.
#define WAIVE_AUTOMUTE_CHECK (1<<0)
+///From base of /turf/closed/mineral/proc/gets_drilled(): (turf/closed/mineral/rock, give_exp)
+#define COMSIG_MOB_MINED "mob_mined"
///from living/flash_act(), when a mob is successfully flashed.
#define COMSIG_MOB_FLASHED "mob_flashed"
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index a5ed899e17f..7438c78f4d2 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -249,6 +249,7 @@
#define CRIT_LAYER 5
#define CURSE_LAYER 6
#define ECHO_LAYER 7
+#define PARRY_LAYER 8
#define FOV_EFFECT_LAYER 100
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index 79e2e4e1411..afedfa5befe 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -118,6 +118,9 @@
icon_state = "oxydamageoverlay"
layer = BLIND_LAYER
+/atom/movable/screen/fullscreen/crit/projectile_parry
+ layer = PARRY_LAYER
+
/atom/movable/screen/fullscreen/blind
icon_state = "blackimageoverlay"
layer = BLIND_LAYER
diff --git a/code/datums/achievements/misc_scores.dm b/code/datums/achievements/misc_scores.dm
index 067cd68d7df..b91f46008de 100644
--- a/code/datums/achievements/misc_scores.dm
+++ b/code/datums/achievements/misc_scores.dm
@@ -15,3 +15,9 @@
name = "Intento Score"
desc = "A blast from the future?"
database_id = INTENTO_SCORE
+
+/// What's the highest amount of style points we've gotten?
+/datum/award/score/style_score
+ name = "Style Score"
+ desc = "You might not be a robot, but you were damn close."
+ database_id = STYLE_SCORE
diff --git a/code/datums/components/parry.dm b/code/datums/components/parry.dm
new file mode 100644
index 00000000000..fedfcb77d15
--- /dev/null
+++ b/code/datums/components/parry.dm
@@ -0,0 +1,38 @@
+/// Add to a living mob to allow them to "parry" projectiles by clicking on their tile, sending them back at the firer.
+/datum/component/projectile_parry
+ /// typecache of valid projectiles to be able to parry
+ var/list/parryable_projectiles
+
+
+/datum/component/projectile_parry/Initialize(list/projectiles_to_parry)
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ parryable_projectiles = typecacheof(projectiles_to_parry)
+
+
+/datum/component/projectile_parry/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRYING, PROC_REF(parrying_projectile))
+ RegisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRIED, PROC_REF(parried_projectile))
+
+
+/datum/component/projectile_parry/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_LIVING_PROJECTILE_PARRYING, COMSIG_LIVING_PROJECTILE_PARRIED))
+
+
+/datum/component/projectile_parry/proc/parrying_projectile(datum/source, obj/projectile/parried_projectile)
+ SIGNAL_HANDLER
+
+ if(is_type_in_typecache(parried_projectile, parryable_projectiles))
+ return ALLOW_PARRY
+
+
+/datum/component/projectile_parry/proc/parried_projectile(datum/source, obj/projectile/parried_projectile)
+ SIGNAL_HANDLER
+
+ var/mob/living/living_parent = parent
+
+ living_parent.playsound_local(get_turf(parried_projectile), 'sound/effects/parry.ogg', 50, TRUE)
+ living_parent.overlay_fullscreen("projectile_parry", /atom/movable/screen/fullscreen/crit/projectile_parry, 2)
+ addtimer(CALLBACK(living_parent, TYPE_PROC_REF(/mob, clear_fullscreen), "projectile_parry"), 0.25 SECONDS)
+ living_parent.visible_message(span_warning("[living_parent] expertly parries [parried_projectile] with [living_parent.p_their()] bare hand!"), span_warning("You parry [parried_projectile] with your hand!"))
diff --git a/code/datums/components/style/style.dm b/code/datums/components/style/style.dm
new file mode 100644
index 00000000000..fa40df7ef10
--- /dev/null
+++ b/code/datums/components/style/style.dm
@@ -0,0 +1,496 @@
+#define STYLE_DULL 1
+#define STYLE_COOL 2
+#define STYLE_BRUTAL 3
+#define STYLE_ABSOLUTE 4
+#define STYLE_SPACED 5
+
+#define ACTION_KILL "KILL"
+#define ACTION_MINOR_KILL "MINOR KILL"
+#define ACTION_MAJOR_KILL "MAJOR KILL"
+#define ACTION_DISRESPECT "DISRESPECT"
+#define ACTION_MELEED "MELEE'D"
+#define ACTION_ROCK_MINED "ROCK MINED"
+#define ACTION_ORE_MINED "ORE MINED"
+#define ACTION_TRAPPER "TRAPPER"
+#define ACTION_PARRIED "PARRIED"
+#define ACTION_PROJECTILE_BOOST "PROJECTILE BOOST"
+#define ACTION_GIBTONITE_HIT "GIBTONITE HIT"
+#define ACTION_GIBTONITE_BOOM "GIBTONITE BOOM"
+#define ACTION_GIBTONITE_DEFUSED "GIBTONITE DEFUSED"
+#define ACTION_MARK_DETONATED "MARK DETONATED"
+#define ACTION_GEYSER_MARKED "GEYSER MARKED"
+
+/datum/component/style
+ /// Amount of style we have.
+ var/style_points = -1
+ /// Our style point multiplier.
+ var/point_multiplier = 1
+ /// The current rank we have.
+ var/rank = STYLE_DULL
+ /// The last point affecting actions we've done
+ var/list/actions = list()
+ /// The style meter shown on screen.
+ var/atom/movable/screen/style_meter_background/meter
+ /// The image of the style meter.
+ var/atom/movable/screen/style_meter/meter_image
+ /// The timer for meter updating
+ var/timerid
+ /// Highest score attained by this component, to avoid as much overhead when considering to award a high score to the client
+ var/high_score = 0
+ /// Weakref to the added projectile parry component
+ var/datum/weakref/projectile_parry
+ /// What rank, minimum, the user needs to be to hotswap items
+ var/hotswap_rank = STYLE_BRUTAL
+ /// If this is multitooled, making it make funny noises on the user's rank going up
+ var/multitooled = FALSE
+ /// A static list of lists of all the possible sounds to play when multitooled, in numerical order
+ var/static/list/rankup_sounds = list(
+ list(
+ 'sound/items/style/combo_dull1.ogg',
+ 'sound/items/style/combo_dull2.ogg',
+ 'sound/items/style/combo_dull3.ogg',
+ ),
+ list(
+ 'sound/items/style/combo_cool1.ogg',
+ 'sound/items/style/combo_cool2.ogg',
+ 'sound/items/style/combo_cool3.ogg',
+ ),
+ list(
+ 'sound/items/style/combo_brutal1.ogg',
+ 'sound/items/style/combo_brutal2.ogg',
+ 'sound/items/style/combo_brutal3.ogg',
+ ),
+ list(
+ 'sound/items/style/combo_absolute1.ogg',
+ 'sound/items/style/combo_absolute2.ogg',
+ 'sound/items/style/combo_absolute3.ogg',
+ ),
+ list(
+ 'sound/items/style/combo_spaced1.ogg',
+ 'sound/items/style/combo_spaced2.ogg',
+ ),
+ )
+
+
+/datum/component/style/Initialize(multitooled = FALSE)
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ var/mob/mob_parent = parent
+
+ meter = new()
+ meter_image = new()
+ meter.vis_contents += meter_image
+ meter_image.add_filter("meter_mask", 1, list(type = "alpha", icon = icon('icons/hud/style_meter.dmi', "style_meter"), flags = MASK_INVERSE))
+ meter.update_appearance()
+ meter_image.update_appearance()
+
+ update_screen()
+
+ if(mob_parent.hud_used)
+ mob_parent.hud_used.static_inventory += meter
+ mob_parent.hud_used.show_hud(mob_parent.hud_used.hud_version)
+
+ START_PROCESSING(SSdcs, src)
+
+ if(multitooled)
+ src.multitooled = multitooled
+
+ RegisterSignal(src, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(on_parent_multitool))
+
+/datum/component/style/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_MOB_ITEM_AFTERATTACK, PROC_REF(hotswap))
+ RegisterSignal(parent, COMSIG_MOB_MINED, PROC_REF(on_mine))
+ RegisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_take_damage))
+ RegisterSignal(parent, COMSIG_MOB_EMOTED("flip"), PROC_REF(on_flip))
+ RegisterSignal(parent, COMSIG_MOB_EMOTED("spin"), PROC_REF(on_spin))
+ RegisterSignal(parent, COMSIG_MOB_ITEM_ATTACK, PROC_REF(on_attack))
+ RegisterSignal(parent, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, PROC_REF(on_punch))
+ RegisterSignal(SSdcs, COMSIG_GLOB_MOB_DEATH, PROC_REF(on_death))
+ RegisterSignal(parent, COMSIG_LIVING_RESONATOR_BURST, PROC_REF(on_resonator_burst))
+ RegisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRIED, PROC_REF(on_projectile_parry))
+ RegisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE, PROC_REF(on_gibtonite_defuse))
+ RegisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE, PROC_REF(on_crusher_detonate))
+ RegisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER, PROC_REF(on_geyser_discover))
+
+ projectile_parry = WEAKREF(AddComponent(\
+ /datum/component/projectile_parry,\
+ list(\
+ /obj/projectile/colossus,\
+ /obj/projectile/temp/basilisk,\
+ /obj/projectile/kinetic,\
+ /obj/projectile/bileworm_acid,\
+ /obj/projectile/herald,\
+ )\
+ )
+ )
+
+
+/datum/component/style/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_MOB_ITEM_AFTERATTACK)
+ UnregisterSignal(parent, COMSIG_MOB_MINED)
+ UnregisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE)
+ UnregisterSignal(parent, list(COMSIG_MOB_EMOTED("flip"), COMSIG_MOB_EMOTED("spin")))
+ UnregisterSignal(parent, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_HUMAN_MELEE_UNARMED_ATTACK))
+ UnregisterSignal(SSdcs, COMSIG_GLOB_MOB_DEATH)
+ UnregisterSignal(parent, COMSIG_LIVING_RESONATOR_BURST)
+ UnregisterSignal(parent, COMSIG_LIVING_PROJECTILE_PARRIED)
+ UnregisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE)
+ UnregisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE)
+ UnregisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER)
+
+ if(projectile_parry)
+ qdel(projectile_parry.resolve())
+
+
+/datum/component/style/Destroy(force, silent)
+ STOP_PROCESSING(SSdcs, src)
+ var/mob/mob_parent = parent
+ if(mob_parent.hud_used)
+ mob_parent.hud_used.static_inventory -= meter
+ mob_parent.hud_used.show_hud(mob_parent.hud_used.hud_version)
+ return ..()
+
+
+/datum/component/style/process(seconds_per_tick)
+ point_multiplier = round(max(point_multiplier - 0.2 * seconds_per_tick, 1), 0.1)
+ change_points(-5 * seconds_per_tick * ROUND_UP((style_points + 1) / 200), use_multiplier = FALSE)
+ update_screen()
+
+
+
+/datum/component/style/proc/add_action(action, amount)
+ if(length(actions) > 9)
+ actions.Cut(1, 2)
+ if(length(actions))
+ var/last_action = actions[length(actions)]
+ if(action == actions[last_action])
+ amount *= 0.5
+ var/id
+ while(!id || (id in actions))
+ id = "action[rand(1, 1000)]"
+ actions[id] = action
+ change_points(amount)
+ addtimer(CALLBACK(src, PROC_REF(remove_action), id), 10 SECONDS)
+
+/datum/component/style/proc/remove_action(action_id)
+ actions -= action_id
+ update_screen()
+
+/datum/component/style/proc/change_points(amount, use_multiplier = TRUE)
+ if(!amount)
+ return
+
+ var/modified_amount = amount * (amount > 0 ? 1 - 0.1 * rank : 1) * (use_multiplier ? point_multiplier : 1)
+ style_points = max(style_points + modified_amount, -1)
+ update_screen()
+
+ if(style_points > high_score)
+ var/mob/mob_parent = parent
+ if((mob_parent.flags_1 & ADMIN_SPAWNED_1) || (mob_parent.datum_flags & DF_VAR_EDITED) || (datum_flags & DF_VAR_EDITED) || !SSachievements.achievements_enabled || !mob_parent.client) // No varediting/spawning this or the owner
+ return
+
+ var/award_status = mob_parent.client.get_award_status(/datum/award/score/style_score)
+
+ if(award_status >= style_points)
+ high_score = style_points
+ return
+
+ mob_parent.client.give_award(/datum/award/score/style_score, mob_parent, style_points - award_status)
+ high_score = style_points
+
+/datum/component/style/proc/update_screen(rank_changed)
+ var/go_back = null
+ if(!isnull(rank_changed))
+ timerid = null
+ if(rank_changed == point_to_rank())
+ go_back = rank > rank_changed ? 100 : 0
+
+ if(multitooled && (rank_changed > rank)) // make funny noises
+ var/mob/mob_parent = parent
+ mob_parent.playsound_local(get_turf(mob_parent), pick(rankup_sounds[rank_changed]), 70, vary = FALSE)
+
+ if((rank < hotswap_rank) && (rank_changed >= hotswap_rank))
+ var/mob/mob_parent = parent
+ mob_parent.balloon_alert(mob_parent, "hotswapping enabled")
+
+ else if((rank >= hotswap_rank) && (rank_changed < hotswap_rank))
+ var/mob/mob_parent = parent
+ mob_parent.balloon_alert(mob_parent, "hotswapping disabled")
+
+ rank = rank_changed
+ meter.maptext = "[format_rank_string(rank)][generate_multiplier()][generate_actions()]"
+ meter.maptext_y = 100 - 9 * length(actions)
+ update_meter(point_to_rank(), go_back)
+
+/datum/component/style/proc/update_meter(new_rank, go_back)
+ if(!isnull(go_back))
+ animate(meter_image.get_filter("meter_mask"), time = 0 SECONDS, flags = ANIMATION_END_NOW, x = go_back)
+ animate(meter_image.get_filter("meter_mask"), time = 1 SECONDS, x = (rank > new_rank ? 0 : (rank < new_rank ? 100 : (style_points % 100) + 1)))
+ if(!isnull(new_rank) && new_rank != rank && !timerid)
+ timerid = addtimer(CALLBACK(src, PROC_REF(update_screen), new_rank), 1 SECONDS)
+
+/datum/component/style/proc/rank_to_color(new_rank)
+ switch(new_rank)
+ if(STYLE_DULL)
+ return "#aaaaaa"
+ if(STYLE_COOL)
+ return "#aaaaff"
+ if(STYLE_BRUTAL)
+ return "#aaffff"
+ if(STYLE_ABSOLUTE)
+ return "#66ffff"
+ if(STYLE_SPACED)
+ return "#ffaa00"
+
+/datum/component/style/proc/point_to_rank()
+ switch(style_points)
+ if(-1 to 99)
+ return STYLE_DULL
+ if(100 to 199)
+ return STYLE_COOL
+ if(200 to 299)
+ return STYLE_BRUTAL
+ if(300 to 399)
+ return STYLE_ABSOLUTE
+ if(400 to INFINITY)
+ return STYLE_SPACED
+
+
+/datum/component/style/proc/rank_to_string(new_rank)
+ switch(new_rank)
+ if(STYLE_DULL)
+ return "DULL"
+ if(STYLE_COOL)
+ return "COOL"
+ if(STYLE_BRUTAL)
+ return "BRUTAL"
+ if(STYLE_ABSOLUTE)
+ return "ABSOLUTE"
+ if(STYLE_SPACED)
+ return "SPACED!"
+
+/datum/component/style/proc/format_rank_string(new_rank)
+ var/rank_string = rank_to_string(new_rank)
+ var/final_string = ""
+ final_string += "[rank_string[1]]"
+ final_string += "[copytext(rank_string, 2)]"
+ return final_string
+
+/datum/component/style/proc/generate_multiplier()
+ return "
MULTIPLIER: [point_multiplier]X"
+
+/datum/component/style/proc/generate_actions()
+ var/action_string = ""
+ for(var/action in actions)
+ action_string += "
+ [actions[action]]"
+ return action_string
+
+/datum/component/style/proc/action_to_color(action)
+ switch(action)
+ if(ACTION_KILL)
+ return "#ff0000"
+ if(ACTION_MINOR_KILL)
+ return "#ff6666"
+ if(ACTION_MAJOR_KILL)
+ return "#ffaa00"
+ if(ACTION_DISRESPECT)
+ return "#990000"
+ if(ACTION_MELEED)
+ return "#660033"
+ if(ACTION_ROCK_MINED)
+ return "#664433"
+ if(ACTION_ORE_MINED)
+ return "#663366"
+ if(ACTION_TRAPPER)
+ return "#363366"
+ if(ACTION_PARRIED)
+ return "#591324"
+ if(ACTION_PROJECTILE_BOOST)
+ return "#80112c"
+ if(ACTION_GIBTONITE_HIT)
+ return "#201d40"
+ if(ACTION_GIBTONITE_BOOM)
+ return "#1e176e"
+ if(ACTION_GIBTONITE_DEFUSED)
+ return "#2b2573"
+ if(ACTION_MARK_DETONATED)
+ return "#ac870e"
+ if(ACTION_GEYSER_MARKED)
+ return "#364866"
+
+/// A proc that lets a user, when their rank >= `hotswap_rank`, swap items in storage with what's in their hands, simply by clicking on the stored item with a held item
+/datum/component/style/proc/hotswap(mob/living/source, atom/target, obj/item/weapon, proximity_flag, click_parameters)
+ SIGNAL_HANDLER
+
+ if((rank < hotswap_rank) || !isitem(target) || !(target in source.get_all_contents()))
+ return
+
+ var/obj/item/item_target = target
+
+ if(!(item_target.item_flags & IN_STORAGE))
+ return
+
+ var/datum/storage/atom_storage = item_target.loc.atom_storage
+
+ if(!atom_storage.can_insert(weapon, source, messages = FALSE))
+ source.balloon_alert(source, "unable to hotswap!")
+ return
+
+ atom_storage.attempt_insert(weapon, source, override = TRUE)
+ INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, put_in_hands), target)
+ source.visible_message(span_notice("[source] quickly swaps [weapon] out with [target]!"), span_notice("You quickly swap [weapon] with [target]."))
+
+
+/datum/component/style/proc/on_parent_multitool(datum/source, mob/living/user, obj/item/tool, list/recipes)
+ multitooled = !multitooled
+ user.balloon_alert(user, "meter [multitooled ? "" : "un"]hacked")
+
+
+// Point givers
+/datum/component/style/proc/on_punch(mob/living/carbon/human/punching_person, atom/attacked_atom, proximity)
+ SIGNAL_HANDLER
+
+ if(!proximity || !punching_person.combat_mode || !isliving(attacked_atom))
+ return
+
+ var/mob/living/disrespected = attacked_atom
+ if(disrespected.stat || faction_check(punching_person.faction, disrespected.faction) || !(FACTION_MINING in disrespected.faction))
+ return
+
+ add_action(ACTION_DISRESPECT, 60 * (ismegafauna(disrespected) ? 2 : 1))
+
+/datum/component/style/proc/on_attack(mob/living/attacking_person, mob/living/attacked_mob)
+ SIGNAL_HANDLER
+
+ if(!istype(attacked_mob) || attacked_mob.stat)
+ return
+
+ var/mob/living/attacked = attacked_mob
+ var/mob/mob_parent = parent
+ if(faction_check(attacking_person.faction, attacked.faction) || !(FACTION_MINING in attacked.faction) || (istype(mob_parent.get_active_held_item(), /obj/item/kinetic_crusher) && attacked.has_status_effect(/datum/status_effect/crusher_mark)))
+ return
+
+ add_action(ACTION_MELEED, 50 * (ismegafauna(attacked) ? 1.5 : 1))
+
+/datum/component/style/proc/on_mine(datum/source, turf/closed/mineral/rock, give_exp)
+ SIGNAL_HANDLER
+
+ if(istype(rock, /turf/closed/mineral/gibtonite))
+ var/turf/closed/mineral/gibtonite/gib_rock = rock
+ switch(gib_rock.stage)
+ if(GIBTONITE_UNSTRUCK)
+ add_action(ACTION_GIBTONITE_HIT, 40)
+ return
+
+ if(GIBTONITE_ACTIVE)
+ add_action(ACTION_GIBTONITE_BOOM, 50)
+ return
+
+ if(GIBTONITE_DETONATE)
+ add_action(ACTION_GIBTONITE_BOOM, 50)
+ return
+
+ if(rock.mineralType)
+ if(give_exp)
+ add_action(ACTION_ORE_MINED, 40)
+ rock.mineralAmt = ROUND_UP(rock.mineralAmt * (1 + ((rank * 0.1) - 0.3))) // You start out getting 20% less ore, but it goes up to 20% more at S-tier
+
+ else if(give_exp)
+ add_action(ACTION_ROCK_MINED, 25)
+
+/datum/component/style/proc/on_resonator_burst(datum/source, mob/creator, mob/living/hit_living)
+ SIGNAL_HANDLER
+
+ if(faction_check(creator.faction, hit_living.faction) || (hit_living.stat != CONSCIOUS) || !(FACTION_MINING in hit_living.faction))
+ return
+
+ add_action(ACTION_TRAPPER, 70)
+
+/datum/component/style/proc/on_projectile_parry(datum/source, obj/projectile/parried)
+ SIGNAL_HANDLER
+
+ if(parried.firer == parent)
+ add_action(ACTION_PROJECTILE_BOOST, 160) // This is genuinely really impressive
+ else
+ add_action(ACTION_PARRIED, 110)
+
+/datum/component/style/proc/on_gibtonite_defuse(datum/source, det_time)
+ SIGNAL_HANDLER
+
+ add_action(ACTION_GIBTONITE_DEFUSED, min(40, 20 * (10 - det_time))) // 40 to 180 points depending on speed
+
+/datum/component/style/proc/on_crusher_detonate(datum/source, mob/living/target, obj/item/kinetic_crusher/crusher, backstabbed)
+ SIGNAL_HANDLER
+
+ var/has_brimdemon_trophy = locate(/obj/item/crusher_trophy/brimdemon_fang) in crusher.trophies
+
+ add_action(ACTION_MARK_DETONATED, round((backstabbed ? 60 : 30) * (ismegafauna(target) ? 1.5 : 1) * (has_brimdemon_trophy ? 1.25 : 1)))
+
+
+/datum/component/style/proc/on_geyser_discover(datum/source, obj/structure/geyser/geyser)
+ SIGNAL_HANDLER
+
+ add_action(ACTION_GEYSER_MARKED, 100)
+
+
+// Emote-based multipliers
+/datum/component/style/proc/on_flip()
+ SIGNAL_HANDLER
+
+ point_multiplier = round(min(point_multiplier + 0.5, 3), 0.1)
+ update_screen()
+
+/datum/component/style/proc/on_spin()
+ SIGNAL_HANDLER
+
+ point_multiplier = round(min(point_multiplier + 0.3, 3), 0.1)
+ update_screen()
+
+
+// Negative effects
+/datum/component/style/proc/on_take_damage()
+ SIGNAL_HANDLER
+
+ point_multiplier = round(max(point_multiplier - 0.3, 1), 0.1)
+ change_points(-30, use_multiplier = FALSE)
+
+/datum/component/style/proc/on_death(datum/source, mob/living/died, gibbed)
+ SIGNAL_HANDLER
+
+ var/mob/mob_parent = parent
+ if(died == parent)
+ change_points(-500, use_multiplier = FALSE)
+ return
+ else if(faction_check(mob_parent.faction, died.faction) || !(FACTION_MINING in died.faction) || (died.z != mob_parent.z) || !(died in view(mob_parent.client?.view, get_turf(mob_parent))))
+ return
+ if(ismegafauna(died))
+ add_action(ACTION_MAJOR_KILL, 350)
+
+ else if(died.maxHealth >= 75) //at least legions
+ add_action(ACTION_KILL, 125)
+
+ else if(died.maxHealth >= 30) //at least goliath children, dont count legion skulls
+ add_action(ACTION_MINOR_KILL, 75)
+
+#undef STYLE_DULL
+#undef STYLE_COOL
+#undef STYLE_BRUTAL
+#undef STYLE_ABSOLUTE
+#undef STYLE_SPACED
+
+#undef ACTION_KILL
+#undef ACTION_MINOR_KILL
+#undef ACTION_MAJOR_KILL
+#undef ACTION_DISRESPECT
+#undef ACTION_MELEED
+#undef ACTION_ROCK_MINED
+#undef ACTION_ORE_MINED
+#undef ACTION_TRAPPER
+#undef ACTION_PARRIED
+#undef ACTION_PROJECTILE_BOOST
+#undef ACTION_GIBTONITE_HIT
+#undef ACTION_GIBTONITE_BOOM
+#undef ACTION_GIBTONITE_DEFUSED
+#undef ACTION_MARK_DETONATED
+#undef ACTION_GEYSER_MARKED
diff --git a/code/datums/components/style/style_meter.dm b/code/datums/components/style/style_meter.dm
new file mode 100644
index 00000000000..2b0d4871cca
--- /dev/null
+++ b/code/datums/components/style/style_meter.dm
@@ -0,0 +1,139 @@
+/obj/item/style_meter
+ name = "style meter attachment"
+ desc = "Attach this to a pair of glasses to install a style meter system in them. \
+ You get style points from performing stylish acts and lose them for breaking your style. \
+ The style affects the quality of your mining, with you being able to mine ore better during a good chain. \
+ A responsive data HUD gives you the ability to reflect lavaland-based projectiles by punching them with an empty hand. \
+ In addition, at high style, you are able to swap an item in your hand with one in your backpack by hitting one with another."
+ icon_state = "style_meter"
+ icon = 'icons/obj/clothing/glasses.dmi'
+ /// The style meter component we give.
+ var/datum/component/style/style_meter
+ /// Mutable appearance added to the attached glasses
+ var/mutable_appearance/meter_appearance
+ /// If this is multitooled, which is passed onto the component on-creation, if one doesn't currently exist
+ var/multitooled = FALSE
+
+/obj/item/style_meter/Initialize(mapload)
+ . = ..()
+ meter_appearance = mutable_appearance(icon, icon_state)
+ RegisterSignal(src, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(on_multitool))
+
+/obj/item/style_meter/Destroy(force)
+ if(istype(loc, /obj/item/clothing/glasses))
+ clean_up(loc)
+ return ..()
+
+/obj/item/style_meter/examine(mob/user)
+ . = ..()
+ . += span_notice("You feel like a multitool could be used on this.")
+
+/obj/item/style_meter/afterattack(atom/movable/attacked_atom, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!istype(attacked_atom, /obj/item/clothing/glasses))
+ return
+
+ forceMove(attacked_atom)
+ attacked_atom.add_overlay(meter_appearance)
+ RegisterSignal(attacked_atom, COMSIG_ITEM_EQUIPPED, PROC_REF(check_wearing))
+ RegisterSignal(attacked_atom, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
+ RegisterSignal(attacked_atom, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(attacked_atom, COMSIG_CLICK_ALT, PROC_REF(on_altclick))
+ RegisterSignal(attacked_atom, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(on_multitool))
+ balloon_alert(user, "style meter attached")
+ playsound(src, 'sound/machines/click.ogg', 30, TRUE)
+ if(!iscarbon(attacked_atom.loc))
+ return
+
+ var/mob/living/carbon/carbon_wearer = attacked_atom.loc
+ if(carbon_wearer.glasses != attacked_atom)
+ return
+
+ style_meter = carbon_wearer.AddComponent(/datum/component/style, multitooled)
+
+/obj/item/style_meter/Moved(atom/old_loc, Dir, momentum_change)
+ . = ..()
+ if(!istype(old_loc, /obj/item/clothing/glasses))
+ return
+ clean_up(old_loc)
+
+
+/// Check if the glasses that this meter is linked with are being worn
+/obj/item/style_meter/proc/check_wearing(datum/source, mob/equipper, slot)
+ SIGNAL_HANDLER
+
+ if(!(slot & ITEM_SLOT_EYES))
+ if(style_meter)
+ QDEL_NULL(style_meter)
+ return
+
+ style_meter = equipper.AddComponent(/datum/component/style, multitooled)
+
+
+/// Signal proc for when the meter-holding glasses are dropped/unequipped
+/obj/item/style_meter/proc/on_drop(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(!style_meter)
+ return
+
+ QDEL_NULL(style_meter)
+
+
+/// Signal proc for on-examine
+/obj/item/style_meter/proc/on_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ examine_list += span_notice("You feel like a multitool could be used on this.")
+ examine_list += span_notice("Alt-click to remove the style meter.")
+
+
+/// Signal proc to remove from glasses
+/obj/item/style_meter/proc/on_altclick(datum/source, mob/user)
+ SIGNAL_HANDLER
+
+ if(istype(loc, /obj/item/clothing/glasses))
+ clean_up()
+ forceMove(get_turf(src))
+
+ return COMPONENT_CANCEL_CLICK_ALT
+
+
+/// Signal proc for when the glasses or the meter is multitooled
+/obj/item/style_meter/proc/on_multitool(datum/source, mob/living/user, obj/item/tool, list/recipes)
+ multitooled = !multitooled
+ if(style_meter)
+ SEND_SIGNAL(style_meter, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), user, tool, recipes)
+ else
+ balloon_alert(user, "meter [multitooled ? "" : "un"]hacked")
+
+
+/// Unregister signals and just generally clean up ourselves after being removed from glasses
+/obj/item/style_meter/proc/clean_up(atom/movable/old_location)
+ old_location.cut_overlay(meter_appearance)
+ UnregisterSignal(old_location, COMSIG_ITEM_EQUIPPED)
+ UnregisterSignal(old_location, COMSIG_ITEM_DROPPED)
+ UnregisterSignal(old_location, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(old_location, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL))
+ if(!style_meter)
+ return
+ QDEL_NULL(style_meter)
+
+
+/atom/movable/screen/style_meter_background
+ icon_state = "style_meter_background"
+ icon = 'icons/hud/style_meter.dmi'
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ screen_loc = "WEST,CENTER:16"
+ maptext_height = 120
+ maptext_width = 105
+ maptext_x = 5
+ maptext_y = 100
+ maptext = ""
+ layer = SCREENTIP_LAYER
+
+/atom/movable/screen/style_meter
+ icon_state = "style_meter"
+ icon = 'icons/hud/style_meter.dmi'
+ layer = SCREENTIP_LAYER
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
diff --git a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm
index 78242c12608..b41ccfa6254 100644
--- a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm
+++ b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm
@@ -97,3 +97,7 @@
/datum/orderable_item/mining/hiking_boots
item_path = /obj/item/clothing/shoes/winterboots/ice_boots
cost_per_order = 2500
+
+/datum/orderable_item/mining/style_meter
+ item_path = /obj/item/style_meter
+ cost_per_order = 1500
diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm
index 8e2649fea03..dcfc86c0310 100644
--- a/code/game/objects/structures/lavaland/geyser.dm
+++ b/code/game/objects/structures/lavaland/geyser.dm
@@ -67,6 +67,7 @@
return
to_chat(user, span_notice("You discovered the geyser and mark it on the GPS system!"))
+ SEND_SIGNAL(user, COMSIG_LIVING_DISCOVERED_GEYSER, src)
if(discovery_message)
to_chat(user, discovery_message)
diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm
index 6d456f8ff67..f04d5e5c277 100644
--- a/code/game/turfs/closed/minerals.dm
+++ b/code/game/turfs/closed/minerals.dm
@@ -134,7 +134,9 @@
if(user.Adjacent(src))
attack_hand(user)
-/turf/closed/mineral/proc/gets_drilled(user, give_exp = FALSE)
+/turf/closed/mineral/proc/gets_drilled(mob/user, give_exp = FALSE)
+ if(istype(user))
+ SEND_SIGNAL(user, COMSIG_MOB_MINED, src, give_exp)
if (mineralType && (mineralAmt > 0))
new mineralType(src, mineralAmt)
SSblackbox.record_feedback("tally", "ore_mined", mineralAmt, mineralType)
@@ -674,7 +676,7 @@
var/previous_stage = stage
if(istype(attacking_item, /obj/item/mining_scanner) || istype(attacking_item, /obj/item/t_scanner/adv_mining_scanner) && stage == GIBTONITE_ACTIVE)
user.visible_message(span_notice("[user] holds [attacking_item] to [src]..."), span_notice("You use [attacking_item] to locate where to cut off the chain reaction and attempt to stop it..."))
- defuse()
+ defuse(user)
..()
if(istype(attacking_item, /obj/item/clothing/gloves/gauntlets) && previous_stage == GIBTONITE_UNSTRUCK && stage == GIBTONITE_ACTIVE && istype(user))
user.Immobilize(0.5 SECONDS)
@@ -713,7 +715,7 @@
stage = GIBTONITE_DETONATE
explosion(bombturf, devastation_range = 1, heavy_impact_range = 3, light_impact_range = 5, adminlog = notify_admins, explosion_cause = src)
-/turf/closed/mineral/gibtonite/proc/defuse()
+/turf/closed/mineral/gibtonite/proc/defuse(mob/living/defuser)
if(stage == GIBTONITE_ACTIVE)
cut_overlay(activated_overlay)
activated_overlay.icon_state = "rock_Gibtonite_inactive"
@@ -723,8 +725,13 @@
if(det_time < 0)
det_time = 0
visible_message(span_notice("The chain reaction stopped! The gibtonite had [det_time] reactions left till the explosion!"))
+ if(defuser)
+ SEND_SIGNAL(defuser, COMSIG_LIVING_DEFUSED_GIBTONITE, det_time)
/turf/closed/mineral/gibtonite/gets_drilled(mob/user, give_exp = FALSE, triggered_by_explosion = FALSE)
+ if(istype(user))
+ SEND_SIGNAL(user, COMSIG_MOB_MINED, src, give_exp)
+
if(stage == GIBTONITE_UNSTRUCK && mineralAmt >= 1) //Gibtonite deposit is activated
playsound(src,'sound/effects/hit_on_shattered_glass.ogg',50,TRUE)
explosive_reaction(user)
@@ -794,6 +801,9 @@
/turf/closed/mineral/strong/gets_drilled(mob/user, give_exp = FALSE)
+ if(istype(user))
+ SEND_SIGNAL(user, COMSIG_MOB_MINED, src, give_exp)
+
if(!ishuman(user))
return // see attackby
var/mob/living/carbon/human/H = user
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 9a79e33ef4f..beeecfc4169 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -919,7 +919,7 @@ Striking a noncultist, however, will tear their flesh."}
for(var/turf/T in get_line(targets_from,temp_target))
if (locate(/obj/effect/blessing, T))
temp_target = T
- playsound(T, 'sound/machines/clockcult/ark_damage.ogg', 50, TRUE)
+ playsound(T, 'sound/effects/parry.ogg', 50, TRUE)
new /obj/effect/temp_visual/at_shield(T, T)
break
T.narsie_act(TRUE, TRUE)
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 6c096bd3ec0..a0658b8ad59 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -101,17 +101,21 @@
if(!QDELETED(C))
C.total_damage += target_health - L.health //we did some damage, but let's not assume how much we did
new /obj/effect/temp_visual/kinetic_blast(get_turf(L))
+ var/backstabbed = FALSE
+ var/combined_damage = detonation_damage
var/backstab_dir = get_dir(user, L)
var/def_check = L.getarmor(type = BOMB)
if((user.dir & backstab_dir) && (L.dir & backstab_dir))
- if(!QDELETED(C))
- C.total_damage += detonation_damage + backstab_bonus //cheat a little and add the total before killing it, so certain mobs don't have much lower chances of giving an item
- L.apply_damage(detonation_damage + backstab_bonus, BRUTE, blocked = def_check)
+ backstabbed = TRUE
+ combined_damage += backstab_bonus
playsound(user, 'sound/weapons/kenetic_accel.ogg', 100, TRUE) //Seriously who spelled it wrong
- else
- if(!QDELETED(C))
- C.total_damage += detonation_damage
- L.apply_damage(detonation_damage, BRUTE, blocked = def_check)
+
+ if(!QDELETED(C))
+ C.total_damage += combined_damage
+
+
+ SEND_SIGNAL(user, COMSIG_LIVING_CRUSHER_DETONATE, L, src, backstabbed)
+ L.apply_damage(combined_damage, BRUTE, blocked = def_check)
/obj/item/kinetic_crusher/attack_secondary(atom/target, mob/living/user, clickparams)
return SECONDARY_ATTACK_CONTINUE_CHAIN
diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm
index ead9207f28f..99b27e15ca8 100644
--- a/code/modules/mining/equipment/resonator.dm
+++ b/code/modules/mining/equipment/resonator.dm
@@ -60,7 +60,7 @@
/// the modifier to resonance_damage; affected by the quick_burst_mod from the resonator
var/damage_multiplier = 1
/// the parent creator (user) of this field
- var/creator
+ var/mob/creator
/// the parent resonator of this field
var/obj/item/resonator/parent_resonator
/// whether the field is rupturing currently or not (to prevent recursion)
@@ -127,6 +127,7 @@
for(var/mob/living/attacked_living in src_turf)
if(creator)
log_combat(creator, attacked_living, "used a resonator field on", "resonator")
+ SEND_SIGNAL(creator, COMSIG_LIVING_RESONATOR_BURST, creator, attacked_living)
to_chat(attacked_living, span_userdanger("[src] ruptured with you in it!"))
attacked_living.apply_damage(resonance_damage, BRUTE)
attacked_living.add_movespeed_modifier(/datum/movespeed_modifier/resonance)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 0cdd844da66..dcf7b915f46 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -203,6 +203,8 @@
/// If true directly targeted turfs can be hit
var/can_hit_turfs = FALSE
+ /// If this projectile has been parried before
+ var/parried = FALSE
/obj/projectile/Initialize(mapload)
. = ..()
@@ -210,6 +212,7 @@
if(embedding)
updateEmbedding()
AddElement(/datum/element/connect_loc, projectile_connections)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_enter))
/obj/projectile/proc/Range()
range--
@@ -381,6 +384,41 @@
return
Impact(A)
+/// Signal proc for when a projectile enters a turf.
+/obj/projectile/proc/on_enter(datum/source, atom/old_loc, dir, forced, list/old_locs)
+ SIGNAL_HANDLER
+
+ UnregisterSignal(old_loc, COMSIG_ATOM_ATTACK_HAND)
+
+ if(isturf(loc))
+ RegisterSignal(loc, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attempt_parry))
+
+/// Signal proc for when a mob attempts to attack this projectile or the turf it's on with an empty hand.
+/obj/projectile/proc/attempt_parry(datum/source, mob/user, list/modifiers)
+ SIGNAL_HANDLER
+
+ if(parried)
+ return FALSE
+
+ if(SEND_SIGNAL(user, COMSIG_LIVING_PROJECTILE_PARRYING, src) & ALLOW_PARRY)
+ on_parry(user, modifiers)
+ return TRUE
+
+ return FALSE
+
+
+/// Called when a mob with PARRY_TRAIT clicks on this projectile or the tile its on, reflecting the projectile within 17 degrees and increasing the bullet's stats.
+/obj/projectile/proc/on_parry(mob/user, list/modifiers)
+ if(SEND_SIGNAL(user, COMSIG_LIVING_PROJECTILE_PARRIED, src) & INTERCEPT_PARRY_EFFECTS)
+ return
+
+ parried = TRUE
+ set_angle(dir2angle(user.dir) + rand(-3, 3))
+ speed *= 0.8 // Go 20% faster when parried
+ damage *= 1.15 // And do 15% more damage
+ add_atom_colour(COLOR_RED_LIGHT, TEMPORARY_COLOUR_PRIORITY)
+
+
/**
* Called when the projectile hits something
* This can either be from it bumping something,
@@ -745,6 +783,7 @@
fired = TRUE
play_fov_effect(starting, 6, "gunfire", dir = NORTH, angle = Angle)
SEND_SIGNAL(src, COMSIG_PROJECTILE_FIRE)
+ RegisterSignal(src, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attempt_parry))
if(hitscan)
process_hitscan()
if(!(datum_flags & DF_ISPROCESSING))
diff --git a/icons/hud/style_meter.dmi b/icons/hud/style_meter.dmi
new file mode 100644
index 00000000000..5da5f72c3ee
Binary files /dev/null and b/icons/hud/style_meter.dmi differ
diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi
index a9d213d9fd8..25cfd514d9a 100644
Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ
diff --git a/sound/machines/clockcult/ark_damage.ogg b/sound/effects/parry.ogg
similarity index 100%
rename from sound/machines/clockcult/ark_damage.ogg
rename to sound/effects/parry.ogg
diff --git a/sound/items/style/combo_absolute1.ogg b/sound/items/style/combo_absolute1.ogg
new file mode 100644
index 00000000000..d82f318ac6a
Binary files /dev/null and b/sound/items/style/combo_absolute1.ogg differ
diff --git a/sound/items/style/combo_absolute2.ogg b/sound/items/style/combo_absolute2.ogg
new file mode 100644
index 00000000000..804902024b7
Binary files /dev/null and b/sound/items/style/combo_absolute2.ogg differ
diff --git a/sound/items/style/combo_absolute3.ogg b/sound/items/style/combo_absolute3.ogg
new file mode 100644
index 00000000000..676d1d5864b
Binary files /dev/null and b/sound/items/style/combo_absolute3.ogg differ
diff --git a/sound/items/style/combo_brutal1.ogg b/sound/items/style/combo_brutal1.ogg
new file mode 100644
index 00000000000..ba0b732dd69
Binary files /dev/null and b/sound/items/style/combo_brutal1.ogg differ
diff --git a/sound/items/style/combo_brutal2.ogg b/sound/items/style/combo_brutal2.ogg
new file mode 100644
index 00000000000..ce64cae15c9
Binary files /dev/null and b/sound/items/style/combo_brutal2.ogg differ
diff --git a/sound/items/style/combo_brutal3.ogg b/sound/items/style/combo_brutal3.ogg
new file mode 100644
index 00000000000..651e79acb76
Binary files /dev/null and b/sound/items/style/combo_brutal3.ogg differ
diff --git a/sound/items/style/combo_cool1.ogg b/sound/items/style/combo_cool1.ogg
new file mode 100644
index 00000000000..7afbe5a89f9
Binary files /dev/null and b/sound/items/style/combo_cool1.ogg differ
diff --git a/sound/items/style/combo_cool2.ogg b/sound/items/style/combo_cool2.ogg
new file mode 100644
index 00000000000..a9f3106fe2d
Binary files /dev/null and b/sound/items/style/combo_cool2.ogg differ
diff --git a/sound/items/style/combo_cool3.ogg b/sound/items/style/combo_cool3.ogg
new file mode 100644
index 00000000000..c304be6dfff
Binary files /dev/null and b/sound/items/style/combo_cool3.ogg differ
diff --git a/sound/items/style/combo_dull1.ogg b/sound/items/style/combo_dull1.ogg
new file mode 100644
index 00000000000..42b05cabc3f
Binary files /dev/null and b/sound/items/style/combo_dull1.ogg differ
diff --git a/sound/items/style/combo_dull2.ogg b/sound/items/style/combo_dull2.ogg
new file mode 100644
index 00000000000..0e72e82d9f6
Binary files /dev/null and b/sound/items/style/combo_dull2.ogg differ
diff --git a/sound/items/style/combo_dull3.ogg b/sound/items/style/combo_dull3.ogg
new file mode 100644
index 00000000000..f4674d068d6
Binary files /dev/null and b/sound/items/style/combo_dull3.ogg differ
diff --git a/sound/items/style/combo_spaced1.ogg b/sound/items/style/combo_spaced1.ogg
new file mode 100644
index 00000000000..62624fd6fdd
Binary files /dev/null and b/sound/items/style/combo_spaced1.ogg differ
diff --git a/sound/items/style/combo_spaced2.ogg b/sound/items/style/combo_spaced2.ogg
new file mode 100644
index 00000000000..eccdb97be2c
Binary files /dev/null and b/sound/items/style/combo_spaced2.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 4d7aa3df402..5c10d37aa69 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1034,6 +1034,7 @@
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\palette.dm"
+#include "code\datums\components\parry.dm"
#include "code\datums\components\payment.dm"
#include "code\datums\components\pellet_cloud.dm"
#include "code\datums\components\phylactery.dm"
@@ -1151,6 +1152,8 @@
#include "code\datums\components\riding\riding.dm"
#include "code\datums\components\riding\riding_mob.dm"
#include "code\datums\components\riding\riding_vehicle.dm"
+#include "code\datums\components\style\style.dm"
+#include "code\datums\components\style\style_meter.dm"
#include "code\datums\diseases\_disease.dm"
#include "code\datums\diseases\_MobProcs.dm"
#include "code\datums\diseases\adrenal_crisis.dm"