mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-22 19:47:22 +01:00
Reworks the morph. Makes it a more stealth ambush oriented antag (#15208)
* Reworks the morph * Hunger points. Reproduce. Sprites. Other tweaks * vent open. Convert to spells. Valid checks. Minor tweaks * undo life hack * Icons, Fixes, No more morphed lights * Item eat fix. All eating takes 3 seconds * Add sounds * Fixes and tweaks. Pass airlock ability. Ambush screen alert * Steel review fixes * New dead morph sprite from Pewtershmitz * Pass airlock sprite provided by Pewtershmitz * No barsigns or cryo cells as mimic * Gluttany ruin change * Color fix. No forms fix * fix the stupid icons * Apply suggestions from code review Co-authored-by: dearmochi <shenesis@gmail.com> Co-authored-by: joep van der velden <15887760+farie82@users.noreply.github.com> Co-authored-by: dearmochi <shenesis@gmail.com>
This commit is contained in:
co-authored by
dearmochi
joep van der velden
parent
34b40c7860
commit
c4cda0930f
@@ -225,7 +225,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
charge_counter = charge_max
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr, make_attack_logs = TRUE) //if recharge is started is important for the trigger spells
|
||||
before_cast(targets)
|
||||
before_cast(targets, user)
|
||||
invocation()
|
||||
if(user && user.ckey && make_attack_logs)
|
||||
add_attack_logs(user, targets, "cast the spell [name]", ATKLOG_ALL)
|
||||
@@ -244,7 +244,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
if(action)
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/before_cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/proc/before_cast(list/targets, mob/user)
|
||||
if(vampire_ability)
|
||||
if(!before_cast_vampire(targets))
|
||||
return
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
var/sound2 = 'sound/weapons/zapbang.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr)
|
||||
var/thearea = before_cast(targets)
|
||||
var/thearea = before_cast(targets, user)
|
||||
if(!thearea || !cast_check(FALSE, FALSE, user))
|
||||
revert_cast()
|
||||
return
|
||||
@@ -21,7 +21,7 @@
|
||||
cast(targets,thearea)
|
||||
after_cast(targets)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets)
|
||||
/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets, mob/user)
|
||||
var/A = null
|
||||
|
||||
if(!randomise_selection)
|
||||
@@ -35,7 +35,7 @@
|
||||
var/area/thearea = SSmapping.teleportlocs[A]
|
||||
|
||||
if(thearea.tele_proof && !istype(thearea, /area/wizard_station))
|
||||
to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.")
|
||||
to_chat(user, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.")
|
||||
return
|
||||
|
||||
return thearea
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic
|
||||
name = "Mimic"
|
||||
desc = "Learn a new form to mimic or become one of your known forms"
|
||||
clothes_req = FALSE
|
||||
charge_max = 3 SECONDS
|
||||
include_user = TRUE // To change forms
|
||||
action_icon_state = "genetic_morph"
|
||||
allowed_type = /atom/movable
|
||||
auto_target_single = FALSE
|
||||
click_radius = -1
|
||||
selection_activated_message = "<span class='sinister'>Click on a target to remember it's form. Click on yourself to change form.</span>"
|
||||
create_logs = FALSE
|
||||
action_icon_state = "morph_mimic"
|
||||
/// Which form is currently selected
|
||||
var/datum/mimic_form/selected_form
|
||||
/// Which forms the user can become
|
||||
var/list/available_forms = list()
|
||||
/// How many forms the user can remember
|
||||
var/max_forms = 5
|
||||
/// Which index will be overriden next when the user wants to remember another form
|
||||
var/next_override_index = 1
|
||||
/// If a message is shown when somebody examines the user from close range
|
||||
var/perfect_disguise = FALSE
|
||||
|
||||
var/static/list/black_listed_form_types = list(/obj/screen, /obj/singularity, /obj/effect, /mob/living/simple_animal/hostile/megafauna, /atom/movable/lighting_object, /obj/machinery/dna_vault,
|
||||
/obj/machinery/power/bluespace_tap, /obj/structure/sign/barsign, /obj/machinery/atmospherics/unary/cryo_cell)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/valid_target(atom/target, user)
|
||||
if(is_type_in_list(target, black_listed_form_types))
|
||||
return FALSE
|
||||
if(istype(target, /atom/movable))
|
||||
var/atom/movable/AM = target
|
||||
if(AM.bound_height > world.icon_size || AM.bound_width > world.icon_size)
|
||||
return FALSE // No multitile structures
|
||||
if(user != target && istype(target, /mob/living/simple_animal/hostile/morph))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/cast(list/targets, mob/user)
|
||||
var/atom/movable/A = targets[1]
|
||||
if(A == user)
|
||||
INVOKE_ASYNC(src, .proc/pick_form, user)
|
||||
return
|
||||
|
||||
INVOKE_ASYNC(src, .proc/remember_form, A, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/remember_form(atom/movable/A, mob/user)
|
||||
if(A.name in available_forms)
|
||||
to_chat(user, "<span class='warning'>[A] is already an available form.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
if(length(available_forms) >= max_forms)
|
||||
to_chat(user, "<span class='warning'>You start to forget the form of [available_forms[next_override_index]] to learn a new one.</span>")
|
||||
|
||||
to_chat(user, "<span class='sinister'>You start remembering the form of [A].</span>")
|
||||
if(!do_after(user, 2 SECONDS, FALSE, user))
|
||||
to_chat(user, "<span class='warning'>You lose focus.</span>")
|
||||
return
|
||||
|
||||
// Forget the old form if needed
|
||||
if(length(available_forms) >= max_forms)
|
||||
qdel(available_forms[available_forms[next_override_index]]) // Delete the value using the key
|
||||
available_forms[next_override_index++] = A.name
|
||||
// Reset if needed
|
||||
if(next_override_index > max_forms)
|
||||
next_override_index = 1
|
||||
|
||||
available_forms[A.name] = new /datum/mimic_form(A, user)
|
||||
to_chat(user, "<span class='sinister'>You learn the form of [A].</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/pick_form(mob/user)
|
||||
if(!length(available_forms) && !selected_form)
|
||||
to_chat(user, "<span class='warning'>No available forms. Learn more forms by using this spell on other objects first.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
|
||||
var/list/forms = list()
|
||||
if(selected_form)
|
||||
forms += "Original Form"
|
||||
|
||||
forms += available_forms.Copy()
|
||||
var/what = input(user, "Which form do you want to become?", "Mimic") as null|anything in forms
|
||||
if(!what)
|
||||
to_chat(user, "<span class='notice'>You decide against changing forms.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
|
||||
if(what == "Original Form")
|
||||
restore_form(user)
|
||||
return
|
||||
to_chat(user, "<span class='sinister'>You start becoming [what].</span>")
|
||||
if(!do_after(user, 2 SECONDS, FALSE, user))
|
||||
to_chat(user, "<span class='warning'>You lose focus.</span>")
|
||||
return
|
||||
take_form(available_forms[what], user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/take_form(datum/mimic_form/form, mob/user)
|
||||
var/old_name = "[user]"
|
||||
if(ishuman(user))
|
||||
// Not fully finished yet
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.name_override = form.name
|
||||
else
|
||||
user.appearance = form.appearance
|
||||
user.transform = initial(user.transform)
|
||||
user.pixel_y = initial(user.pixel_y)
|
||||
user.pixel_x = initial(user.pixel_x)
|
||||
user.layer = MOB_LAYER // Avoids weirdness when mimicing something below the vent layer
|
||||
|
||||
playsound(user, "bonebreak", 75, TRUE)
|
||||
show_change_form_message(user, old_name, "[user]")
|
||||
user.create_log(MISC_LOG, "Mimicked into [user]")
|
||||
|
||||
if(!selected_form)
|
||||
RegisterSignal(user, COMSIG_PARENT_EXAMINE, .proc/examine_override)
|
||||
RegisterSignal(user, COMSIG_MOB_DEATH, .proc/on_death)
|
||||
|
||||
selected_form = form
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/show_change_form_message(mob/user, old_name, new_name)
|
||||
user.visible_message("<span class='warning'>[old_name] contorts and slowly becomes [new_name]!</span>", "<span class='sinister'>You take form of [new_name].</span>", "You hear loud cracking noises!")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/restore_form(mob/user, show_message = TRUE)
|
||||
selected_form = null
|
||||
var/old_name = "[user]"
|
||||
|
||||
user.cut_overlays()
|
||||
user.icon = initial(user.icon)
|
||||
user.icon_state = initial(user.icon_state)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.name_override = null
|
||||
H.regenerate_icons()
|
||||
else
|
||||
user.name = initial(user.name)
|
||||
user.desc = initial(user.desc)
|
||||
user.color = initial(user.color)
|
||||
|
||||
playsound(user, "bonebreak", 150, TRUE)
|
||||
if(show_message)
|
||||
show_restore_form_message(user, old_name, "[user]")
|
||||
|
||||
UnregisterSignal(user, list(COMSIG_PARENT_EXAMINE, COMSIG_MOB_DEATH))
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/show_restore_form_message(mob/user, old_name, new_name)
|
||||
user.visible_message("<span class='warning'>[old_name] shakes and contorts and quickly becomes [new_name]!</span>", "<span class='sinister'>You take return to your normal self.</span>", "You hear loud cracking noises!")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/examine_override(datum/source, mob/user, list/examine_list)
|
||||
examine_list.Cut()
|
||||
examine_list += selected_form.examine_text
|
||||
if(!perfect_disguise && get_dist(user, source) <= 3)
|
||||
examine_list += "<span class='warning'>It doesn't look quite right...</span>"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/on_death(mob/user, gibbed)
|
||||
if(!gibbed)
|
||||
restore_form(user, FALSE)
|
||||
show_death_message(user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/proc/show_death_message(mob/user)
|
||||
user.visible_message("<span class='warning'>[user] shakes and contorts as [user.p_they()] die[user.p_s()], returning to [user.p_their()] true form!</span>", "<span class='deadsay'>Your disguise fails as your life forces drain away.</span>", "You hear loud cracking noises followed by a thud!")
|
||||
|
||||
|
||||
/datum/mimic_form
|
||||
/// How does the form look like?
|
||||
var/appearance
|
||||
/// What is the examine text paired with this form
|
||||
var/examine_text
|
||||
/// What the name of the form is
|
||||
var/name
|
||||
|
||||
/datum/mimic_form/New(atom/movable/form, mob/user)
|
||||
appearance = form.appearance
|
||||
examine_text = form.examine(user)
|
||||
name = form.name
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph
|
||||
action_background_icon_state = "bg_morph"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/valid_target(atom/target, user)
|
||||
if(target != user && istype(target, /mob/living/simple_animal/hostile/morph))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/take_form(datum/mimic_form/form, mob/living/simple_animal/hostile/morph/user)
|
||||
..()
|
||||
user.assume()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/restore_form(mob/living/simple_animal/hostile/morph/user, show_message = TRUE)
|
||||
..()
|
||||
user.restore()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/show_change_form_message(mob/user, old_name, new_name)
|
||||
user.visible_message("<span class='warning'>[old_name] suddenly twists and changes shape, becoming a copy of [new_name]!</span>", \
|
||||
"<span class='notice'>You twist your body and assume the form of [new_name].</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/show_restore_form_message(mob/user, old_name, new_name)
|
||||
user.visible_message("<span class='warning'>[old_name] suddenly collapses in on itself, dissolving into a pile of green flesh!</span>", \
|
||||
"<span class='notice'>You reform to your normal body.</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mimic/morph/show_death_message(mob/user)
|
||||
user.visible_message("<span class='warning'>[user] twists and dissolves into a pile of green flesh!</span>", \
|
||||
"<span class='userdanger'>Your skin ruptures! Your flesh breaks apart! No disguise can ward off de--</span>")
|
||||
@@ -1,4 +1,5 @@
|
||||
#define MORPH_COOLDOWN 50
|
||||
#define MORPHED_SPEED 2.5
|
||||
#define ITEM_EAT_COST 5
|
||||
|
||||
/mob/living/simple_animal/hostile/morph
|
||||
name = "morph"
|
||||
@@ -10,11 +11,12 @@
|
||||
icon_state = "morph"
|
||||
icon_living = "morph"
|
||||
icon_dead = "morph_dead"
|
||||
speed = 2
|
||||
speed = 1.5
|
||||
a_intent = INTENT_HARM
|
||||
stop_automated_movement = 1
|
||||
status_flags = CANPUSH
|
||||
pass_flags = PASSTABLE
|
||||
move_resist = MOVE_FORCE_STRONG // Fat being
|
||||
ventcrawler = 2
|
||||
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
@@ -24,8 +26,8 @@
|
||||
health = 150
|
||||
environment_smash = 1
|
||||
obj_damage = 50
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
vision_range = 1 // Only attack when target is close
|
||||
@@ -34,18 +36,40 @@
|
||||
attack_sound = 'sound/effects/blobattack.ogg'
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 2)
|
||||
|
||||
var/morphed = 0
|
||||
var/atom/movable/form = null
|
||||
var/morph_time = 0
|
||||
/// If the morph is disguised or not
|
||||
var/morphed = FALSE
|
||||
/// If the morph is ready to perform an ambush
|
||||
var/ambush_prepared = FALSE
|
||||
/// How much damage a successful ambush attack does
|
||||
var/ambush_damage = 25
|
||||
/// How much weaken a successful ambush attack applies
|
||||
var/ambush_weaken = 3
|
||||
/// The spell the morph uses to morph
|
||||
var/obj/effect/proc_holder/spell/targeted/click/mimic/morph/mimic_spell
|
||||
/// The ambush action used by the morph
|
||||
var/obj/effect/proc_holder/spell/morph/ambush/ambush_spell
|
||||
/// The spell the morph uses to pass through airlocks
|
||||
var/obj/effect/proc_holder/spell/targeted/click/pass_airlock/pass_airlock_spell
|
||||
|
||||
var/list/examine_text_list
|
||||
/// How much the morph has gathered in terms of food. Used to reproduce and such
|
||||
var/gathered_food = 20 // Start with a bit to use abilities
|
||||
|
||||
var/playstyle_string = "<b><font size=3 color='red'>You are a morph.</font><br> As an abomination created primarily with changeling cells, \
|
||||
you may take the form of anything nearby by shift-clicking it. This process will alert any nearby \
|
||||
observers, and can only be performed once every five seconds.<br> While morphed, you move faster, but do \
|
||||
less damage. In addition, anyone within three tiles will note an uncanny wrongness if examining you. \
|
||||
You can restore yourself to your original form while morphed by shift-clicking yourself.<br> \
|
||||
Finally, you can attack any item or dead creature to consume it - creatures will restore 1/3 of your max health.</b>"
|
||||
/mob/living/simple_animal/hostile/morph/Initialize(mapload)
|
||||
. = ..()
|
||||
mimic_spell = new
|
||||
AddSpell(mimic_spell)
|
||||
ambush_spell = new
|
||||
AddSpell(ambush_spell)
|
||||
AddSpell(new /obj/effect/proc_holder/spell/morph/reproduce)
|
||||
AddSpell(new /obj/effect/proc_holder/spell/morph/open_vent)
|
||||
pass_airlock_spell = new
|
||||
AddSpell(pass_airlock_spell)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/Stat(Name, Value)
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
stat(null, "Food Stored: [gathered_food]")
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/wizard
|
||||
name = "magical morph"
|
||||
@@ -57,78 +81,117 @@
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/smoke)
|
||||
AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/examine(mob/user)
|
||||
if(morphed)
|
||||
. = examine_text_list.Copy()
|
||||
if(get_dist(user, src) <= 3)
|
||||
. += "<span class='warning'>It doesn't look quite right...</span>"
|
||||
else
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/allowed(atom/movable/A) // make it into property/proc ? not sure if worth it
|
||||
if(istype(A,/obj/screen))
|
||||
return 0
|
||||
if(istype(A,/obj/singularity))
|
||||
return 0
|
||||
if(istype(A,/mob/living/simple_animal/hostile/morph))
|
||||
return 0
|
||||
return 1
|
||||
/mob/living/simple_animal/hostile/morph/proc/try_eat(atom/movable/A)
|
||||
var/food_value = calc_food_gained(A)
|
||||
if(food_value + gathered_food < 0)
|
||||
to_chat(src, "<span class='warning'>You can't force yourself to eat more disgusting items. Eat some living things first.</span>")
|
||||
return
|
||||
var/eat_self_message
|
||||
if(food_value < 0)
|
||||
eat_self_message = "<span class='warning'>You start eating [A]... disgusting....</span>"
|
||||
else
|
||||
eat_self_message = "<span class='notice'>You start eating [A].</span>"
|
||||
visible_message("<span class='warning'>[src] starts eating [target]!</span>", eat_self_message, "You hear loud crunching!")
|
||||
if(do_after(src, 3 SECONDS, target = A))
|
||||
if(food_value + gathered_food < 0)
|
||||
to_chat(src, "<span class='warning'>You can't force yourself to eat more disgusting items. Eat some living things first.</span>")
|
||||
return
|
||||
eat(A)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/eat(atom/movable/A)
|
||||
if(A && A.loc != src)
|
||||
visible_message("<span class='warning'>[src] swallows [A] whole!</span>")
|
||||
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(istype(H) && H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under))
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
U.turn_sensors_off()
|
||||
|
||||
A.extinguish_light()
|
||||
A.forceMove(src)
|
||||
return 1
|
||||
return 0
|
||||
var/food_value = calc_food_gained(A)
|
||||
add_food(food_value)
|
||||
if(food_value > 0)
|
||||
adjustHealth(-food_value)
|
||||
add_attack_logs(src, A, "morph ate")
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/ShiftClickOn(atom/movable/A)
|
||||
if(morph_time <= world.time && !stat)
|
||||
if(A == src)
|
||||
restore()
|
||||
return
|
||||
if(istype(A) && allowed(A))
|
||||
assume(A)
|
||||
else
|
||||
to_chat(src, "<span class='warning'>Your chameleon skin is still repairing itself!</span>")
|
||||
..()
|
||||
/mob/living/simple_animal/hostile/morph/proc/calc_food_gained(mob/living/L)
|
||||
if(!istype(L))
|
||||
return -ITEM_EAT_COST // Anything other than a tasty mob will make me sad ;(
|
||||
var/gained_food = max(5, 10 * L.mob_size) // Tiny things are worth less
|
||||
if(ishuman(L) && !ismonkeybasic(L))
|
||||
gained_food += 10 // Humans are extra tasty
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/assume(atom/movable/target)
|
||||
morphed = 1
|
||||
form = target
|
||||
visible_message("<span class='warning'>[src] suddenly twists and changes shape, becoming a copy of [target]!</span>", \
|
||||
"<span class='notice'>You twist your body and assume the form of [target].</span>")
|
||||
return gained_food
|
||||
|
||||
appearance = target.appearance
|
||||
transform = initial(transform)
|
||||
pixel_y = initial(pixel_y)
|
||||
pixel_x = initial(pixel_x)
|
||||
//Morphed is weaker
|
||||
/mob/living/simple_animal/hostile/morph/proc/use_food(amount)
|
||||
if(amount > gathered_food)
|
||||
return FALSE
|
||||
add_food(-amount)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Adds the given amount of food to the gathered food and updates the actions.
|
||||
* Does not include a check to see if it goes below 0 or not
|
||||
*/
|
||||
/mob/living/simple_animal/hostile/morph/proc/add_food(amount)
|
||||
gathered_food += amount
|
||||
for(var/obj/effect/proc_holder/spell/morph/MS in mind.spell_list)
|
||||
MS.updateButtonIcon()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/assume()
|
||||
morphed = TRUE
|
||||
|
||||
//Morph is weaker initially when disguised
|
||||
melee_damage_lower = 5
|
||||
melee_damage_upper = 5
|
||||
speed = 0
|
||||
examine_text_list = form.examine(src)
|
||||
morph_time = world.time + MORPH_COOLDOWN
|
||||
return
|
||||
speed = MORPHED_SPEED
|
||||
ambush_spell.updateButtonIcon()
|
||||
pass_airlock_spell.updateButtonIcon()
|
||||
move_resist = MOVE_FORCE_DEFAULT // They become more fragile and easier to move
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/restore()
|
||||
if(!morphed)
|
||||
return
|
||||
morphed = 0
|
||||
form = null
|
||||
examine_text_list = null // Free that memory
|
||||
visible_message("<span class='warning'>[src] suddenly collapses in on itself, dissolving into a pile of green flesh!</span>", \
|
||||
"<span class='notice'>You reform to your normal body.</span>")
|
||||
name = initial(name)
|
||||
icon = initial(icon)
|
||||
icon_state = initial(icon_state)
|
||||
overlays.Cut()
|
||||
morphed = FALSE
|
||||
|
||||
//Baseline stats
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
speed = initial(speed)
|
||||
if(ambush_prepared)
|
||||
to_chat(src, "<span class='warning'>The ambush potential has faded as you take your true form.</span>")
|
||||
failed_ambush()
|
||||
pass_airlock_spell.updateButtonIcon()
|
||||
move_resist = MOVE_FORCE_STRONG // Return to their fatness
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/prepare_ambush()
|
||||
ambush_prepared = TRUE
|
||||
to_chat(src, "<span class='sinister'>You are ready to ambush any unsuspected target. Your next attack will hurt a lot more and weaken the target! Moving will break your focus. Standing still will perfect your disguise.</span>")
|
||||
apply_status_effect(/datum/status_effect/morph_ambush)
|
||||
RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/on_move)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/failed_ambush()
|
||||
ambush_prepared = FALSE
|
||||
ambush_spell.updateButtonIcon()
|
||||
mimic_spell.perfect_disguise = FALSE // Reset the perfect disguise
|
||||
remove_status_effect(/datum/status_effect/morph_ambush)
|
||||
UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/perfect_ambush()
|
||||
mimic_spell.perfect_disguise = TRUE // Reset the perfect disguise
|
||||
to_chat(src, "<span class='sinister'>You've perfected your disguise. Making you indistinguishable from the real form!</span>")
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/on_move()
|
||||
failed_ambush()
|
||||
to_chat(src, "<span class='warning'>You moved out of your ambush spot!</span>")
|
||||
|
||||
morph_time = world.time + MORPH_COOLDOWN
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/death(gibbed)
|
||||
. = ..()
|
||||
@@ -140,40 +203,122 @@
|
||||
// Only execute the below if we successfully died
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(morphed)
|
||||
visible_message("<span class='warning'>[src] twists and dissolves into a pile of green flesh!</span>", \
|
||||
"<span class='userdanger'>Your skin ruptures! Your flesh breaks apart! No disguise can ward off de--</span>")
|
||||
restore()
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/Aggro() // automated only
|
||||
..()
|
||||
restore()
|
||||
/mob/living/simple_animal/hostile/morph/attack_hand(mob/living/carbon/human/M)
|
||||
if(ambush_prepared)
|
||||
to_chat(M, "<span class='warning'>[src] feels a bit different from normal... it feels more.. </span><span class='userdanger'>SLIMEY?!</span>")
|
||||
ambush_attack(M, TRUE)
|
||||
else
|
||||
return ..()
|
||||
|
||||
#define MORPH_ATTACKED if((. = ..()) && morphed) mimic_spell.restore_form(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attackby(obj/item/O, mob/living/user)
|
||||
if(user.a_intent == INTENT_HELP && ambush_prepared)
|
||||
to_chat(user, "<span class='warning'>You try to use [O] on [src]... it seems different than no-</span>")
|
||||
ambush_attack(user, TRUE)
|
||||
return TRUE
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_animal(mob/living/simple_animal/M)
|
||||
if(M.a_intent == INTENT_HELP && ambush_prepared)
|
||||
to_chat(M, "<span class='notice'>You nuzzle [src].</span><span class='danger'> And [src] nuzzles back!</span>")
|
||||
ambush_attack(M, TRUE)
|
||||
return TRUE
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_hulk(mob/living/carbon/human/user, does_attack_animation) // Me SMASH
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_tk(mob/user)
|
||||
MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/attack_slime(mob/living/simple_animal/slime/M)
|
||||
MORPH_ATTACKED
|
||||
|
||||
#undef MORPH_ATTACKED
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/ambush_attack(mob/living/L, touched)
|
||||
ambush_prepared = FALSE
|
||||
var/total_weaken = ambush_weaken
|
||||
var/total_damage = ambush_damage
|
||||
if(touched) // Touching a morph while he's ready to kill you is a bad idea
|
||||
total_weaken *= 2
|
||||
total_damage *= 2
|
||||
|
||||
L.Weaken(total_weaken)
|
||||
L.apply_damage(total_damage, BRUTE)
|
||||
add_attack_logs(src, L, "morph ambush attacked")
|
||||
do_attack_animation(L, ATTACK_EFFECT_BITE)
|
||||
visible_message("<span class='danger'>[src] suddenly leaps towards [L]!</span>", "<span class='warning'>You strike [L] when [L.p_they()] least expected it!</span>", "You hear a horrible crunch!")
|
||||
|
||||
mimic_spell.restore_form(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/LoseAggro()
|
||||
vision_range = initial(vision_range)
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/AIShouldSleep(list/possible_targets)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(. && !morphed)
|
||||
var/list/things = list()
|
||||
for(var/atom/movable/A in view(src))
|
||||
if(allowed(A))
|
||||
if(mimic_spell.valid_target(A, src))
|
||||
things += A
|
||||
var/atom/movable/T = pick(things)
|
||||
assume(T)
|
||||
mimic_spell.take_form(new /datum/mimic_form(T, src), src)
|
||||
prepare_ambush() // They cheat okay
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/AttackingTarget()
|
||||
if(isliving(target)) // Eat Corpses to regen health
|
||||
var/mob/living/L = target
|
||||
if(L.stat == DEAD)
|
||||
if(do_after(src, 30, target = L))
|
||||
if(eat(L))
|
||||
adjustHealth(-50)
|
||||
return
|
||||
try_eat(L)
|
||||
return TRUE
|
||||
if(ambush_prepared)
|
||||
ambush_attack(L)
|
||||
return TRUE // No double attack
|
||||
else if(istype(target,/obj/item)) // Eat items just to be annoying
|
||||
var/obj/item/I = target
|
||||
if(!I.anchored)
|
||||
if(do_after(src, 20, target = I))
|
||||
eat(I)
|
||||
return
|
||||
return ..()
|
||||
try_eat(I)
|
||||
return TRUE
|
||||
. = ..()
|
||||
if(. && morphed)
|
||||
mimic_spell.restore_form(src)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/morph/proc/make_morph_antag(give_default_objectives = TRUE)
|
||||
mind.assigned_role = SPECIAL_ROLE_MORPH
|
||||
mind.special_role = SPECIAL_ROLE_MORPH
|
||||
SSticker.mode.traitors |= mind
|
||||
to_chat(src, "<b><font size=3 color='red'>You are a morph.</font><br></b>")
|
||||
to_chat(src, "<span class='sinister'>You hunger for living beings and desire to procreate. Achieve this goal by ambushing unsuspecting pray using your abilities.</span>")
|
||||
to_chat(src, "<span class='specialnotice'>As an abomination created primarily with changeling cells you may take the form of anything nearby by using your <span class='specialnoticebold'>Mimic ability</span>.</span>")
|
||||
to_chat(src, "<span class='specialnotice'>The transformation will not go unnoticed for bystanding observers.</span>")
|
||||
to_chat(src, "<span class='specialnoticebold'>While morphed</span><span class='specialnotice'>, you move slower and do less damage. In addition, anyone within three tiles will note an uncanny wrongness if examining you.</span>")
|
||||
to_chat(src, "<span class='specialnotice'>From this form you can however <span class='specialnoticebold'>Prepare an Ambush</span> using your ability.</span>")
|
||||
to_chat(src, "<span class='specialnotice'>This will allow you to deal a lot of damage the first hit. And if they touch you then even more.</span>")
|
||||
to_chat(src, "<span class='specialnotice'>Finally, you can attack any item or dead creature to consume it - creatures will restore 1/3 of your max health and will add to your stored food while eating items will reduce your stored food</span>.")
|
||||
to_chat(src, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Morph)</span>")
|
||||
SEND_SOUND(src, sound('sound/magic/mutate.ogg'))
|
||||
if(give_default_objectives)
|
||||
var/datum/objective/eat = new /datum/objective
|
||||
eat.owner = mind
|
||||
eat.explanation_text = "Eat as many living beings as possible to still the hunger within you."
|
||||
eat.completed = TRUE
|
||||
mind.objectives += eat
|
||||
var/datum/objective/procreate = new /datum/objective
|
||||
procreate.owner = mind
|
||||
procreate.explanation_text = "Split yourself in as many other [name]'s as possible!"
|
||||
procreate.completed = TRUE
|
||||
mind.objectives += procreate
|
||||
mind.announce_objectives()
|
||||
|
||||
#undef MORPHED_SPEED
|
||||
#undef ITEM_EAT_COST
|
||||
|
||||
@@ -22,12 +22,7 @@
|
||||
return
|
||||
var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn))
|
||||
player_mind.transfer_to(S)
|
||||
player_mind.assigned_role = SPECIAL_ROLE_MORPH
|
||||
player_mind.special_role = SPECIAL_ROLE_MORPH
|
||||
SSticker.mode.traitors |= player_mind
|
||||
to_chat(S, S.playstyle_string)
|
||||
to_chat(S, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Morph)</span>")
|
||||
SEND_SOUND(S, sound('sound/magic/mutate.ogg'))
|
||||
S.make_morph_antag()
|
||||
message_admins("[key_of_morph] has been made into morph by an event.")
|
||||
log_game("[key_of_morph] was spawned as a morph by an event.")
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#define MORPH_AMBUSH_PERFECTION_TIME 15 SECONDS
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush
|
||||
name = "Prepare Ambush"
|
||||
desc = "Prepare an ambush. Dealing significantly more damage on the first hit and you will weaken the target. Only works while morphed. If the target tries to use you with their hands then you will do even more damage. \
|
||||
Keeping still for another 10 seconds will perfect your disguise."
|
||||
action_icon_state = "morph_ambush"
|
||||
charge_max = 8 SECONDS
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/simple_animal/hostile/morph/user = usr)
|
||||
if(!istype(user))
|
||||
return ..() // Message is in there
|
||||
if(!user.morphed)
|
||||
to_chat(user, "<span class='warning'>You can only prepare an ambush if you're disguised!</span>")
|
||||
return FALSE
|
||||
if(user.ambush_prepared)
|
||||
to_chat(user, "<span class='warning'>You are already prepared!</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
|
||||
if(!istype(user) || !user.morphed || user.ambush_prepared)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush/choose_targets(mob/user)
|
||||
perform(list(user), TRUE, user, FALSE)
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
|
||||
to_chat(user, "<span class='sinister'>You start preparing an ambush.</span>")
|
||||
if(!do_after(user, 6 SECONDS, FALSE, user, TRUE, list(CALLBACK(src, .proc/prepare_check, user)), FALSE))
|
||||
if(!user.morphed)
|
||||
to_chat(user, "<span class='warning'>You need to stay morphed to prepare the ambush!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='warning'>You need to stay still to prepare the ambush!</span>")
|
||||
return
|
||||
user.prepare_ambush()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/ambush/proc/prepare_check(mob/living/simple_animal/hostile/morph/user)
|
||||
return !user.morphed
|
||||
|
||||
/datum/status_effect/morph_ambush
|
||||
id = "morph_ambush"
|
||||
duration = -1
|
||||
tick_interval = MORPH_AMBUSH_PERFECTION_TIME
|
||||
alert_type = /obj/screen/alert/status_effect/morph_ambush
|
||||
|
||||
/datum/status_effect/morph_ambush/tick()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
var/mob/living/simple_animal/hostile/morph/M = owner
|
||||
M.perfect_ambush()
|
||||
linked_alert.name = "Perfect Ambush!"
|
||||
linked_alert.desc = "You have prepared an ambush! Your disguise is flawless!"
|
||||
|
||||
/obj/screen/alert/status_effect/morph_ambush
|
||||
name = "Ambush!"
|
||||
desc = "You have prepared an ambush!"
|
||||
icon_state = "morph_ambush"
|
||||
|
||||
#undef MORPH_AMBUSH_PERFECTION_TIME
|
||||
@@ -0,0 +1,36 @@
|
||||
/obj/effect/proc_holder/spell/morph
|
||||
action_background_icon_state = "bg_morph"
|
||||
clothes_req = FALSE
|
||||
/// How much food it costs the morph to use this
|
||||
var/hunger_cost = 0
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/Initialize(mapload)
|
||||
. = ..()
|
||||
if(hunger_cost)
|
||||
name = "[name] ([hunger_cost])"
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/simple_animal/hostile/morph/user = usr)
|
||||
if(!istype(user))
|
||||
to_chat(user, "<span class='warning'>You should not be able to use this abilty! Report this as a bug on github please.</span>")
|
||||
stack_trace()
|
||||
log_debug("[user] has the spell [src] while he is not a morph")
|
||||
return FALSE
|
||||
if(user.gathered_food < hunger_cost)
|
||||
to_chat(user, "<span class='warning'>You require at least [hunger_cost] stored food to use this ability!</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
|
||||
if(!istype(user) || user.gathered_food < hunger_cost)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/before_cast(list/targets, mob/living/simple_animal/hostile/morph/user)
|
||||
user.use_food(hunger_cost)
|
||||
if(hunger_cost)
|
||||
to_chat(user, "<span class='boldnotice'>You have [user.gathered_food] left to use.</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/revert_cast(mob/living/simple_animal/hostile/morph/user)
|
||||
user.add_food(hunger_cost)
|
||||
to_chat(user, "<span class='boldnotice'>You have [user.gathered_food] left to use.</span>")
|
||||
..()
|
||||
@@ -0,0 +1,46 @@
|
||||
/obj/effect/proc_holder/spell/morph/open_vent
|
||||
name = "Open Vents"
|
||||
desc = "Spit out acidic puke on nearby vents or scrubbers. Will take a little while for the acid to take effect. Not usable from inside a vent."
|
||||
action_icon_state = "acid_vent"
|
||||
charge_max = 10 SECONDS
|
||||
hunger_cost = 10
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/open_vent/choose_targets(mob/user)
|
||||
var/list/targets = list()
|
||||
for(var/obj/machinery/atmospherics/unary/U in view(user, 1))
|
||||
if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/S = U
|
||||
if(S.welded)
|
||||
targets += S
|
||||
else if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/V = U
|
||||
if(V.welded)
|
||||
targets += V
|
||||
|
||||
perform(targets, TRUE, user)
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/open_vent/cast(list/targets, mob/user)
|
||||
if(!length(targets))
|
||||
to_chat(user, "<span class='warning'>No nearby welded vents found!</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
to_chat(user, "<span class='sinister'>You begin regurgitating up some acidic puke!</span>")
|
||||
if(!do_after(user, 2 SECONDS, FALSE, user))
|
||||
to_chat(user, "<span class='warning'>You swallow the acid again.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
for(var/thing in targets)
|
||||
var/obj/machinery/atmospherics/unary/U = thing
|
||||
U.add_overlay(GLOB.acid_overlay, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/unweld_vent, U), 2 SECONDS)
|
||||
playsound(U, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/open_vent/proc/unweld_vent(obj/machinery/atmospherics/unary/U)
|
||||
if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/S = U
|
||||
S.welded = FALSE
|
||||
else if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/V = U
|
||||
V.welded = FALSE
|
||||
U.update_icon()
|
||||
U.cut_overlay(GLOB.acid_overlay, TRUE)
|
||||
@@ -0,0 +1,52 @@
|
||||
// TODO refactor when spell code is component based instead of OO based
|
||||
/obj/effect/proc_holder/spell/targeted/click/pass_airlock
|
||||
name = "Pass Airlock"
|
||||
desc = "Reform yourself so you can fit through a non bolted airlock. Takes a while to do and can only be used in a non disguised form."
|
||||
action_background_icon_state = "bg_morph"
|
||||
action_icon_state = "morph_airlock"
|
||||
clothes_req = FALSE
|
||||
charge_max = 10 SECONDS
|
||||
range = 1
|
||||
allowed_type = /obj/machinery/door/airlock
|
||||
selection_activated_message = "<span class='sinister'>Click on an airlock to try pass it.</span>"
|
||||
click_radius = -1
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/pass_airlock/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/simple_animal/hostile/morph/user = usr)
|
||||
if(!istype(user))
|
||||
to_chat(user, "<span class='warning'>You should not be able to use this abilty! Report this as a bug on github please.</span>")
|
||||
stack_trace()
|
||||
log_debug("[user] has the spell [src] while he is not a morph")
|
||||
return FALSE
|
||||
if(user.morphed)
|
||||
to_chat(user, "<span class='warning'>You can only pass through airlocks in your true form!</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/pass_airlock/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
|
||||
if(!istype(user) || user.morphed)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/pass_airlock/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
|
||||
var/obj/machinery/door/airlock/A = targets[1]
|
||||
if(A.locked)
|
||||
to_chat(user, "<span class='warning'>[A] is bolted shut! You're unable to create a crack to pass through!</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] starts pushing itself against [A]!</span>", "<span class='sinister'>You try to pry [A] open enough to get through.</span>")
|
||||
if(!do_after(user, 6 SECONDS, FALSE, user, TRUE, list(CALLBACK(src, .proc/pass_check, user, A)), FALSE))
|
||||
if(user.morphed)
|
||||
to_chat(user, "<span class='warning'>You need to stay in your true form to pass through [A]!</span>")
|
||||
else if(A.locked)
|
||||
to_chat(user, "<span class='warning'>[A] is bolted shut! You're unable to create a crack to pass through!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need to stay still to pass through [A]!</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
|
||||
user.visible_message("<span class='warning'>[user] briefly opens [A] slightly and passes through!</span>", "<span class='sinister'>You slide through the open crack in [A].</span>")
|
||||
user.forceMove(A.loc) // Move into the turf of the airlock
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/pass_airlock/proc/pass_check(mob/living/simple_animal/hostile/morph/user, obj/machinery/door/airlock/A)
|
||||
return user.morphed || A.locked
|
||||
@@ -0,0 +1,42 @@
|
||||
/obj/effect/proc_holder/spell/morph/reproduce
|
||||
name = "Reproduce"
|
||||
desc = "Split yourself in half making a new morph. Can only be used while on a floor. Makes you temporarily unable to vent crawl."
|
||||
hunger_cost = 150 // 5 humans
|
||||
charge_max = 30 SECONDS
|
||||
action_icon_state = "morph_reproduce"
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/reproduce/choose_targets(mob/user)
|
||||
perform(list(user), TRUE, user, FALSE)
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/reproduce/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/simple_animal/hostile/morph/user = usr)
|
||||
if(!isturf(user.loc))
|
||||
to_chat(user, "<span class='warning'>You can only split while on flooring!</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/reproduce/can_cast(mob/living/simple_animal/hostile/morph/user, charge_check, show_message)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(!isturf(user.loc))
|
||||
return FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/morph/reproduce/cast(list/targets, mob/living/simple_animal/hostile/morph/user)
|
||||
to_chat(user, "<span class='sinister'>You prepare to split in two, making you unable to vent crawl!</span>")
|
||||
user.ventcrawler = FALSE // Temporarily disable it
|
||||
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/hostile/morph)
|
||||
if(!length(candidates))
|
||||
to_chat(user, "<span class='warning'>Your body refuses to split at the moment. Try again later.</span>")
|
||||
revert_cast(user)
|
||||
user.ventcrawler = initial(user.ventcrawler) // re enable the crawling
|
||||
return
|
||||
var/mob/C = pick(candidates)
|
||||
|
||||
var/mob/living/simple_animal/hostile/morph/new_morph = new /mob/living/simple_animal/hostile/morph(get_turf(user))
|
||||
var/datum/mind/player_mind = new /datum/mind(C.key)
|
||||
player_mind.active = TRUE
|
||||
player_mind.transfer_to(new_morph)
|
||||
new_morph.make_morph_antag()
|
||||
user.create_log(MISC_LOG, "Made a new morph using [src]", new_morph)
|
||||
user.ventcrawler = initial(user.ventcrawler) // re enable the crawling
|
||||
@@ -38,7 +38,7 @@
|
||||
C.update_handcuffed()
|
||||
return
|
||||
else
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/wirecutters/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
|
||||
@@ -395,6 +395,28 @@
|
||||
block = GLOB.smallsizeblock
|
||||
..()
|
||||
|
||||
/obj/item/dnainjector/eatmut
|
||||
name = "DNA-Injector (Matter Eater)"
|
||||
desc = "Gives you an appetite for anything."
|
||||
datatype = DNA2_BUF_SE
|
||||
value = 0xFFF
|
||||
forcedmutation = TRUE
|
||||
|
||||
/obj/item/dnainjector/eatmut/Initialize()
|
||||
block = GLOB.eatblock
|
||||
return ..()
|
||||
|
||||
/obj/item/dnainjector/antieat
|
||||
name = "DNA-Injector (Anti-Matter Eater)"
|
||||
desc = "Makes you regain your normal appetite."
|
||||
datatype = DNA2_BUF_SE
|
||||
value = 0x001
|
||||
forcedmutation = TRUE
|
||||
|
||||
/obj/item/dnainjector/antieat/Initialize()
|
||||
block = GLOB.eatblock
|
||||
return ..()
|
||||
|
||||
/////////////////////////////////////
|
||||
/obj/item/dnainjector/antiglasses
|
||||
name = "DNA-Injector (Anti-Glasses)"
|
||||
|
||||
@@ -269,10 +269,7 @@
|
||||
/obj/item/antag_spawner/morph/spawn_antag(client/C, turf/T, type = "", mob/user)
|
||||
var/mob/living/simple_animal/hostile/morph/wizard/M = new /mob/living/simple_animal/hostile/morph/wizard(pick(GLOB.xeno_spawn))
|
||||
M.key = C.key
|
||||
M.mind.assigned_role = SPECIAL_ROLE_MORPH
|
||||
M.mind.special_role = SPECIAL_ROLE_MORPH
|
||||
to_chat(M, M.playstyle_string)
|
||||
SSticker.mode.traitors += M.mind
|
||||
M.make_morph_antag(FALSE)
|
||||
var/datum/objective/assassinate/KillDaWiz = new /datum/objective/assassinate
|
||||
KillDaWiz.owner = M.mind
|
||||
KillDaWiz.target = user.mind
|
||||
@@ -285,4 +282,3 @@
|
||||
M.mind.objectives += KillDaCrew
|
||||
to_chat(M, "<B>Objective #[1]</B>: [KillDaWiz.explanation_text]")
|
||||
to_chat(M, "<B>Objective #[2]</B>: [KillDaCrew.explanation_text]")
|
||||
SEND_SOUND(M, sound('sound/magic/mutate.ogg'))
|
||||
|
||||
@@ -285,6 +285,15 @@ BLIND // can't see anything
|
||||
else
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Tries to turn the sensors off. Returns TRUE if it succeeds
|
||||
*/
|
||||
/obj/item/clothing/under/proc/turn_sensors_off()
|
||||
if(has_sensor != 1)
|
||||
return FALSE
|
||||
sensor_mode = SUIT_SENSOR_OFF
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/under/proc/set_sensors(mob/user as mob)
|
||||
var/mob/M = user
|
||||
if(istype(M, /mob/dead/)) return
|
||||
|
||||
Reference in New Issue
Block a user