Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit655
This commit is contained in:
@@ -276,7 +276,23 @@ Works together with spawning an observer, noted above.
|
||||
if (!(client.prefs.chat_toggles & CHAT_OOC))
|
||||
client.prefs.chat_toggles ^= CHAT_OOC
|
||||
transfer_ckey(ghost, FALSE)
|
||||
ghost.AddElement(/datum/element/ghost_role_eligibility,penalize) // technically already run earlier, but this adds the penalty
|
||||
if(penalize)
|
||||
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
|
||||
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
|
||||
if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed.
|
||||
penalty += roundstart_quit_limit - world.time
|
||||
if(penalty)
|
||||
penalty += world.realtime
|
||||
if(SSautotransfer.can_fire && SSautotransfer.maxvotes)
|
||||
var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
|
||||
if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
|
||||
penalty = CANT_REENTER_ROUND
|
||||
if(!(ckey in GLOB.client_ghost_timeouts))
|
||||
GLOB.client_ghost_timeouts += ckey
|
||||
GLOB.client_ghost_timeouts[ckey] = 0
|
||||
else if(GLOB.client_ghost_timeouts[ckey] == CANT_REENTER_ROUND)
|
||||
return
|
||||
GLOB.client_ghost_timeouts[ckey] = max(GLOB.client_ghost_timeouts[ckey],penalty)
|
||||
// needs to be done AFTER the ckey transfer, too
|
||||
return ghost
|
||||
|
||||
|
||||
@@ -63,13 +63,13 @@
|
||||
if(hit_atom)
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
if(!L.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
|
||||
if(L.run_block(src, 0, "the [name]", ATTACK_TYPE_TACKLE, 0, src) & BLOCK_SUCCESS)
|
||||
DefaultCombatKnockdown(40, 1, 1)
|
||||
else
|
||||
L.visible_message("<span class ='danger'>[src] pounces on [L]!</span>", "<span class ='userdanger'>[src] pounces on you!</span>")
|
||||
L.DefaultCombatKnockdown(100)
|
||||
sleep(2)//Runtime prevention (infinite bump() calls on hulks)
|
||||
step_towards(src,L)
|
||||
else
|
||||
DefaultCombatKnockdown(40, 1, 1)
|
||||
|
||||
toggle_leap(0)
|
||||
else if(hit_atom.density && !hit_atom.CanPass(src))
|
||||
|
||||
@@ -984,14 +984,121 @@
|
||||
|
||||
/mob/living/carbon/vv_get_dropdown()
|
||||
. = ..()
|
||||
. += "---"
|
||||
.["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]"
|
||||
.["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]"
|
||||
.["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]"
|
||||
.["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]"
|
||||
.["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]"
|
||||
.["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]"
|
||||
.["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]"
|
||||
VV_DROPDOWN_OPTION("", "---------")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MAKE_AI, "Make AI")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MODIFY_BODYPART, "Modify bodypart")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MODIFY_ORGANS, "Modify organs")
|
||||
VV_DROPDOWN_OPTION(VV_HK_HALLUCINATION, "Hallucinate")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MARTIAL_ART, "Give Martial Arts")
|
||||
VV_DROPDOWN_OPTION(VV_HK_GIVE_TRAUMA, "Give Brain Trauma")
|
||||
VV_DROPDOWN_OPTION(VV_HK_CURE_TRAUMA, "Cure Brain Traumas")
|
||||
|
||||
/mob/living/carbon/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
if(href_list[VV_HK_MODIFY_BODYPART])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment")
|
||||
if(!edit_action)
|
||||
return
|
||||
var/list/limb_list = list()
|
||||
if(edit_action == "remove" || edit_action == "augment")
|
||||
for(var/obj/item/bodypart/B in bodyparts)
|
||||
limb_list += B.body_zone
|
||||
if(edit_action == "remove")
|
||||
limb_list -= BODY_ZONE_CHEST
|
||||
else
|
||||
limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
||||
for(var/obj/item/bodypart/B in bodyparts)
|
||||
limb_list -= B.body_zone
|
||||
var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list
|
||||
if(result)
|
||||
var/obj/item/bodypart/BP = get_bodypart(result)
|
||||
switch(edit_action)
|
||||
if("remove")
|
||||
if(BP)
|
||||
BP.drop_limb()
|
||||
else
|
||||
to_chat(usr, "[src] doesn't have such bodypart.")
|
||||
if("add")
|
||||
if(BP)
|
||||
to_chat(usr, "[src] already has such bodypart.")
|
||||
else
|
||||
if(!regenerate_limb(result))
|
||||
to_chat(usr, "[src] cannot have such bodypart.")
|
||||
if("augment")
|
||||
if(ishuman(src))
|
||||
if(BP)
|
||||
BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE)
|
||||
else
|
||||
to_chat(usr, "[src] doesn't have such bodypart.")
|
||||
else
|
||||
to_chat(usr, "Only humans can be augmented.")
|
||||
admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src]")
|
||||
if(href_list[VV_HK_MAKE_AI])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
usr.client.holder.Topic("vv_override", list("makeai"=href_list[VV_HK_TARGET]))
|
||||
if(href_list[VV_HK_MODIFY_ORGANS])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.manipulate_organs(src)
|
||||
if(href_list[VV_HK_MARTIAL_ART])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
var/list/artpaths = subtypesof(/datum/martial_art)
|
||||
var/list/artnames = list()
|
||||
for(var/i in artpaths)
|
||||
var/datum/martial_art/M = i
|
||||
artnames[initial(M.name)] = M
|
||||
var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames
|
||||
if(!usr)
|
||||
return
|
||||
if(QDELETED(src))
|
||||
to_chat(usr, "Mob doesn't exist anymore")
|
||||
return
|
||||
if(result)
|
||||
var/chosenart = artnames[result]
|
||||
var/datum/martial_art/MA = new chosenart
|
||||
MA.teach(src)
|
||||
log_admin("[key_name(usr)] has taught [MA] to [key_name(src)].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has taught [MA] to [key_name_admin(src)].</span>")
|
||||
if(href_list[VV_HK_GIVE_TRAUMA])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
var/list/traumas = subtypesof(/datum/brain_trauma)
|
||||
var/result = input(usr, "Choose the brain trauma to apply","Traumatize") as null|anything in traumas
|
||||
if(!usr)
|
||||
return
|
||||
if(QDELETED(src))
|
||||
to_chat(usr, "Mob doesn't exist anymore")
|
||||
return
|
||||
if(!result)
|
||||
return
|
||||
var/datum/brain_trauma/BT = gain_trauma(result)
|
||||
if(BT)
|
||||
log_admin("[key_name(usr)] has traumatized [key_name(src)] with [BT.name]")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has traumatized [key_name_admin(src)] with [BT.name].</span>")
|
||||
if(href_list[VV_HK_CURE_TRAUMA])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
log_admin("[key_name(usr)] has cured all traumas from [key_name(src)].")
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] has cured all traumas from [key_name_admin(src)].</span>")
|
||||
if(href_list[VV_HK_HALLUCINATION])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
var/list/hallucinations = subtypesof(/datum/hallucination)
|
||||
var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations
|
||||
if(!usr)
|
||||
return
|
||||
if(QDELETED(src))
|
||||
to_chat(usr, "Mob doesn't exist anymore")
|
||||
return
|
||||
if(result)
|
||||
new result(src, TRUE)
|
||||
|
||||
/mob/living/carbon/can_resist()
|
||||
return bodyparts.len > 2 && ..()
|
||||
|
||||
@@ -86,13 +86,10 @@
|
||||
if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
totitemdamage *= 1.5
|
||||
//CIT CHANGES END HERE
|
||||
if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
|
||||
var/impacting_zone = (user == src)? check_zone(user.zone_selected) : ran_zone(user.zone_selected)
|
||||
if((user != src) && (run_block(I, totitemdamage, "the [I]", ATTACK_TYPE_MELEE, I.armour_penetration, user, impacting_zone) & BLOCK_SUCCESS))
|
||||
return FALSE
|
||||
var/obj/item/bodypart/affecting
|
||||
if(user == src)
|
||||
affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay!
|
||||
else
|
||||
affecting = get_bodypart(ran_zone(user.zone_selected))
|
||||
var/obj/item/bodypart/affecting = get_bodypart(impacting_zone)
|
||||
if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest)
|
||||
affecting = bodyparts[1]
|
||||
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
|
||||
|
||||
@@ -849,15 +849,95 @@
|
||||
|
||||
/mob/living/carbon/human/vv_get_dropdown()
|
||||
. = ..()
|
||||
. += "---"
|
||||
.["Make monkey"] = "?_src_=vars;[HrefToken()];makemonkey=[REF(src)]"
|
||||
.["Set Species"] = "?_src_=vars;[HrefToken()];setspecies=[REF(src)]"
|
||||
.["Make cyborg"] = "?_src_=vars;[HrefToken()];makerobot=[REF(src)]"
|
||||
.["Make alien"] = "?_src_=vars;[HrefToken()];makealien=[REF(src)]"
|
||||
.["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=[REF(src)]"
|
||||
.["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=[REF(src)]"
|
||||
.["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]"
|
||||
.["Add/Remove Quirks"] = "?_src_=vars;[HrefToken()];modquirks=[REF(src)]"
|
||||
VV_DROPDOWN_OPTION("", "---------")
|
||||
VV_DROPDOWN_OPTION(VV_HK_COPY_OUTFIT, "Copy Outfit")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MOD_QUIRKS, "Add/Remove Quirks")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MAKE_MONKEY, "Make Monkey")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MAKE_CYBORG, "Make Cyborg")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MAKE_SLIME, "Make Slime")
|
||||
VV_DROPDOWN_OPTION(VV_HK_MAKE_ALIEN, "Make Alien")
|
||||
VV_DROPDOWN_OPTION(VV_HK_SET_SPECIES, "Set Species")
|
||||
VV_DROPDOWN_OPTION(VV_HK_PURRBATION, "Toggle Purrbation")
|
||||
|
||||
/mob/living/carbon/human/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
if(href_list[VV_HK_COPY_OUTFIT])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
copy_outfit()
|
||||
if(href_list[VV_HK_MOD_QUIRKS])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
|
||||
var/list/options = list("Clear"="Clear")
|
||||
for(var/x in subtypesof(/datum/quirk))
|
||||
var/datum/quirk/T = x
|
||||
var/qname = initial(T.name)
|
||||
options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
|
||||
|
||||
var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options
|
||||
if(result)
|
||||
if(result == "Clear")
|
||||
for(var/datum/quirk/q in roundstart_quirks)
|
||||
remove_quirk(q.type)
|
||||
else
|
||||
var/T = options[result]
|
||||
if(has_quirk(T))
|
||||
remove_quirk(T)
|
||||
else
|
||||
add_quirk(T,TRUE)
|
||||
if(href_list[VV_HK_MAKE_MONKEY])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
usr.client.holder.Topic("vv_override", list("monkeyone"=href_list[VV_HK_TARGET]))
|
||||
if(href_list[VV_HK_MAKE_CYBORG])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
usr.client.holder.Topic("vv_override", list("makerobot"=href_list[VV_HK_TARGET]))
|
||||
if(href_list[VV_HK_MAKE_ALIEN])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
usr.client.holder.Topic("vv_override", list("makealien"=href_list[VV_HK_TARGET]))
|
||||
if(href_list[VV_HK_MAKE_SLIME])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
|
||||
return
|
||||
usr.client.holder.Topic("vv_override", list("makeslime"=href_list[VV_HK_TARGET]))
|
||||
if(href_list[VV_HK_SET_SPECIES])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
|
||||
if(result)
|
||||
var/newtype = GLOB.species_list[result]
|
||||
admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src] to [result]")
|
||||
set_species(newtype)
|
||||
if(href_list[VV_HK_PURRBATION])
|
||||
if(!check_rights(R_SPAWN))
|
||||
return
|
||||
if(!ishumanbasic(src))
|
||||
to_chat(usr, "This can only be done to the basic human species at the moment.")
|
||||
return
|
||||
var/success = purrbation_toggle(src)
|
||||
if(success)
|
||||
to_chat(usr, "Put [src] on purrbation.")
|
||||
log_admin("[key_name(usr)] has put [key_name(src)] on purrbation.")
|
||||
var/msg = "<span class='notice'>[key_name_admin(usr)] has put [key_name(src)] on purrbation.</span>"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(src, msg)
|
||||
|
||||
else
|
||||
to_chat(usr, "Removed [src] from purrbation.")
|
||||
log_admin("[key_name(usr)] has removed [key_name(src)] from purrbation.")
|
||||
var/msg = "<span class='notice'>[key_name_admin(usr)] has removed [key_name(src)] from purrbation.</span>"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(src, msg)
|
||||
|
||||
/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user)
|
||||
if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/mob/living/carbon/human/get_blocking_items()
|
||||
. = ..()
|
||||
if(wear_suit)
|
||||
. += wear_suit
|
||||
if(w_uniform)
|
||||
. += w_uniform
|
||||
if(wear_neck)
|
||||
. += wear_neck
|
||||
@@ -52,36 +52,12 @@
|
||||
return martial_art_result
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/check_reflect(def_zone)
|
||||
if(wear_suit?.IsReflect(def_zone))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
var/block_chance_modifier = round(damage / -3)
|
||||
if(wear_suit)
|
||||
var/final_block_chance = wear_suit.block_chance - (CLAMP((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return TRUE
|
||||
if(w_uniform)
|
||||
var/final_block_chance = w_uniform.block_chance - (CLAMP((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return TRUE
|
||||
if(wear_neck)
|
||||
var/final_block_chance = wear_neck.block_chance - (CLAMP((armour_penetration-wear_neck.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/can_embed(obj/item/I)
|
||||
if(I.get_sharpness() || is_pointed(I) || is_type_in_typecache(I, GLOB.can_embed_types))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/proc/check_block()
|
||||
/mob/living/carbon/human/proc/check_martial_melee_block()
|
||||
if(mind)
|
||||
if(mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
|
||||
return TRUE
|
||||
|
||||
@@ -1437,7 +1437,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
to_chat(user, "<span class='notice'>You do not breathe, so you cannot perform CPR.</span>")
|
||||
|
||||
/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
||||
if(target.check_block())
|
||||
if(target.check_martial_melee_block())
|
||||
target.visible_message("<span class='warning'>[target] blocks [user]'s grab attempt!</span>")
|
||||
return 0
|
||||
if(attacker_style && attacker_style.grab_act(user,target))
|
||||
@@ -1453,9 +1453,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(IS_STAMCRIT(user)) //CITADEL CHANGE - makes it impossible to punch while in stamina softcrit
|
||||
to_chat(user, "<span class='warning'>You're too exhausted.</span>") //CITADEL CHANGE - ditto
|
||||
return FALSE //CITADEL CHANGE - ditto
|
||||
if(target.check_block())
|
||||
if(target.check_martial_melee_block())
|
||||
target.visible_message("<span class='warning'>[target] blocks [user]'s attack!</span>")
|
||||
return FALSE
|
||||
|
||||
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount
|
||||
user.adjustStaminaLossBuffered(1.5)
|
||||
else
|
||||
user.adjustStaminaLossBuffered(3.5)
|
||||
|
||||
if(attacker_style && attacker_style.harm_act(user,target))
|
||||
return TRUE
|
||||
else
|
||||
@@ -1474,13 +1480,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
else
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
|
||||
user.adjustStaminaLossBuffered(5) //CITADEL CHANGE - makes punching cause staminaloss
|
||||
|
||||
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
|
||||
var/puncherstam = user.getStaminaLoss()
|
||||
var/puncherbrute = user.getBruteLoss()
|
||||
var/punchedstam = target.getStaminaLoss()
|
||||
var/punchedbrute = target.getBruteLoss()
|
||||
|
||||
//CITADEL CHANGES - makes resting and disabled combat mode reduce punch damage, makes being out of combat mode result in you taking more damage
|
||||
if(!(target.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && damage < user.dna.species.punchstunthreshold)
|
||||
damage = user.dna.species.punchstunthreshold - 1
|
||||
if(!(target.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
damage *= 1.5
|
||||
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
||||
damage *= 0.5
|
||||
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
|
||||
@@ -1494,7 +1502,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
|
||||
miss_chance = 0
|
||||
else
|
||||
miss_chance = min((user.dna.species.punchdamagehigh/user.dna.species.punchdamagelow) + user.getStaminaLoss() + (user.getBruteLoss()*0.5), 100) //old base chance for a miss + various damage. capped at 100 to prevent weirdness in prob()
|
||||
miss_chance = min(10 + ((puncherstam + puncherbrute)*0.5), 100) //probability of miss has a base of 10, and modified based on half your stamina and brute total. Capped at max 100 and min 0 to prevent weirdness in prob()
|
||||
|
||||
if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
|
||||
playsound(target.loc, user.dna.species.miss_sound, 25, TRUE, -1)
|
||||
@@ -1529,13 +1537,25 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
log_combat(user, target, "punched")
|
||||
|
||||
if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold)
|
||||
target.visible_message("<span class='danger'>[user] knocks [target] down!</span>", \
|
||||
"<span class='userdanger'>You're knocked down by [user]!</span>", "<span class='hear'>You hear aggressive shuffling followed by a loud thud!</span>", COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, "<span class='danger'>You knock [target] down!</span>")
|
||||
var/knockdown_duration = 40 + (target.getStaminaLoss() + (target.getBruteLoss()*0.5))*0.8 - armor_block
|
||||
target.DefaultCombatKnockdown(knockdown_duration)
|
||||
target.forcesay(GLOB.hit_appends)
|
||||
log_combat(user, target, "got a stun punch with their previous punch")
|
||||
if((punchedstam > 50) && prob(punchedstam*0.5)) //If our punch victim has been hit above the threshold, and they have more than 50 stamina damage, roll for stun, probability of 1% per 2 stamina damage
|
||||
|
||||
target.visible_message("<span class='danger'>[user] knocks [target] down!</span>", \
|
||||
"<span class='userdanger'>You're knocked down by [user]!</span>", "<span class='hear'>You hear aggressive shuffling followed by a loud thud!</span>", COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, "<span class='danger'>You knock [target] down!</span>")
|
||||
|
||||
var/knockdown_duration = 40 + (punchedstam + (punchedbrute*0.5))*0.8 - armor_block
|
||||
target.DefaultCombatKnockdown(knockdown_duration)
|
||||
target.forcesay(GLOB.hit_appends)
|
||||
log_combat(user, target, "got a stun punch with their previous punch")
|
||||
|
||||
if(HAS_TRAIT(user, TRAIT_KI_VAMPIRE) && !HAS_TRAIT(target, TRAIT_NOBREATH) && (punchedbrute < 100)) //If we're a ki vampire we also sap them of lifeforce, but only if they're not too beat up. Also living organics only.
|
||||
user.adjustBruteLoss(-5)
|
||||
user.adjustFireLoss(-5)
|
||||
user.adjustStaminaLoss(-20)
|
||||
|
||||
target.adjustCloneLoss(10)
|
||||
target.adjustBruteLoss(10)
|
||||
|
||||
else if(!(target.mobility_flags & MOBILITY_STAND))
|
||||
target.forcesay(GLOB.hit_appends)
|
||||
|
||||
@@ -1552,6 +1572,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
var/aim_for_groin = user.zone_selected == "groin"
|
||||
var/target_aiming_for_groin = target.zone_selected == "groin"
|
||||
|
||||
if(target.check_martial_melee_block()) //END EDIT
|
||||
target.visible_message("<span class='warning'>[target] blocks [user]'s disarm attempt!</span>")
|
||||
return FALSE
|
||||
if(IS_STAMCRIT(user))
|
||||
to_chat(user, "<span class='warning'>You're too exhausted!</span>")
|
||||
return FALSE
|
||||
@@ -1680,9 +1703,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H)
|
||||
// Allows you to put in item-specific reactions based on species
|
||||
if(user != H)
|
||||
if(H.check_shields(I, I.force, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
|
||||
if(H.run_block(I, I.force, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone) & BLOCK_SUCCESS)
|
||||
return 0
|
||||
if(H.check_block())
|
||||
if(H.check_martial_melee_block())
|
||||
H.visible_message("<span class='warning'>[H] blocks [I]!</span>")
|
||||
return 0
|
||||
|
||||
@@ -1798,7 +1821,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
return TRUE
|
||||
if(M.mind)
|
||||
attacker_style = M.mind.martial_art
|
||||
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
|
||||
if((M != H) && M.a_intent != INTENT_HELP && (H.run_block(M, 0, "[M]", ATTACK_TYPE_UNARMED, 0, M, M.zone_selected) & BLOCK_SUCCESS))
|
||||
log_combat(M, H, "attempted to touch")
|
||||
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>")
|
||||
return TRUE
|
||||
@@ -1836,7 +1859,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
if(IS_STAMCRIT(user))
|
||||
to_chat(user, "<span class='warning'>You're too exhausted.</span>")
|
||||
return FALSE
|
||||
if(target.check_block())
|
||||
if(target.check_martial_melee_block())
|
||||
target.visible_message("<span class='warning'>[target] blocks [user]'s shoving attempt!</span>")
|
||||
return FALSE
|
||||
if(attacker_style && attacker_style.disarm_act(user,target))
|
||||
|
||||
@@ -92,6 +92,8 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
//These count in on_life ticks which should be 2 seconds per every increment of 1 in a perfect world.
|
||||
var/dwarf_filth_ticker = 0 //Currently set =< 4, that means this will fire the proc around every 4-8 seconds.
|
||||
var/dwarf_eth_ticker = 0 //Currently set =< 1, that means this will fire the proc around every 2 seconds
|
||||
var/last_filth_spam
|
||||
var/last_alcohol_spam
|
||||
|
||||
/obj/item/organ/dwarfgland/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
@@ -136,40 +138,39 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
filth_counter += 10 //Dwarves could technically chainstun each other in a vomit tantrum spiral.
|
||||
switch(filth_counter)
|
||||
if(11 to 25)
|
||||
if(prob(25))
|
||||
to_chat(owner, "<span class = 'danger'>Someone should really clean up in here!</span>")
|
||||
if(last_filth_spam + 40 SECONDS < world.time)
|
||||
to_chat(owner, "<span class = 'warning'>Someone should really clean up in here!</span>")
|
||||
last_filth_spam = world.time
|
||||
if(26 to 50)
|
||||
if(prob(30)) //Probability the message appears
|
||||
if(prob(6)) //And then the probability they vomit along with it.
|
||||
to_chat(owner, "<span class = 'danger'>The stench makes you queasy.</span>")
|
||||
if(prob(20)) //And then the probability they vomit along with it.
|
||||
owner.vomit(20) //I think vomit should stay over a disgust adjustment.
|
||||
owner.vomit(10) //I think vomit should stay over a disgust adjustment.
|
||||
if(51 to 75)
|
||||
if(prob(35))
|
||||
if(prob(9))
|
||||
to_chat(owner, "<span class = 'danger'>By Armok! You won't be able to keep alcohol down at all!</span>")
|
||||
if(prob(25))
|
||||
owner.vomit(20) //Its more funny
|
||||
owner.vomit(20) //Its more funny
|
||||
if(76 to 100)
|
||||
if(prob(40))
|
||||
if(prob(11))
|
||||
to_chat(owner, "<span class = 'userdanger'>You can't live in such FILTH!</span>")
|
||||
if(prob(25))
|
||||
owner.adjustToxLoss(10) //Now they start dying.
|
||||
owner.vomit(20)
|
||||
owner.adjustToxLoss(10) //Now they start dying.
|
||||
owner.vomit(20)
|
||||
if(101 to INFINITY) //Now they will really start dying
|
||||
if(prob(40))
|
||||
if(last_filth_spam + 12 SECONDS < world.time)
|
||||
to_chat(owner, "<span class = 'userdanger'> THERES TOO MUCH FILTH, OH GODS THE FILTH!</span>")
|
||||
last_filth_spam = world.time
|
||||
if(prob(40))
|
||||
owner.adjustToxLoss(15)
|
||||
owner.vomit(40)
|
||||
owner.vomit(30)
|
||||
CHECK_TICK //Check_tick right here, its motherfuckin magic. (To me at least)
|
||||
|
||||
//Handles the dwarf alcohol cycle tied to on_life, it ticks in dwarf_cycle_ticker.
|
||||
/obj/item/organ/dwarfgland/proc/dwarf_eth_cycle()
|
||||
//BOOZE POWER
|
||||
var/init_stored_alcohol = stored_alcohol
|
||||
for(var/datum/reagent/R in owner.reagents.reagent_list)
|
||||
if(istype(R, /datum/reagent/consumable/ethanol))
|
||||
var/datum/reagent/consumable/ethanol/E = R
|
||||
stored_alcohol += (E.boozepwr / 50)
|
||||
if(stored_alcohol > max_alcohol) //Dwarves technically start at 250 alcohol stored.
|
||||
stored_alcohol = max_alcohol
|
||||
stored_alcohol = CLAMP(stored_alcohol + E.boozepwr / 50, 0, max_alcohol)
|
||||
var/heal_amt = heal_rate
|
||||
stored_alcohol -= alcohol_rate //Subtracts alcohol_Rate from stored alcohol so EX: 250 - 0.25 per each loop that occurs.
|
||||
if(stored_alcohol > 400) //If they are over 400 they start regenerating
|
||||
@@ -177,16 +178,27 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
|
||||
owner.adjustFireLoss(-heal_amt) //Unless they drink casually all the time.
|
||||
owner.adjustOxyLoss(-heal_amt)
|
||||
owner.adjustCloneLoss(-heal_amt) //Also they will probably get brain damage if thats a thing here.
|
||||
if(prob(25))
|
||||
switch(stored_alcohol)
|
||||
if(0 to 24)
|
||||
if(init_stored_alcohol + 0.5 < stored_alcohol) //recovering stored alcohol at a steady rate of +0.75, no spam.
|
||||
return
|
||||
switch(stored_alcohol)
|
||||
if(0 to 24)
|
||||
if(last_alcohol_spam + 8 SECONDS < world.time)
|
||||
to_chat(owner, "<span class='userdanger'>DAMNATION INCARNATE, WHY AM I CURSED WITH THIS DRY-SPELL? I MUST DRINK.</span>")
|
||||
owner.adjustToxLoss(35)
|
||||
if(25 to 50)
|
||||
last_alcohol_spam = world.time
|
||||
owner.adjustToxLoss(10)
|
||||
if(25 to 50)
|
||||
if(last_alcohol_spam + 20 SECONDS < world.time)
|
||||
to_chat(owner, "<span class='danger'>Oh DAMN, I need some brew!</span>")
|
||||
if(51 to 75)
|
||||
last_alcohol_spam = world.time
|
||||
if(51 to 75)
|
||||
if(last_alcohol_spam + 35 SECONDS < world.time)
|
||||
to_chat(owner, "<span class='warning'>Your body aches, you need to get ahold of some booze...</span>")
|
||||
if(76 to 100)
|
||||
last_alcohol_spam = world.time
|
||||
if(76 to 100)
|
||||
if(last_alcohol_spam + 40 SECONDS < world.time)
|
||||
to_chat(owner, "<span class='notice'>A pint of anything would really hit the spot right now.</span>")
|
||||
if(101 to 150)
|
||||
last_alcohol_spam = world.time
|
||||
if(101 to 150)
|
||||
if(last_alcohol_spam + 50 SECONDS < world.time)
|
||||
to_chat(owner, "<span class='notice'>You feel like you could use a good brew.</span>")
|
||||
last_alcohol_spam = world.time
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
siemens_coeff = 0
|
||||
punchdamagelow = 5
|
||||
punchdamagehigh = 14
|
||||
punchstunthreshold = 11 //about 40% chance to stun
|
||||
punchstunthreshold = 10
|
||||
no_equip = list(SLOT_WEAR_MASK, SLOT_WEAR_SUIT, SLOT_GLOVES, SLOT_SHOES, SLOT_W_UNIFORM, SLOT_S_STORE)
|
||||
nojumpsuit = 1
|
||||
sexes = 1
|
||||
@@ -168,7 +168,7 @@
|
||||
name = "Silver Golem"
|
||||
id = "silver golem"
|
||||
fixed_mut_color = "ddd"
|
||||
punchstunthreshold = 9 //60% chance, from 40%
|
||||
punchstunthreshold = 9
|
||||
meat = /obj/item/stack/ore/silver
|
||||
info_text = "As a <span class='danger'>Silver Golem</span>, your attacks have a higher chance of stunning. Being made of silver, your body is immune to most types of magic."
|
||||
prefix = "Silver"
|
||||
@@ -190,7 +190,7 @@
|
||||
stunmod = 0.4
|
||||
punchdamagelow = 12
|
||||
punchdamagehigh = 21
|
||||
punchstunthreshold = 18 //still 40% stun chance
|
||||
punchstunthreshold = 18
|
||||
speedmod = 4 //pretty fucking slow
|
||||
meat = /obj/item/stack/ore/iron
|
||||
info_text = "As a <span class='danger'>Plasteel Golem</span>, you are slower, but harder to stun, and hit very hard when punching."
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
inherent_traits = list(TRAIT_NOBREATH)
|
||||
speedmod = 1.5 //faster than golems but not by much
|
||||
|
||||
punchdamagelow = 6
|
||||
punchdamagehigh = 14
|
||||
punchstunthreshold = 14 //about 44% chance to stun
|
||||
punchdamagelow = 2
|
||||
punchdamagehigh = 12 //still better than humans
|
||||
punchstunthreshold = 10
|
||||
|
||||
no_equip = list(SLOT_WEAR_MASK, SLOT_WEAR_SUIT, SLOT_GLOVES, SLOT_SHOES, SLOT_W_UNIFORM)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
armor = 25
|
||||
punchdamagelow = 10
|
||||
punchdamagehigh = 19
|
||||
punchstunthreshold = 14 //about 50% chance to stun
|
||||
punchstunthreshold = 14
|
||||
disguise_fail_health = 50
|
||||
|
||||
/datum/species/synth/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
|
||||
|
||||
@@ -146,5 +146,7 @@
|
||||
if(transfer_name)
|
||||
H.name = caster.name
|
||||
|
||||
clothes_req = 0
|
||||
human_req = 0
|
||||
|
||||
clothes_req = NONE
|
||||
mobs_whitelist = null
|
||||
mobs_blacklist = null
|
||||
|
||||
@@ -377,9 +377,15 @@
|
||||
|
||||
var/turf/open/miasma_turf = deceasedturf
|
||||
|
||||
var/list/cached_gases = miasma_turf.air.gases
|
||||
var/datum/gas_mixture/stank = new
|
||||
|
||||
cached_gases[/datum/gas/miasma] += 0.1
|
||||
stank.gases[/datum/gas/miasma] = 0.1
|
||||
|
||||
stank.temperature = BODYTEMP_NORMAL
|
||||
|
||||
miasma_turf.assume_air(stank)
|
||||
|
||||
miasma_turf.air_update_turf()
|
||||
|
||||
/mob/living/carbon/proc/handle_blood()
|
||||
return
|
||||
|
||||
@@ -1198,3 +1198,19 @@
|
||||
gender = ngender
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/vv_get_header()
|
||||
. = ..()
|
||||
var/refid = REF(src)
|
||||
. += {"
|
||||
<br><font size='1'>[VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[ckey || "no ckey"]", NAMEOF(src, ckey))] / [VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[real_name || "no real name"]", NAMEOF(src, real_name))]</font>
|
||||
<br><font size='1'>
|
||||
BRUTE:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=brute' id='brute'>[getBruteLoss()]</a>
|
||||
FIRE:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=fire' id='fire'>[getFireLoss()]</a>
|
||||
TOXIN:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=toxin' id='toxin'>[getToxLoss()]</a>
|
||||
OXY:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=oxygen' id='oxygen'>[getOxyLoss()]</a>
|
||||
CLONE:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=clone' id='clone'>[getCloneLoss()]</a>
|
||||
BRAIN:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=brain' id='brain'>[getOrganLoss(ORGAN_SLOT_BRAIN)]</a>
|
||||
STAMINA:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=stamina' id='stamina'>[getStaminaLoss()]</a>
|
||||
</font>
|
||||
"}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// This file has a weird name, but it's for anything related to the checks for shields, blocking, dodging, and similar "stop this attack before it actually impacts the target" as opposed to "defend once it has hit".
|
||||
|
||||
/*
|
||||
/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS.
|
||||
/// Attack was not blocked
|
||||
#define BLOCK_NONE NONE
|
||||
/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION!
|
||||
#define BLOCK_SUCCESS (1<<1)
|
||||
|
||||
/// The below are for "metadata" on "how" the attack was blocked.
|
||||
|
||||
/// Attack was and should be reflected (NOTE: the SHOULD here is important, as it says "the thing blocking isn't handling the reflecting for you so do it yourself"!)
|
||||
#define BLOCK_SHOULD_REFLECT (1<<2)
|
||||
/// Attack was manually redirected (including reflected) by any means by the defender. For when YOU are handling the reflection, rather than the thing hitting you. (see sleeping carp)
|
||||
#define BLOCK_REDIRECTED (1<<3)
|
||||
/// Attack was blocked by something like a shield.
|
||||
#define BLOCK_PHYSICAL_EXTERNAL (1<<4)
|
||||
/// Attack was blocked by something worn on you.
|
||||
#define BLOCK_PHYSICAL_INTERNAL (1<<5)
|
||||
/// Attack should pass through. Like SHOULD_REFLECT but for.. well, passing through harmlessly.
|
||||
#define BLOCK_SHOULD_PASSTHROUGH (1<<6)
|
||||
/// Attack outright missed because the target dodged. Should usually be combined with SHOULD_PASSTHROUGH or something (see martial arts)
|
||||
#define BLOCK_TARGET_DODGED (1<<7)
|
||||
*/
|
||||
|
||||
///Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields. Wrapper for do_run_block(). The arguments on that means the same as for this.
|
||||
/mob/living/proc/check_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list)
|
||||
return do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list)
|
||||
|
||||
/// Runs a block "sequence", effectively checking and then doing effects if necessary. Wrapper for do_run_block(). The arguments on that means the same as for this.
|
||||
/mob/living/proc/run_block(atom/object, damage, attack_text = "the attack", attack_type, armour_penetration, mob/attacker, def_zone, list/return_list)
|
||||
return do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list)
|
||||
|
||||
/** The actual proc for block checks. DO NOT USE THIS DIRECTLY UNLESS YOU HAVE VERY GOOD REASON TO. To reduce copypaste for differences between handling for real attacks and virtual checks.
|
||||
* Automatically checks all held items for /obj/item/proc/run_block() with the same parameters.
|
||||
* @params
|
||||
* real_attack - If this attack is real. This one is quirky; If it's real, run_block is called. If it's not, check_block is called and none of the regular checks happen, and this is effectively only useful
|
||||
* for populating return_list with blocking metadata.
|
||||
* object - Whatever /atom is actually hitting us, in essence. For example, projectile if gun, item if melee, structure/whatever if it's a thrown, etc.
|
||||
* damage - The nominal damage this would do if it was to hit. Obviously doesn't take into effect explosions/magic/similar things.. unless you implement it to raise the value.
|
||||
* attack_text - The text that this attack should show, in the context of something like "[src] blocks [attack_text]!"
|
||||
* attack_type - See __DEFINES/combat.dm - Attack types, to distinguish between, for example, someone throwing an item at us vs bashing us with it.
|
||||
* armour_penetration - 0-100 value of how effectively armor penetrating the attack should be.
|
||||
* attacker - Set to the mob attacking IF KNOWN. Do not expect this to always be set!
|
||||
* def_zone - The zone this'll impact.
|
||||
* return_list - If something wants to grab things from what items/whatever put into list/block_return on obj/item/run_block and the comsig, pass in a list so you can grab anything put in it after block runs.
|
||||
*/
|
||||
/mob/living/proc/do_run_block(real_attack = TRUE, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list())
|
||||
// Component signal block runs have highest priority.. for now.
|
||||
. = SEND_SIGNAL(src, COMSIG_LIVING_RUN_BLOCK, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)
|
||||
if((. & BLOCK_SUCCESS) && !(. & BLOCK_CONTINUE_CHAIN))
|
||||
return
|
||||
var/list/obj/item/tocheck = get_blocking_items()
|
||||
sortTim(tocheck, /proc/cmp_item_block_priority_asc)
|
||||
// i don't like this
|
||||
var/block_chance_modifier = round(damage / -3)
|
||||
if(real_attack)
|
||||
for(var/obj/item/I in tocheck)
|
||||
// i don't like this too
|
||||
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
|
||||
var/results = I.run_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
|
||||
. |= results
|
||||
if((results & BLOCK_SUCCESS) && !(results & BLOCK_CONTINUE_CHAIN))
|
||||
break
|
||||
else
|
||||
for(var/obj/item/I in tocheck)
|
||||
// i don't like this too
|
||||
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
|
||||
I.check_block(src, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, return_list)
|
||||
|
||||
/// Gets an unsortedlist of objects to run block checks on.
|
||||
/mob/living/proc/get_blocking_items()
|
||||
. = list()
|
||||
for(var/obj/item/I in held_items)
|
||||
// this is a bad check but i am not removing it until a better catchall is made
|
||||
if(istype(I, /obj/item/clothing))
|
||||
continue
|
||||
. |= I
|
||||
|
||||
/obj/item
|
||||
/// The 0% to 100% chance for the default implementation of random block rolls.
|
||||
var/block_chance = 0
|
||||
/// Block priority, higher means we check this higher in the "chain".
|
||||
var/block_priority = BLOCK_PRIORITY_DEFAULT
|
||||
|
||||
/// Runs block and returns flag for do_run_block to process.
|
||||
/obj/item/proc/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_RUN_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
if(prob(final_block_chance))
|
||||
owner.visible_message("<span class='danger'>[owner] blocks [attack_text] with [src]!</span>")
|
||||
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
return BLOCK_NONE
|
||||
|
||||
/// Returns block information using list/block_return. Used for check_block() on mobs.
|
||||
/obj/item/proc/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_CHECK_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
var/existing = block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE]
|
||||
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = max(existing || 0, final_block_chance)
|
||||
@@ -36,48 +36,44 @@
|
||||
/mob/living/proc/on_hit(obj/item/projectile/P)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/mob/living/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
|
||||
var/block_chance_modifier = round(damage / -3)
|
||||
for(var/obj/item/I in held_items)
|
||||
if(!istype(I, /obj/item/clothing))
|
||||
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
|
||||
if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/check_reflect(def_zone) //Reflection checks for anything in your hands, based on the reflection chance of the object(s)
|
||||
for(var/obj/item/I in held_items)
|
||||
if(I.IsReflect(def_zone))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/reflect_bullet_check(obj/item/projectile/P, def_zone)
|
||||
if(P.is_reflectable && check_reflect(def_zone)) // Checks if you've passed a reflection% check
|
||||
visible_message("<span class='danger'>The [P.name] gets reflected by [src]!</span>", \
|
||||
"<span class='userdanger'>The [P.name] gets reflected by [src]!</span>")
|
||||
// Find a turf near or on the original location to bounce to
|
||||
if(P.starting)
|
||||
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/turf/curloc = get_turf(src)
|
||||
// redirect the projectile
|
||||
P.original = locate(new_x, new_y, P.z)
|
||||
P.starting = curloc
|
||||
P.firer = src
|
||||
P.yo = new_y - curloc.y
|
||||
P.xo = new_x - curloc.x
|
||||
var/new_angle_s = P.Angle + rand(120,240)
|
||||
while(new_angle_s > 180) // Translate to regular projectile degrees
|
||||
new_angle_s -= 360
|
||||
P.setAngle(new_angle_s)
|
||||
return TRUE
|
||||
return FALSE
|
||||
/mob/living/proc/handle_projectile_attack_redirection(obj/item/projectile/P, redirection_mode, silent = FALSE)
|
||||
P.ignore_source_check = TRUE
|
||||
switch(redirection_mode)
|
||||
if(REDIRECT_METHOD_DEFLECT)
|
||||
P.setAngle(SIMPLIFY_DEGREES(P.Angle + rand(120, 240)))
|
||||
if(!silent)
|
||||
visible_message("<span class='danger'>[P] gets deflected by [src]!</span>", \
|
||||
"<span class='userdanger'>[P] gets deflected by [src]!</span>")
|
||||
if(REDIRECT_METHOD_REFLECT)
|
||||
P.setAngle(SIMPLIFY_DEGREES(P.Angle + 180))
|
||||
if(!silent)
|
||||
visible_message("<span class='danger'>[P] gets reflected by [src]!</span>", \
|
||||
"<span class='userdanger'>[P] gets reflected by [src]!</span>")
|
||||
if(REDIRECT_METHOD_PASSTHROUGH)
|
||||
if(!silent)
|
||||
visible_message("<span class='danger'>[P] passes through [src]!</span>", \
|
||||
"<span class='userdanger'>[P] passes through [src]!</span>")
|
||||
return
|
||||
if(REDIRECT_METHOD_RETURN_TO_SENDER)
|
||||
if(!silent)
|
||||
visible_message("<span class='danger'>[src] deflects [P] back at their attacker!</span>", \
|
||||
"<span class='userdanger'>[src] deflects [P] back at their attacker!</span>")
|
||||
if(P.firer)
|
||||
P.setAngle(Get_Angle(src, P.firer))
|
||||
else
|
||||
P.setAngle(SIMPLIFY_DEGREES(P.Angle + 180))
|
||||
else
|
||||
CRASH("Invalid rediretion mode [redirection_mode]")
|
||||
|
||||
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
|
||||
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
|
||||
if(reflect_bullet_check(P, def_zone))
|
||||
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
|
||||
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
|
||||
var/list/returnlist = list()
|
||||
var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist)
|
||||
if(returned & BLOCK_SHOULD_REDIRECT)
|
||||
handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD])
|
||||
if(returned & BLOCK_REDIRECTED)
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
if(returned & BLOCK_SUCCESS)
|
||||
P.on_hit(src, 100, def_zone)
|
||||
return BULLET_ACT_BLOCK
|
||||
var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null)
|
||||
@@ -108,12 +104,14 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
|
||||
// Throwingdatum can be null if someone had an accident() while slipping with an item in hand.
|
||||
var/obj/item/I
|
||||
var/throwpower = 30
|
||||
if(isitem(AM))
|
||||
I = AM
|
||||
throwpower = I.throwforce
|
||||
if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK))
|
||||
var/impacting_zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
|
||||
if(run_block(AM, throwpower, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, impacting_zone) & BLOCK_SUCCESS)
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE
|
||||
blocked = TRUE
|
||||
@@ -124,10 +122,9 @@
|
||||
if(I)
|
||||
if(!skipcatch && isturf(I.loc) && catch_item(I))
|
||||
return TRUE
|
||||
var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
|
||||
var/dtype = BRUTE
|
||||
var/volume = I.get_volume_by_throwforce_and_or_w_class()
|
||||
SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, zone)
|
||||
SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, impacting_zone)
|
||||
dtype = I.damtype
|
||||
|
||||
if (I.throwforce > 0) //If the weapon's throwforce is greater than zero...
|
||||
@@ -145,8 +142,8 @@
|
||||
if(!blocked)
|
||||
visible_message("<span class='danger'>[src] has been hit by [I].</span>", \
|
||||
"<span class='userdanger'>[src] has been hit by [I].</span>")
|
||||
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
|
||||
apply_damage(I.throwforce, dtype, zone, armor)
|
||||
var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration)
|
||||
apply_damage(I.throwforce, dtype, impacting_zone, armor)
|
||||
if(I.thrownby)
|
||||
log_combat(I.thrownby, src, "threw and hit", I)
|
||||
else
|
||||
@@ -193,17 +190,6 @@
|
||||
if(user == anchored || !isturf(user.loc))
|
||||
return FALSE
|
||||
|
||||
//pacifist vore check.
|
||||
if(user.pulling && HAS_TRAIT(user, TRAIT_PACIFISM) && user.voremode) //they can only do heals, noisy guts, absorbing (technically not harm)
|
||||
if(ismob(user.pulling))
|
||||
var/mob/P = user.pulling
|
||||
if(src != user)
|
||||
to_chat(user, "<span class='notice'>You can't risk digestion!</span>")
|
||||
return FALSE
|
||||
else
|
||||
user.vore_attack(user, P, user)
|
||||
return
|
||||
|
||||
//normal vore check.
|
||||
if(user.pulling && user.grab_state == GRAB_AGGRESSIVE && user.voremode)
|
||||
if(ismob(user.pulling))
|
||||
@@ -283,7 +269,7 @@
|
||||
/mob/living/attack_hand(mob/user)
|
||||
..() //Ignoring parent return value here.
|
||||
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user)
|
||||
if((user != src) && user.a_intent != INTENT_HELP && check_shields(user, 0, user.name, attack_type = UNARMED_ATTACK))
|
||||
if((user != src) && user.a_intent != INTENT_HELP && (run_block(user, 0, user.name, ATTACK_TYPE_UNARMED | ATTACK_TYPE_MELEE, null, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS))
|
||||
log_combat(user, src, "attempted to touch")
|
||||
visible_message("<span class='warning'>[user] attempted to touch [src]!</span>")
|
||||
return TRUE
|
||||
@@ -294,7 +280,7 @@
|
||||
to_chat(user, "<span class='notice'>You don't want to hurt [src]!</span>")
|
||||
return TRUE
|
||||
var/hulk_verb = pick("smash","pummel")
|
||||
if(user != src && check_shields(user, 15, "the [hulk_verb]ing"))
|
||||
if(user != src && (run_block(user, 15, "the [hulk_verb]ing", ATTACK_TYPE_MELEE, null, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS))
|
||||
return TRUE
|
||||
..()
|
||||
return FALSE
|
||||
@@ -309,14 +295,14 @@
|
||||
M.Feedstop()
|
||||
return // can't attack while eating!
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_PACIFISM))
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(20, 40)
|
||||
if(check_shields(M, damage, "the [M.name]"))
|
||||
if(run_block(M, damage, "the [M.name]", ATTACK_TYPE_MELEE, null, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS)
|
||||
return FALSE
|
||||
|
||||
if (stat != DEAD)
|
||||
@@ -335,7 +321,7 @@
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
if(check_shields(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", MELEE_ATTACK, M.armour_penetration))
|
||||
if(run_block(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", ATTACK_TYPE_MELEE, M.armour_penetration, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS)
|
||||
return FALSE
|
||||
if(M.attack_sound)
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
@@ -345,17 +331,15 @@
|
||||
log_combat(M, src, "attacked")
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/attack_paw(mob/living/carbon/monkey/M)
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
|
||||
if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH))
|
||||
to_chat(M, "<span class='warning'>You can't bite with your mouth covered!</span>")
|
||||
return FALSE
|
||||
if(check_shields(M, 0, "the [M.name]"))
|
||||
if(run_block(M, 0, "the [M.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS)
|
||||
return FALSE
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
|
||||
if (prob(75))
|
||||
@@ -379,7 +363,7 @@
|
||||
if(HAS_TRAIT(L, TRAIT_PACIFISM))
|
||||
to_chat(L, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
if(L != src && check_shields(L, rand(1, 3), "the [L.name]"))
|
||||
if(L != src && (run_block(L, rand(1, 3), "the [L.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, L, check_zone(L.zone_selected)) & BLOCK_SUCCESS))
|
||||
return FALSE
|
||||
L.do_attack_animation(src)
|
||||
if(prob(90))
|
||||
@@ -393,7 +377,7 @@
|
||||
"<span class='userdanger'>[L.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if((M != src) && M.a_intent != INTENT_HELP && check_shields(M, 0, "the [M.name]"))
|
||||
if((M != src) && M.a_intent != INTENT_HELP && (run_block(M, 0, "the [M.name]", ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED, M, check_zone(M.zone_selected)) & BLOCK_SUCCESS))
|
||||
visible_message("<span class='danger'>[M] attempted to touch [src]!</span>")
|
||||
return FALSE
|
||||
switch(M.a_intent)
|
||||
|
||||
@@ -283,9 +283,7 @@
|
||||
T.visible_message("<span class='warning'>A port on [src] opens to reveal [cable], which promptly falls to the floor.</span>", "<span class='italics'>You hear the soft click of something light and hard falling to the ground.</span>")
|
||||
if("loudness")
|
||||
if(subscreen == 1) // Open Instrument
|
||||
internal_instrument.interact(src)
|
||||
if(subscreen == 2) // Change Instrument type
|
||||
internal_instrument.selectInstrument()
|
||||
internal_instrument.ui_interact(src)
|
||||
|
||||
//updateUsrDialog() We only need to account for the single mob this is intended for, and he will *always* be able to call this window
|
||||
paiInterface() // So we'll just call the update directly rather than doing some default checks
|
||||
|
||||
@@ -112,11 +112,15 @@
|
||||
|
||||
/mob/living/silicon/bullet_act(obj/item/projectile/P, def_zone)
|
||||
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
|
||||
if(reflect_bullet_check(P, def_zone))
|
||||
return -1 // complete projectile permutation
|
||||
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
|
||||
var/list/returnlist = list()
|
||||
var/returned = run_block(P, P.damage, "the [P.name]", ATTACK_TYPE_PROJECTILE, P.armour_penetration, P.firer, def_zone, returnlist)
|
||||
if(returned & BLOCK_SHOULD_REDIRECT)
|
||||
handle_projectile_attack_redirection(P, returnlist[BLOCK_RETURN_REDIRECT_METHOD])
|
||||
if(returned & BLOCK_REDIRECTED)
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
if(returned & BLOCK_SUCCESS)
|
||||
P.on_hit(src, 100, def_zone)
|
||||
return 2
|
||||
return BULLET_ACT_BLOCK
|
||||
if((P.damage_type == BRUTE || P.damage_type == BURN))
|
||||
adjustBruteLoss(P.damage)
|
||||
if(prob(P.damage*1.5))
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
if (!(istype(src, /mob/living/simple_animal/pet/gondola/gondolapod)))
|
||||
CreateGondola()
|
||||
|
||||
/mob/living/simple_animal/pet/gondola/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/spellcasting, SPELL_SKIP_VOCAL) // so they can cast spells despite being silent.
|
||||
|
||||
/mob/living/simple_animal/pet/gondola/proc/CreateGondola()
|
||||
icon_state = null
|
||||
icon_living = null
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
var/blocked = FALSE
|
||||
if(hasmatchingsummoner(hit_atom)) //if the summoner matches don't hurt them
|
||||
blocked = TRUE
|
||||
if(L.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK))
|
||||
if(L.run_block(src, 90, "[name]", ATTACK_TYPE_TACKLE, 0, src) & BLOCK_SUCCESS)
|
||||
blocked = TRUE
|
||||
if(!blocked)
|
||||
L.drop_all_held_items()
|
||||
|
||||
@@ -762,7 +762,7 @@ Difficulty: Very Hard
|
||||
name = "Exit Possession"
|
||||
desc = "Exits the body you are possessing."
|
||||
charge_max = 60
|
||||
clothes_req = 0
|
||||
clothes_req = NONE
|
||||
invocation_type = "none"
|
||||
max_targets = 1
|
||||
range = -1
|
||||
|
||||
@@ -40,27 +40,27 @@
|
||||
deathsound = 'sound/magic/demon_dies.ogg'
|
||||
deathmessage = "begins to shudder as it becomes transparent..."
|
||||
loot_drop = /obj/item/clothing/neck/cloak/herald_cloak
|
||||
|
||||
|
||||
can_talk = 1
|
||||
|
||||
attack_action_types = list(/datum/action/innate/elite_attack/herald_trishot,
|
||||
/datum/action/innate/elite_attack/herald_directionalshot,
|
||||
/datum/action/innate/elite_attack/herald_teleshot,
|
||||
/datum/action/innate/elite_attack/herald_mirror)
|
||||
|
||||
|
||||
var/mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/my_mirror = null
|
||||
var/is_mirror = FALSE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/death()
|
||||
. = ..()
|
||||
if(!is_mirror)
|
||||
addtimer(CALLBACK(src, .proc/become_ghost), 8)
|
||||
if(my_mirror != null)
|
||||
qdel(my_mirror)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/become_ghost()
|
||||
icon_state = "herald_ghost"
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
. = ..()
|
||||
if(.)
|
||||
@@ -71,25 +71,25 @@
|
||||
button_icon_state = "herald_trishot"
|
||||
chosen_message = "<span class='boldwarning'>You are now firing three shots in your chosen direction.</span>"
|
||||
chosen_attack_num = HERALD_TRISHOT
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/herald_directionalshot
|
||||
name = "Circular Shot"
|
||||
button_icon_state = "herald_directionalshot"
|
||||
chosen_message = "<span class='boldwarning'>You are firing projectiles in all directions.</span>"
|
||||
chosen_attack_num = HERALD_DIRECTIONALSHOT
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/herald_teleshot
|
||||
name = "Teleport Shot"
|
||||
button_icon_state = "herald_teleshot"
|
||||
chosen_message = "<span class='boldwarning'>You will now fire a shot which teleports you where it lands.</span>"
|
||||
chosen_attack_num = HERALD_TELESHOT
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/herald_mirror
|
||||
name = "Summon Mirror"
|
||||
button_icon_state = "herald_mirror"
|
||||
chosen_message = "<span class='boldwarning'>You will spawn a mirror which duplicates your attacks.</span>"
|
||||
chosen_attack_num = HERALD_MIRROR
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/OpenFire()
|
||||
if(client)
|
||||
switch(chosen_attack)
|
||||
@@ -124,7 +124,7 @@
|
||||
my_mirror.herald_teleshot(target)
|
||||
if(HERALD_MIRROR)
|
||||
herald_mirror()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/shoot_projectile(turf/marker, set_angle, var/is_teleshot)
|
||||
var/turf/startloc = get_turf(src)
|
||||
var/obj/item/projectile/herald/H = null
|
||||
@@ -137,7 +137,7 @@
|
||||
if(target)
|
||||
H.original = target
|
||||
H.fire(set_angle)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_trishot(target)
|
||||
ranged_cooldown = world.time + 30
|
||||
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
|
||||
@@ -151,17 +151,17 @@
|
||||
addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 10)
|
||||
addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 12)
|
||||
addtimer(CALLBACK(src, .proc/shoot_projectile, target_turf, angle_to_target, FALSE), 14)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_circleshot()
|
||||
var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315)
|
||||
for(var/i in directional_shot_angles)
|
||||
shoot_projectile(get_turf(src), i, FALSE)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/unenrage()
|
||||
if(stat == DEAD || is_mirror)
|
||||
return
|
||||
icon_state = "herald"
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_directionalshot()
|
||||
ranged_cooldown = world.time + 50
|
||||
if(!is_mirror)
|
||||
@@ -172,14 +172,14 @@
|
||||
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/herald_circleshot), 15)
|
||||
addtimer(CALLBACK(src, .proc/unenrage), 20)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_teleshot(target)
|
||||
ranged_cooldown = world.time + 30
|
||||
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
|
||||
var/target_turf = get_turf(target)
|
||||
var/angle_to_target = Get_Angle(src, target_turf)
|
||||
shoot_projectile(target_turf, angle_to_target, TRUE)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/proc/herald_mirror()
|
||||
ranged_cooldown = world.time + 40
|
||||
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
|
||||
@@ -190,7 +190,7 @@
|
||||
my_mirror = new_mirror
|
||||
my_mirror.my_master = src
|
||||
my_mirror.faction = faction.Copy()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/mirror
|
||||
name = "herald's mirror"
|
||||
desc = "This fiendish work of magic copies the herald's attacks. Seems logical to smash it."
|
||||
@@ -203,16 +203,16 @@
|
||||
del_on_death = TRUE
|
||||
is_mirror = TRUE
|
||||
var/mob/living/simple_animal/hostile/asteroid/elite/herald/my_master = null
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Initialize()
|
||||
..()
|
||||
toggle_ai(AI_OFF)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/herald/mirror/Destroy()
|
||||
if(my_master != null)
|
||||
my_master.my_mirror = null
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/projectile/herald
|
||||
name ="death bolt"
|
||||
icon_state= "chronobolt"
|
||||
@@ -222,7 +222,7 @@
|
||||
eyeblur = 0
|
||||
damage_type = BRUTE
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
|
||||
/obj/item/projectile/herald/teleshot
|
||||
name ="golden bolt"
|
||||
damage = 0
|
||||
@@ -239,11 +239,11 @@
|
||||
var/mob/living/F = firer
|
||||
if(F != null && istype(F, /mob/living/simple_animal/hostile/asteroid/elite) && F.faction_check_mob(L))
|
||||
L.heal_overall_damage(damage)
|
||||
|
||||
|
||||
/obj/item/projectile/herald/teleshot/on_hit(atom/target, blocked = FALSE)
|
||||
. = ..()
|
||||
firer.forceMove(get_turf(src))
|
||||
|
||||
|
||||
//Herald's loot: Cloak of the Prophet
|
||||
|
||||
/obj/item/clothing/neck/cloak/herald_cloak
|
||||
@@ -252,13 +252,13 @@
|
||||
icon = 'icons/obj/lavaland/elite_trophies.dmi'
|
||||
icon_state = "herald_cloak"
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
hit_reaction_chance = 10
|
||||
|
||||
var/hit_reaction_chance = 10
|
||||
|
||||
/obj/item/clothing/neck/cloak/herald_cloak/proc/reactionshot(mob/living/carbon/owner)
|
||||
var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315)
|
||||
for(var/i in directional_shot_angles)
|
||||
shoot_projectile(get_turf(owner), i, owner)
|
||||
|
||||
|
||||
/obj/item/clothing/neck/cloak/herald_cloak/proc/shoot_projectile(turf/marker, set_angle, mob/living/carbon/owner)
|
||||
var/turf/startloc = get_turf(owner)
|
||||
var/obj/item/projectile/herald/H = null
|
||||
@@ -266,12 +266,11 @@
|
||||
H.preparePixelProjectile(marker, startloc)
|
||||
H.firer = owner
|
||||
H.fire(set_angle)
|
||||
|
||||
/obj/item/clothing/neck/cloak/herald_cloak/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
|
||||
|
||||
/obj/item/clothing/neck/cloak/herald_cloak/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
. = ..()
|
||||
if(rand(1,100) > hit_reaction_chance)
|
||||
return
|
||||
owner.visible_message("<span class='danger'>[owner]'s [src] emits a loud noise as [owner] is struck!</span>")
|
||||
var/static/list/directional_shot_angles = list(0, 45, 90, 135, 180, 225, 270, 315)
|
||||
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/reactionshot, owner), 10)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* - Charges at the target after a telegraph, throwing them across the arena should it connect.
|
||||
* - Legionnaire's head detaches, attacking as it's own entity. Has abilities of it's own later into the fight. Once dead, regenerates after a brief period. If the skill is used while the head is off, it will be killed.
|
||||
* - Leaves a pile of bones at your location. Upon using this skill again, you'll swap locations with the bone pile.
|
||||
* - Spews a cloud of smoke from it's maw, wherever said maw is.
|
||||
* - Spews a cloud of smoke from it's maw, wherever said maw is.
|
||||
* A unique fight incorporating the head mechanic of legion into a whole new beast. Combatants will need to make sure the tag-team of head and body don't lure them into a deadly trap.
|
||||
*/
|
||||
|
||||
@@ -45,35 +45,35 @@
|
||||
/datum/action/innate/elite_attack/head_detach,
|
||||
/datum/action/innate/elite_attack/bonfire_teleport,
|
||||
/datum/action/innate/elite_attack/spew_smoke)
|
||||
|
||||
|
||||
var/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/myhead = null
|
||||
var/obj/structure/legionnaire_bonfire/mypile = null
|
||||
var/has_head = TRUE
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/legionnaire_charge
|
||||
name = "Legionnaire Charge"
|
||||
button_icon_state = "legionnaire_charge"
|
||||
chosen_message = "<span class='boldwarning'>You will attempt to grab your opponent and throw them.</span>"
|
||||
chosen_attack_num = LEGIONNAIRE_CHARGE
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/head_detach
|
||||
name = "Release Head"
|
||||
button_icon_state = "head_detach"
|
||||
chosen_message = "<span class='boldwarning'>You will now detach your head or kill it if it is already released.</span>"
|
||||
chosen_attack_num = HEAD_DETACH
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/bonfire_teleport
|
||||
name = "Bonfire Teleport"
|
||||
button_icon_state = "bonfire_teleport"
|
||||
chosen_message = "<span class='boldwarning'>You will leave a bonfire. Second use will let you swap positions with it indefintiely. Using this move on the same tile as your active bonfire removes it.</span>"
|
||||
chosen_attack_num = BONFIRE_TELEPORT
|
||||
|
||||
|
||||
/datum/action/innate/elite_attack/spew_smoke
|
||||
name = "Spew Smoke"
|
||||
button_icon_state = "spew_smoke"
|
||||
chosen_message = "<span class='boldwarning'>Your head will spew smoke in an area, wherever it may be.</span>"
|
||||
chosen_attack_num = SPEW_SMOKE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/OpenFire()
|
||||
if(client)
|
||||
switch(chosen_attack)
|
||||
@@ -96,7 +96,7 @@
|
||||
bonfire_teleport()
|
||||
if(SPEW_SMOKE)
|
||||
spew_smoke()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/legionnaire_charge(target)
|
||||
ranged_cooldown = world.time + 50
|
||||
var/dir_to_target = get_dir(get_turf(src), get_turf(target))
|
||||
@@ -107,7 +107,7 @@
|
||||
playsound(src,'sound/magic/demon_attack1.ogg', 200, 1)
|
||||
visible_message("<span class='boldwarning'>[src] prepares to charge!</span>")
|
||||
addtimer(CALLBACK(src, .proc/legionnaire_charge_2, dir_to_target, 0), 5)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/legionnaire_charge_2(var/move_dir, var/times_ran)
|
||||
if(times_ran >= 4)
|
||||
return
|
||||
@@ -136,7 +136,7 @@
|
||||
L.Stun(20) //substituting this for the Paralyze from the line above, because we don't have tg paralysis stuff
|
||||
L.adjustBruteLoss(50)
|
||||
addtimer(CALLBACK(src, .proc/legionnaire_charge_2, move_dir, (times_ran + 1)), 2)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/head_detach(target)
|
||||
ranged_cooldown = world.time + 10
|
||||
if(myhead != null)
|
||||
@@ -160,11 +160,11 @@
|
||||
else if(health < maxHealth * 0.5)
|
||||
myhead.melee_damage_lower = 20
|
||||
myhead.melee_damage_upper = 20
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/onHeadDeath()
|
||||
myhead = null
|
||||
addtimer(CALLBACK(src, .proc/regain_head), 50)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/regain_head()
|
||||
has_head = TRUE
|
||||
if(stat == DEAD)
|
||||
@@ -196,7 +196,7 @@
|
||||
forceMove(pileturf)
|
||||
visible_message("<span class='boldwarning'>[src] forms from the bonfire!</span>")
|
||||
mypile.forceMove(legionturf)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/proc/spew_smoke()
|
||||
ranged_cooldown = world.time + 60
|
||||
var/turf/T = null
|
||||
@@ -213,7 +213,7 @@
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.set_up(2, T)
|
||||
smoke.start()
|
||||
|
||||
|
||||
//The legionnaire's head. Basically the same as any legion head, but we have to tell our creator when we die so they can generate another head.
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead
|
||||
name = "legionnaire head"
|
||||
@@ -239,12 +239,12 @@
|
||||
faction = list()
|
||||
ranged = FALSE
|
||||
var/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/body = null
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/death()
|
||||
. = ..()
|
||||
if(body)
|
||||
body.onHeadDeath()
|
||||
|
||||
|
||||
//The legionnaire's bonfire, which can be swapped positions with. Also sets flammable living beings on fire when they walk over it.
|
||||
/obj/structure/legionnaire_bonfire
|
||||
name = "bone pile"
|
||||
@@ -258,20 +258,20 @@
|
||||
light_range = 4
|
||||
light_color = LIGHT_COLOR_RED
|
||||
var/mob/living/simple_animal/hostile/asteroid/elite/legionnaire/myowner = null
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/structure/legionnaire_bonfire/Entered(atom/movable/mover, turf/target)
|
||||
if(isliving(mover))
|
||||
var/mob/living/L = mover
|
||||
L.adjust_fire_stacks(3)
|
||||
L.IgniteMob()
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/structure/legionnaire_bonfire/Destroy()
|
||||
if(myowner != null)
|
||||
myowner.mypile = null
|
||||
. = ..()
|
||||
|
||||
|
||||
//The visual effect which appears in front of legionnaire when he goes to charge.
|
||||
/obj/effect/temp_visual/dragon_swoop/legionnaire
|
||||
duration = 10
|
||||
@@ -280,7 +280,7 @@
|
||||
/obj/effect/temp_visual/dragon_swoop/legionnaire/Initialize()
|
||||
. = ..()
|
||||
transform *= 0.33
|
||||
|
||||
|
||||
// Legionnaire's loot: Legionnaire Spine
|
||||
|
||||
/obj/item/crusher_trophy/legionnaire_spine
|
||||
|
||||
@@ -285,9 +285,10 @@
|
||||
if("Miner")
|
||||
mob_species = pickweight(list(/datum/species/human = 70, /datum/species/lizard = 26, /datum/species/fly = 2, /datum/species/plasmaman = 2))
|
||||
if(mob_species == /datum/species/plasmaman)
|
||||
uniform = /obj/item/clothing/under/plasmaman
|
||||
head = /obj/item/clothing/head/helmet/space/plasmaman
|
||||
uniform = /obj/item/clothing/under/plasmaman/mining
|
||||
head = /obj/item/clothing/head/helmet/space/plasmaman/mining
|
||||
belt = /obj/item/tank/internals/plasmaman/belt
|
||||
mask = /obj/item/clothing/mask/gas/explorer
|
||||
else
|
||||
uniform = /obj/item/clothing/under/rank/cargo/miner/lavaland
|
||||
if (prob(4))
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
desc = "You will trigger a large amount of lights around you to flicker."
|
||||
|
||||
charge_max = 300
|
||||
clothes_req = 0
|
||||
clothes_req = NONE
|
||||
range = 14
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets,mob/user = usr)
|
||||
@@ -185,7 +185,7 @@
|
||||
|
||||
message = "<span class='notice'>You glare your eyes.</span>"
|
||||
charge_max = 600
|
||||
clothes_req = 0
|
||||
clothes_req = NONE
|
||||
range = 10
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets,mob/user = usr)
|
||||
@@ -201,7 +201,7 @@
|
||||
desc = "Toggle your nightvision mode."
|
||||
|
||||
charge_max = 10
|
||||
clothes_req = 0
|
||||
clothes_req = NONE
|
||||
|
||||
message = "<span class='notice'>You toggle your night vision!</span>"
|
||||
range = -1
|
||||
|
||||
@@ -43,23 +43,23 @@
|
||||
/mob/living/simple_animal/hostile/wizard/Initialize()
|
||||
. = ..()
|
||||
fireball = new /obj/effect/proc_holder/spell/aimed/fireball
|
||||
fireball.clothes_req = 0
|
||||
fireball.human_req = 0
|
||||
fireball.player_lock = 0
|
||||
fireball.clothes_req = NONE
|
||||
fireball.mobs_whitelist = null
|
||||
fireball.player_lock = FALSE
|
||||
AddSpell(fireball)
|
||||
var/obj/item/implant/exile/I = new
|
||||
I.implant(src, null, TRUE)
|
||||
|
||||
mm = new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile
|
||||
mm.clothes_req = 0
|
||||
mm.human_req = 0
|
||||
mm.player_lock = 0
|
||||
mm.clothes_req = NONE
|
||||
mm.mobs_whitelist = null
|
||||
mm.player_lock = FALSE
|
||||
AddSpell(mm)
|
||||
|
||||
blink = new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink
|
||||
blink.clothes_req = 0
|
||||
blink.human_req = 0
|
||||
blink.player_lock = 0
|
||||
blink.clothes_req = NONE
|
||||
blink.mobs_whitelist = null
|
||||
blink.player_lock = FALSE
|
||||
blink.outer_tele_radius = 3
|
||||
AddSpell(blink)
|
||||
|
||||
|
||||
+64
-13
@@ -632,7 +632,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
|
||||
|
||||
/mob/proc/add_spells_to_statpanel(list/spells)
|
||||
for(var/obj/effect/proc_holder/spell/S in spells)
|
||||
if(S.can_be_cast_by(src))
|
||||
if((!S.mobs_blacklist || !S.mobs_blacklist[src]) && (!S.mobs_whitelist || S.mobs_whitelist[src]))
|
||||
switch(S.charge_type)
|
||||
if("recharge")
|
||||
statpanel("[S.panel]","[S.charge_counter/10.0]/[S.charge_max/10]",S)
|
||||
@@ -946,18 +946,65 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
|
||||
|
||||
/mob/vv_get_dropdown()
|
||||
. = ..()
|
||||
. += "---"
|
||||
.["Gib"] = "?_src_=vars;[HrefToken()];gib=[REF(src)]"
|
||||
.["Give Spell"] = "?_src_=vars;[HrefToken()];give_spell=[REF(src)]"
|
||||
.["Remove Spell"] = "?_src_=vars;[HrefToken()];remove_spell=[REF(src)]"
|
||||
.["Give Disease"] = "?_src_=vars;[HrefToken()];give_disease=[REF(src)]"
|
||||
.["Toggle Godmode"] = "?_src_=vars;[HrefToken()];godmode=[REF(src)]"
|
||||
.["Drop Everything"] = "?_src_=vars;[HrefToken()];drop_everything=[REF(src)]"
|
||||
.["Regenerate Icons"] = "?_src_=vars;[HrefToken()];regenerateicons=[REF(src)]"
|
||||
.["Show player panel"] = "?_src_=vars;[HrefToken()];mob_player_panel=[REF(src)]"
|
||||
.["Toggle Build Mode"] = "?_src_=vars;[HrefToken()];build_mode=[REF(src)]"
|
||||
.["Assume Direct Control"] = "?_src_=vars;[HrefToken()];direct_control=[REF(src)]"
|
||||
.["Offer Control to Ghosts"] = "?_src_=vars;[HrefToken()];offer_control=[REF(src)]"
|
||||
VV_DROPDOWN_OPTION("", "---------")
|
||||
VV_DROPDOWN_OPTION(VV_HK_GIB, "Gib")
|
||||
VV_DROPDOWN_OPTION(VV_HK_GIVE_SPELL, "Give Spell")
|
||||
VV_DROPDOWN_OPTION(VV_HK_REMOVE_SPELL, "Remove Spell")
|
||||
VV_DROPDOWN_OPTION(VV_HK_GIVE_DISEASE, "Give Disease")
|
||||
VV_DROPDOWN_OPTION(VV_HK_GODMODE, "Toggle Godmode")
|
||||
VV_DROPDOWN_OPTION(VV_HK_DROP_ALL, "Drop Everything")
|
||||
VV_DROPDOWN_OPTION(VV_HK_REGEN_ICONS, "Regenerate Icons")
|
||||
VV_DROPDOWN_OPTION(VV_HK_PLAYER_PANEL, "Show player panel")
|
||||
VV_DROPDOWN_OPTION(VV_HK_BUILDMODE, "Toggle Buildmode")
|
||||
VV_DROPDOWN_OPTION(VV_HK_DIRECT_CONTROL, "Assume Direct Control")
|
||||
VV_DROPDOWN_OPTION(VV_HK_OFFER_GHOSTS, "Offer Control to Ghosts")
|
||||
|
||||
/mob/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
if(href_list[VV_HK_REGEN_ICONS])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
regenerate_icons()
|
||||
if(href_list[VV_HK_PLAYER_PANEL])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.holder.show_player_panel(src)
|
||||
if(href_list[VV_HK_GODMODE])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
usr.client.cmd_admin_godmode(src)
|
||||
if(href_list[VV_HK_GIVE_SPELL])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.give_spell(src)
|
||||
if(href_list[VV_HK_REMOVE_SPELL])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.remove_spell(src)
|
||||
if(href_list[VV_HK_GIVE_DISEASE])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.give_disease(src)
|
||||
if(href_list[VV_HK_GIB])
|
||||
if(!check_rights(R_FUN))
|
||||
return
|
||||
usr.client.cmd_admin_gib(src)
|
||||
if(href_list[VV_HK_BUILDMODE])
|
||||
if(!check_rights(R_BUILDMODE))
|
||||
return
|
||||
togglebuildmode(src)
|
||||
if(href_list[VV_HK_DROP_ALL])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.cmd_admin_drop_everything(src)
|
||||
if(href_list[VV_HK_DIRECT_CONTROL])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
usr.client.cmd_assume_direct_control(src)
|
||||
if(href_list[VV_HK_OFFER_GHOSTS])
|
||||
if(!check_rights(NONE))
|
||||
return
|
||||
offer_control(src)
|
||||
|
||||
/mob/vv_get_var(var_name)
|
||||
switch(var_name)
|
||||
@@ -965,6 +1012,10 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
|
||||
return debug_variable(var_name, logging, 0, src, FALSE)
|
||||
. = ..()
|
||||
|
||||
/mob/vv_auto_rename(new_name)
|
||||
//Do not do parent's actions, as we *usually* do this differently.
|
||||
fully_replace_character_name(real_name, new_name)
|
||||
|
||||
/mob/verb/open_language_menu()
|
||||
set name = "Open Language Menu"
|
||||
set category = "IC"
|
||||
|
||||
Reference in New Issue
Block a user