[MIRROR] Miner Style Points 2: Style on Everyone [MDB IGNORE] (#20826)

* Miner Style Points 2: Style on Everyone (#74690)

## About The Pull Request
A re-open of https://github.com/tgstation/tgstation/pull/66326 with
Fikou's permission

Adds the style meter, it can be bought from the mining vendor for 1500
points, it is an attachment to your glasses.
The style meter creates a display on your hud, with your recent actions,
like attacking enemies, killing them, mining ore etc. Actions like
spinning or flipping increase your score multiplier, making you get more
points.
Your style meter affects how much ore you get from mining rocks. By
default with the meter, you get 20% less ore, but at the highest, you
can get 1.2x the ore from mining. In addition, on B-tier or above, you
can "hotswap" items, by attacking an item in your backpack with one in
your hand (should it fit and all that). Also features a leaderboard for
highest style point count!

New streamable: https://streamable.com/eewi6l

The following are sources of points:

- Killing things
- Killing big things
- Killing small things
- Punching things
- Melee'ing things
- Mining rocks and ores
- Having matrix traps detonate
- Hit, defuse, and detonate gibtonite
- Detonate crusher marks
- Scan geysers
- Parry projectiles (others or your own)

Oh, right. While wearing the style meter, you're able to parry any
lavaland-based projectile by clicking on it or the tile it is on, which
reflects it back in a 7 degree arc, making it 20% faster and 15% more
damaging. Usually not very easy.

Maybe-plan in the future for some syndicate variant of this (with bullet
parrying and appropriate style sources, etc.), but not for this PR

Thanks to Arcane, multitooling the style meter will make it play some
sounds on rank-up.

![image](https://user-images.githubusercontent.com/41448081/231605640-a01c2b60-1ba1-4390-8bea-0aa804ea1973.png)

https://streamable.com/nheaky

Parrying in action

## Why It's Good For The Game
Makes miners bring more ore in a fun way.

## Changelog
🆑 Fikou, Zonespace, Arcane for voicing
add: The mining vendor now has a style meter. This meter gauges your
style points and uses them to improve your ore yield.
/🆑

---------

Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com>

* Miner Style Points 2: Style on Everyone

---------

Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com>
Co-authored-by: Fikou <23585223+Fikou@ users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-04-28 19:54:39 -07:00
committed by GitHub
co-authored by Fikou Zonespace
parent 655bda28f9
commit 9c2e07ee7c
34 changed files with 784 additions and 12 deletions
+3
View File
@@ -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)
@@ -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"
@@ -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"
+1
View File
@@ -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
+3
View File
@@ -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
+6
View File
@@ -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
+38
View File
@@ -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!"))
+496
View File
@@ -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 += "<span class='maptext' style='font-size: 8px'><font color='[rank_to_color(new_rank)]'><b>[rank_string[1]]</b>"
final_string += "<span style='font-size: 7px'>[copytext(rank_string, 2)]</span></font></span>"
return final_string
/datum/component/style/proc/generate_multiplier()
return "<br><span class='maptext' style='font-size: 7px'>MULTIPLIER: [point_multiplier]X</span>"
/datum/component/style/proc/generate_actions()
var/action_string = ""
for(var/action in actions)
action_string += "<br><span class='maptext'>+ <font color='[action_to_color(actions[action])]'>[actions[action]]</font></span>"
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
+139
View File
@@ -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 <b>hitting</b> 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 <b>multitool</b> 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 <b>multitool</b> could be used on this.")
examine_list += span_notice("<b>Alt-click</b> 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
@@ -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
@@ -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)
+13 -3
View File
@@ -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
+1 -1
View File
@@ -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)
@@ -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
+2 -1
View File
@@ -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)
+39
View File
@@ -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))
Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -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"