mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-17 11:05:16 +01:00
751bfc08bb
## About The Pull Request Removes the IC tab in its entirety - Some mobs (Blob Minion & Lightgeist) had manually removed the "pull" verb, this replaces it with taking away their pull force. - Lightgeist had their "Me" verb manually removed, this is replaced by giving them Emotemute trait (the same that mime's bane gives) - Floor Changer: You can now RMB the arrow buttons to look in a direction w/o moving. Added a tip to help with this. - Exit Hivemind: Already an action button and was removed without further edits ### Removed entirely Rest Resist Look Up/Down Move Upwards/Down Activate Held Object Open Language Menu Memories View Skills ### Moved to command bar (secret verb ooh) because I am biased Sleep Navigate ### Martial Arts Martial Arts had a button to see your current skills & the ability to cycle between artstyles. This was replaced with 1 action button that does both of these. <img width="317" height="148" alt="image" src="https://github.com/user-attachments/assets/afed910f-b6b6-441d-86cb-dade0e253044" /> Name auto-updates as you swap artstyle to whatever help verb the current style uses. Mention of RMB in the name is only there if you have more than 1 martial art, but the desc will remain just so players can see it even if they only have one, for future reference. ## Why It's Good For The Game Brings greater visibility to Martial Art stuff, and especially since the stat panel is being made optional we should not be hiding ANYTHING in there. This also helps me with my project listed in https://hackmd.io/443_dE5lRWeEAp9bjGcKYw?view And also fixes some sorta-removed features due to removing verbs not matching people's actual powers due to new features like "Custom Me" and "Ctrl Clicking". ## Changelog 🆑 SirNightKnight (sprites), JohnFulpWillard del: Deleted the "IC" tab of the stat panel. fix: Lightgeists can't emote or pull as intended. fix: Blob minions can't pull again. qol: Martial Art users now have an action button for their artstyle-related businesses, rather than it being in the stat panel. qol: You can now right-click the move up/down UI buttons to look in the direction without moving. /🆑
441 lines
15 KiB
Plaintext
441 lines
15 KiB
Plaintext
/datum/martial_art
|
|
/// Player readable name of the martial art
|
|
var/name = "Martial Art"
|
|
/// ID of the martial art
|
|
var/id = ""
|
|
/// The streak of attacks the user has performed
|
|
VAR_FINAL/streak = ""
|
|
/// The maximum length of streaks allowed
|
|
var/max_streak_length = 6
|
|
|
|
/// Are we being actively used by a mob?
|
|
var/active = FALSE
|
|
/// Where this martial art is from, sometimes the same as the holder if it's tied to them
|
|
/// If the origin is deleted, this martial art will be too.
|
|
VAR_PRIVATE/datum/origin
|
|
/// The current mob associated with this martial art datum. Do not set directly.
|
|
VAR_PRIVATE/mob/living/holder
|
|
/// Weakref to the last mob we attacked, for determining when to reset streaks
|
|
VAR_PRIVATE/datum/weakref/current_target
|
|
|
|
/// Path to verb to display help text for this martial art.
|
|
var/help_verb = "Remember the Basics"
|
|
/// If TRUE, this martial art smashes tables when performing table slams and head smashes
|
|
var/smashes_tables = FALSE
|
|
/// If TRUE, a combo meter will be displayed on the HUD for the current streak
|
|
var/display_combos = FALSE
|
|
/// The length of time until streaks are auto-reset.
|
|
var/combo_timer = 6 SECONDS
|
|
/// Timer ID for the combo reset timer.
|
|
var/timerid
|
|
/// If TRUE, this style allows you to punch people despite being a pacifist (IE: Boxing, which does no damage)
|
|
var/pacifist_style = FALSE
|
|
/// If TRUE, the user is locked to using this martial art, and can't swap to other ones they know.
|
|
/// If the mob has two locked martial arts, it's first come first serve.
|
|
var/locked_to_use = FALSE
|
|
/// A modifier to the effective grab state for resist grabs of users of this martial art.
|
|
/// IE: grab_state_modifier = 1 means passive grabs are aggro grab difficulty, and aggro grabs are neckgrab difficulty.
|
|
var/grab_state_modifier = 0
|
|
/// A modifier to the damage dealt on a failed grab resist.
|
|
/// IE: grab_damage_modifier = 10 means 10 more stamina damage dealt
|
|
var/grab_damage_modifier = 0
|
|
/// A modifier to the chance of escaping a grab.
|
|
/// IE: grab_escape_chance_modifier = -10 means 10% less chance to escape a grab
|
|
var/grab_escape_chance_modifier = 0
|
|
|
|
/datum/martial_art/serialize_list(list/options, list/semvers)
|
|
. = ..()
|
|
|
|
.["name"] = name
|
|
.["id"] = id
|
|
.["pacifist_style"] = pacifist_style
|
|
|
|
SET_SERIALIZATION_SEMVER(semvers, "1.0.0")
|
|
return .
|
|
|
|
/datum/martial_art/New(datum/new_origin)
|
|
set_origin(new_origin)
|
|
|
|
/datum/martial_art/Destroy()
|
|
if(!isnull(holder))
|
|
unlearn(holder)
|
|
if(!isnull(origin))
|
|
set_origin(null)
|
|
return ..()
|
|
|
|
/datum/martial_art/proc/set_origin(datum/new_origin)
|
|
if(origin)
|
|
UnregisterSignal(origin, COMSIG_QDELETING)
|
|
origin = null
|
|
if(isnull(new_origin))
|
|
return
|
|
src.origin = new_origin
|
|
RegisterSignal(origin, COMSIG_QDELETING, PROC_REF(clear_origin))
|
|
|
|
/datum/martial_art/proc/clear_origin()
|
|
SIGNAL_HANDLER
|
|
qdel(src)
|
|
|
|
/datum/martial_art/proc/clear_holder(datum/source)
|
|
SIGNAL_HANDLER
|
|
unlearn(holder)
|
|
|
|
/// Signal proc for [COMSIG_LIVING_UNARMED_ATTACK] to hook into the appropriate proc
|
|
/datum/martial_art/proc/unarmed_strike(mob/living/source, atom/attack_target, proximity, modifiers)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!proximity || !isliving(attack_target))
|
|
return NONE
|
|
|
|
if(HAS_TRAIT(attack_target, TRAIT_MARTIAL_ARTS_IMMUNE))
|
|
return NONE
|
|
|
|
if(!can_use(source))
|
|
return NONE
|
|
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
return disarm_act(source, attack_target)
|
|
|
|
if(source.combat_mode)
|
|
if(HAS_TRAIT(source, TRAIT_PACIFISM) && !pacifist_style)
|
|
return NONE
|
|
|
|
return harm_act(source, attack_target)
|
|
|
|
return help_act(source, attack_target)
|
|
|
|
/// Signal proc for [COMSIG_LIVING_GRAB] to hook into the grab
|
|
/datum/martial_art/proc/attempt_grab(mob/living/source, mob/living/grabbing)
|
|
SIGNAL_HANDLER
|
|
|
|
if(HAS_TRAIT(grabbing, TRAIT_MARTIAL_ARTS_IMMUNE))
|
|
return NONE
|
|
|
|
if(!source.can_unarmed_attack()) // For parity with unarmed attacks
|
|
return NONE
|
|
|
|
if(!can_use(source))
|
|
return NONE
|
|
|
|
return grab_act(source, grabbing)
|
|
|
|
/**
|
|
* Called when help-intenting on someone
|
|
*
|
|
* What is checked going into this:
|
|
* Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
|
|
*
|
|
* What is NOT:
|
|
* check_block
|
|
*
|
|
* Arguments
|
|
* * mob/living/attacker - The mob attacking
|
|
* * mob/living/defender - The mob being attacked
|
|
*
|
|
* Returns
|
|
* * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
|
|
* * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
|
|
* * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
|
|
*/
|
|
/datum/martial_art/proc/help_act(mob/living/attacker, mob/living/defender)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
PROTECTED_PROC(TRUE)
|
|
return MARTIAL_ATTACK_INVALID
|
|
|
|
/**
|
|
* Called when disarm-intenting on someone
|
|
*
|
|
* What is checked going into this:
|
|
* Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
|
|
*
|
|
* What is NOT:
|
|
* check_block
|
|
*
|
|
* Arguments
|
|
* * mob/living/attacker - The mob attacking
|
|
* * mob/living/defender - The mob being attacked
|
|
*
|
|
* Returns
|
|
* * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
|
|
* * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
|
|
* * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
|
|
*/
|
|
/datum/martial_art/proc/disarm_act(mob/living/attacker, mob/living/defender)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
PROTECTED_PROC(TRUE)
|
|
return MARTIAL_ATTACK_INVALID
|
|
|
|
/**
|
|
* Called when harm-intenting on someone
|
|
*
|
|
* What is checked going into this:
|
|
* Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
|
|
*
|
|
* What is NOT:
|
|
* check_block
|
|
*
|
|
* Arguments
|
|
* * mob/living/attacker - The mob attacking
|
|
* * mob/living/defender - The mob being attacked
|
|
*
|
|
* Returns
|
|
* * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
|
|
* * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
|
|
* * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
|
|
*/
|
|
/datum/martial_art/proc/harm_act(mob/living/attacker, mob/living/defender)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
PROTECTED_PROC(TRUE)
|
|
return MARTIAL_ATTACK_INVALID
|
|
|
|
/**
|
|
* Called when grabbing someone
|
|
*
|
|
* What is checked going into this:
|
|
* Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
|
|
*
|
|
* What is NOT:
|
|
* check_block
|
|
*
|
|
* Arguments
|
|
* * mob/living/attacker - The mob attacking
|
|
* * mob/living/defender - The mob being attacked
|
|
*
|
|
* Returns
|
|
* * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
|
|
* * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
|
|
* * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
|
|
*/
|
|
/datum/martial_art/proc/grab_act(mob/living/attacker, mob/living/defender)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
PROTECTED_PROC(TRUE)
|
|
return MARTIAL_ATTACK_INVALID
|
|
|
|
/**
|
|
* Checks if the passed mob can use this martial art.
|
|
*
|
|
* Arguments
|
|
* * mob/living/martial_artist - The mob to check
|
|
*
|
|
* Returns
|
|
* * TRUE - The mob can use this martial art
|
|
* * FALSE - The mob cannot use this martial art
|
|
*/
|
|
/datum/martial_art/proc/can_use(mob/living/martial_artist)
|
|
return TRUE
|
|
|
|
/**
|
|
* Gets what limb is being used going when punching with this martial art.
|
|
*
|
|
* Override get_prefered_attacking_limb() to change the limb used.
|
|
*
|
|
* Arguments
|
|
* * mob/living/martial_artist - The mob using the martial art
|
|
* * mob/living/target - The target of the attack
|
|
*
|
|
* Returns
|
|
* A bodypart, or null if we want to use default behavior (brain determines, or active hand).
|
|
*/
|
|
/datum/martial_art/proc/get_attacking_limb(mob/living/martial_artist, mob/living/target)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
if(!can_use(martial_artist))
|
|
return null
|
|
var/preferred_zone = get_prefered_attacking_limb(martial_artist, target)
|
|
if(!preferred_zone)
|
|
return null
|
|
return martial_artist.get_bodypart(preferred_zone)
|
|
|
|
/**
|
|
* Allows martial arts to have a say which limb the user should be striking with.
|
|
*
|
|
*
|
|
* Arguments
|
|
* * mob/living/martial_artist - The mob using the martial art
|
|
* * mob/living/target - The target of the attack
|
|
*
|
|
* Returns
|
|
* * A body zone, or null if we have no preference.
|
|
*/
|
|
/datum/martial_art/proc/get_prefered_attacking_limb(mob/living/martial_artist, mob/living/target)
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
PROTECTED_PROC(TRUE)
|
|
return null
|
|
|
|
/**
|
|
* Adds the passed element to the current streak, resetting it if the target is not the same as the last target.
|
|
*
|
|
* Arguments
|
|
* * element - The element to add to the streak. This is some one letter string.
|
|
* * mob/living/defender - The mob being attacked
|
|
*/
|
|
/datum/martial_art/proc/add_to_streak(element, mob/living/defender)
|
|
if(!IS_WEAKREF_OF(defender, current_target))
|
|
reset_streak(defender)
|
|
streak += element
|
|
if(length(streak) > max_streak_length)
|
|
streak = copytext(streak, 1 + length(streak[1]))
|
|
if(!display_combos)
|
|
return
|
|
timerid = addtimer(CALLBACK(src, PROC_REF(reset_streak), null, FALSE), combo_timer, TIMER_UNIQUE | TIMER_STOPPABLE)
|
|
var/atom/movable/screen/combo/combo_display = holder.hud_used?.screen_objects[HUD_MOB_COMBO]
|
|
if(istype(combo_display))
|
|
combo_display.update_icon_state(streak, combo_timer - 2 SECONDS)
|
|
|
|
/**
|
|
* Resets the current streak.
|
|
*
|
|
* Arguments
|
|
* * mob/living/new_target - (Optional) The mob being attacked while the reset is occurring.
|
|
* * update_icon - If TRUE, the combo display will be updated.
|
|
*/
|
|
/datum/martial_art/proc/reset_streak(mob/living/new_target, update_icon = TRUE)
|
|
if(timerid)
|
|
deltimer(timerid)
|
|
current_target = WEAKREF(new_target)
|
|
streak = ""
|
|
var/atom/movable/screen/combo/combo_display = holder.hud_used?.screen_objects[HUD_MOB_COMBO]
|
|
if(istype(combo_display) && display_combos && update_icon)
|
|
combo_display.update_icon_state(streak)
|
|
|
|
/datum/martial_art/proc/smash_table(mob/living/source, mob/living/pushed_mob, obj/structure/table/table)
|
|
SIGNAL_HANDLER
|
|
if(smashes_tables)
|
|
table.deconstruct(FALSE)
|
|
|
|
/**
|
|
* Teaches the passed mob this martial art.
|
|
*
|
|
* Arguments
|
|
* * mob/living/new_holder - The mob to teach this martial art to.
|
|
*
|
|
* Returns
|
|
* * TRUE - The martial art was successfully taught.
|
|
* * FALSE - The mob failed to learn the martial art, for whatever reason.
|
|
*/
|
|
/datum/martial_art/proc/teach(mob/living/new_holder)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
|
|
if(!can_teach(new_holder) || holder == new_holder)
|
|
return FALSE
|
|
|
|
holder = new_holder
|
|
if(origin != new_holder)
|
|
RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(clear_holder))
|
|
// locked martial arts always get inserted as the next up
|
|
// (so if you learn two locked martial arts, and you get rid of the first, the second will slot itself in)
|
|
if(locked_to_use && LAZYLEN(new_holder.martial_arts) >= 2)
|
|
LAZYINSERT(new_holder.martial_arts, 2, src)
|
|
else
|
|
LAZYADD(new_holder.martial_arts, src)
|
|
|
|
if(LAZYLEN(new_holder.martial_arts) == 1 ? get_style_help() : !(locate(/datum/action/swap_arts) in new_holder.actions))
|
|
var/datum/action/swap_arts/new_action = new(new_holder, src)
|
|
new_action.Grant(new_holder)
|
|
if(LAZYLEN(new_holder.martial_arts) >= 2)
|
|
// if the active one is locked, this will no-op, which is fine
|
|
new_holder.switch_style(GET_ACTIVE_MARTIAL_ART(new_holder), src)
|
|
else if(!active)
|
|
activate_style(new_holder)
|
|
return TRUE
|
|
|
|
/**
|
|
* Checks if the passed mob can be taught this martial art.
|
|
*
|
|
* Arguments
|
|
* * mob/living/new_holder - The mob to check
|
|
*
|
|
* Returns
|
|
* * TRUE - The mob can be taught this martial art
|
|
* * FALSE - The mob cannot be taught this martial art
|
|
*/
|
|
/datum/martial_art/proc/can_teach(mob/living/new_holder)
|
|
return isliving(new_holder)
|
|
|
|
/**
|
|
* Removes this martial art from the passed mob.
|
|
*
|
|
* Arguments
|
|
* * mob/living/old_holder - The mob to remove this martial art from.
|
|
*/
|
|
/datum/martial_art/proc/unlearn(mob/living/old_holder)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
|
|
if(old_holder != holder)
|
|
return FALSE
|
|
|
|
if(LAZYLEN(old_holder.martial_arts) >= 2 && !QDELING(old_holder))
|
|
old_holder.switch_style(src, GET_NEXT_MARTIAL_ART(old_holder))
|
|
else if(active)
|
|
deactivate_style(old_holder)
|
|
if(origin != old_holder)
|
|
UnregisterSignal(old_holder, COMSIG_QDELETING)
|
|
LAZYREMOVE(old_holder.martial_arts, src)
|
|
holder = null
|
|
if(LAZYLEN(old_holder.martial_arts) < 1) //no more arts, we check above to switch style already.
|
|
var/datum/action/swap_arts/swap_button = locate() in old_holder.actions
|
|
qdel(swap_button)
|
|
return TRUE
|
|
|
|
/**
|
|
* Called when this martial art is added to a mob.
|
|
*/
|
|
/datum/martial_art/proc/activate_style(mob/living/new_holder)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
active = TRUE
|
|
RegisterSignal(new_holder, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(unarmed_strike))
|
|
RegisterSignal(new_holder, COMSIG_LIVING_GRAB, PROC_REF(attempt_grab))
|
|
RegisterSignals(new_holder, list(COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING), PROC_REF(smash_table))
|
|
if(display_combos)
|
|
RegisterSignal(new_holder, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
|
|
if(new_holder.hud_used)
|
|
on_hud_created(new_holder)
|
|
|
|
/**
|
|
* Called when this martial art is removed from a mob.
|
|
*/
|
|
/datum/martial_art/proc/deactivate_style(mob/living/remove_from)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
active = FALSE
|
|
UnregisterSignal(remove_from, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_LIVING_GRAB, COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING))
|
|
remove_from.hud_used?.remove_screen_object(HUD_MOB_COMBO)
|
|
|
|
///Gives the owner of the martial art the combo HUD.
|
|
/datum/martial_art/proc/on_hud_created(mob/source)
|
|
SIGNAL_HANDLER
|
|
source.hud_used.add_screen_object(/atom/movable/screen/combo, HUD_MOB_COMBO, HUD_GROUP_INFO, update_screen = TRUE)
|
|
|
|
/mob/living/proc/cycle_style()
|
|
var/datum/martial_art/current = GET_ACTIVE_MARTIAL_ART(src)
|
|
var/datum/martial_art/next = GET_NEXT_MARTIAL_ART(src)
|
|
|
|
if(current.locked_to_use)
|
|
to_chat(src, span_warning("You can't stop practicing [current]! It's too ingrained in your muscle memory."))
|
|
return
|
|
|
|
switch_style(current, next)
|
|
to_chat(src, span_notice("You stop practicing [current] and start practicing [next]."))
|
|
|
|
/// Deactivates the current martial art and activates the next one.
|
|
/mob/living/proc/switch_style(datum/martial_art/current_martial, datum/martial_art/next_martial)
|
|
if(current_martial.locked_to_use)
|
|
return
|
|
// something's wrong if this assertion fails, but not terribly wrong that we need a stack trace
|
|
if(!current_martial.active || next_martial.active)
|
|
return
|
|
|
|
var/datum/action/swap_arts/swap_button = locate() in actions
|
|
swap_button.current_used_art = next_martial
|
|
|
|
current_martial.deactivate_style(src)
|
|
next_martial.activate_style(src)
|
|
// front of the list with ye
|
|
LAZYREMOVE(martial_arts, next_martial)
|
|
LAZYINSERT(martial_arts, 1, next_martial)
|
|
// back of the list with ye
|
|
LAZYREMOVE(martial_arts, current_martial)
|
|
LAZYADD(martial_arts, current_martial)
|
|
|
|
///To be overwritten for artstyle help.
|
|
/datum/martial_art/proc/get_style_help()
|
|
return FALSE
|