diff --git a/NTstation13.dme b/NTstation13.dme index ae09142204..3a61c2b5b5 100644 --- a/NTstation13.dme +++ b/NTstation13.dme @@ -254,6 +254,12 @@ #include "code\game\gamemodes\changeling\changeling_power.dm" #include "code\game\gamemodes\changeling\evolution_menu.dm" #include "code\game\gamemodes\changeling\traitor_chan.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\changeling_horror.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\death.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\head_spider.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\interactions.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\life.dm" +#include "code\game\gamemodes\changeling\changeling-horror-mob\update_icons.dm" #include "code\game\gamemodes\changeling\powers\absorb.dm" #include "code\game\gamemodes\changeling\powers\digitalcamo.dm" #include "code\game\gamemodes\changeling\powers\epinephrine.dm" @@ -261,6 +267,7 @@ #include "code\game\gamemodes\changeling\powers\fleshmend.dm" #include "code\game\gamemodes\changeling\powers\glands.dm" #include "code\game\gamemodes\changeling\powers\hivemind.dm" +#include "code\game\gamemodes\changeling\powers\horrorform.dm" #include "code\game\gamemodes\changeling\powers\humanform.dm" #include "code\game\gamemodes\changeling\powers\lesserform.dm" #include "code\game\gamemodes\changeling\powers\mimic_voice.dm" diff --git a/NTstation13.int b/NTstation13.int index 3da4246897..0889a91d8f 100644 --- a/NTstation13.int +++ b/NTstation13.int @@ -1,6 +1,11 @@ // BEGIN_INTERNALS /* MAP_ICON_TYPE: 0 +LAST_COMPILE_VERSION: 503.1224 +WINDOW: icons\mob\animal.dmi;code\game\gamemodes\changeling\changeling-horror-mob\changeling_horror.dm;code\game\gamemodes\changeling\changeling-horror-mob\update_icons.dm;code\modules\mob\living\carbon\monkey\update_icons.dm +DIR: code code\game code\game\gamemodes code\game\gamemodes\changeling code\game\gamemodes\changeling\changeling-horror-mob code\modules code\modules\mob code\modules\mob\living code\modules\mob\living\carbon code\modules\mob\living\carbon\monkey icons icons\mob +FILE: code\game\gamemodes\changeling\changeling-horror-mob\update_icons.dm +LAST_COMPILE_TIME: 1399564164 AUTO_FILE_DIR: OFF */ // END_INTERNALS diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index ba74450b50..0dfbd02888 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -182,6 +182,8 @@ datum/hud/New(mob/owner) monkey_hud(ui_style) else if(isbrain(mymob)) brain_hud(ui_style) + else if(ishorror(mymob)) + changelinghorror_hud(ui_style) else if(islarva(mymob)) larva_hud() else if(isalien(mymob)) diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index e1ccf1732a..a3e097bf92 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -33,3 +33,137 @@ mymob.client.screen = null mymob.client.screen += list(blobpwrdisplay, blobhealthdisplay) + + +/datum/hud/proc/changelinghorror_hud(ui_style = 'icons/mob/screen_midnight.dmi') //Not really deserving of it's own File - RR + adding = list() + other = list() + + var/obj/screen/using + var/obj/screen/inventory/inv_box + + lingchemdisplay = new /obj/screen() + lingchemdisplay.name = "chemical storage" + lingchemdisplay.icon_state = "power_display" + lingchemdisplay.screen_loc = ui_lingchemdisplay + lingchemdisplay.layer = 20 + lingchemdisplay.invisibility = 101 + + lingstingdisplay = new /obj/screen() + lingstingdisplay.name = "current sting" + lingstingdisplay.screen_loc = ui_lingstingdisplay + lingstingdisplay.layer = 20 + lingstingdisplay.invisibility = 101 + + mymob.pullin = new /obj/screen() + mymob.pullin.icon = ui_style + mymob.pullin.icon_state = "pull0" + mymob.pullin.name = "pull" + mymob.pullin.screen_loc = ui_pull_resist + + inv_box = new /obj/screen/inventory() + inv_box.name = "r_hand" + inv_box.icon = ui_style + inv_box.icon_state = "hand_r_inactive" + if(mymob && !mymob.hand) + inv_box.icon_state = "hand_r_active" + inv_box.screen_loc = ui_rhand + inv_box.slot_id = slot_r_hand + inv_box.layer = 19 + r_hand_hud_object = inv_box + adding += inv_box + + inv_box = new /obj/screen/inventory() + inv_box.name = "l_hand" + inv_box.icon = ui_style + inv_box.icon_state = "hand_l_inactive" + if(mymob && mymob.hand) + inv_box.icon_state = "hand_l_active" + inv_box.screen_loc = ui_lhand + inv_box.slot_id = slot_l_hand + inv_box.layer = 19 + l_hand_hud_object = inv_box + adding += inv_box + + using = new /obj/screen() + using.name = "drop" + using.icon = ui_style + using.icon_state = "act_drop" + using.screen_loc = ui_drop_throw + using.layer = 19 + adding += using + + mymob.throw_icon = new /obj/screen() + mymob.throw_icon.icon = ui_style + mymob.throw_icon.icon_state = "act_throw_off" + mymob.throw_icon.name = "throw/catch" + mymob.throw_icon.screen_loc = ui_drop_throw + + using = new /obj/screen() + using.name = "mov_intent" + using.icon = ui_style + using.icon_state = (mymob.m_intent == "run" ? "running" : "walking") + using.screen_loc = ui_zonesel + using.layer = 20 + adding += using + move_intent = using + + using = new /obj/screen() + using.name = "act_intent" + using.icon_state = mymob.a_intent + using.screen_loc = ui_acti + using.layer = 20 + adding += using + action_intent = using + + using = new /obj/screen() + using.name = "equip" + using.icon = ui_style + using.icon_state = "act_equip" + using.screen_loc = ui_equip + using.layer = 20 + adding += using + + using = new /obj/screen() + using.name = "hand" + using.icon = ui_style + using.icon_state = "swap_1_m" + using.screen_loc = ui_swaphand1 + using.layer = 19 + adding += using + + using = new /obj/screen() + using.name = "hand" + using.icon = ui_style + using.icon_state = "swap_2" + using.screen_loc = ui_swaphand2 + using.layer = 19 + adding += using + + using = new /obj/screen() + using.name = "resist" + using.icon = ui_style + using.icon_state = "act_resist" + using.screen_loc = ui_pull_resist + using.layer = 19 + adding += using + + mymob.bodytemp = new /obj/screen() + mymob.bodytemp.icon_state = "temp1" + mymob.bodytemp.name = "body temperature" + mymob.bodytemp.screen_loc = ui_temp + + mymob.healths = new /obj/screen() + mymob.healths.icon_state = "health0" + mymob.healths.name = "health" + mymob.healths.screen_loc = ui_health + + mymob.fire = new /obj/screen() + mymob.fire.icon_state = "fire0" + mymob.fire.name = "fire" + mymob.fire.screen_loc = ui_fire + + mymob.client.screen = null + + mymob.client.screen += list(lingchemdisplay, lingstingdisplay, mymob.pullin, mymob.healths, mymob.bodytemp, mymob.fire, mymob.throw_icon) + mymob.client.screen += adding + other \ No newline at end of file diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index ba8e3d6850..47629c4336 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -123,3 +123,30 @@ */ /mob/new_player/ClickOn() return + + +/* + Horror Form Changelings: + Unique Attack features need passing to unique proc +*/ + +/mob/living/carbon/changelinghorror/UnarmedAttack(var/atom/A) + A.attack_horror(src) + +/atom/proc/attack_horror(mob/user as mob) + user.attack_alien(src) //Where we haven't handled a special case, let alien handle it. + +/mob/living/carbon/changelinghorror/RestrainedClickOn(var/atom/A) + return + + +/* + Changeling Head Spiders: + Just a proc override to let them use the takeover proc +*/ +/mob/living/simple_animal/head_spider/UnarmedAttack(var/atom/A) + if(!iscarbon(A)) + A.attack_animal(src) + + var/mob/living/carbon/C = A + attempt_takeover(C) \ No newline at end of file diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/changeling_horror.dm b/code/game/gamemodes/changeling/changeling-horror-mob/changeling_horror.dm new file mode 100644 index 0000000000..0895d54b24 --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/changeling_horror.dm @@ -0,0 +1,72 @@ + +/mob/living/carbon/changelinghorror + icon = 'icons/mob/changelinghorror.dmi' + name = "Shambling Abomination" + desc = "What disgusting horror birthed this monstrosity?" + maxHealth = 350 + health = 350 + factions = list("changeling") + var/Default_Damage = 30 + var/oxygen_alert = 0 + var/toxins_alert = 0 + var/fire_alert = 0 + var/pressure_alert = 0 + + +/mob/living/carbon/changelinghorror/assimilant + maxHealth = 200 + health = 200 + Default_Damage = 20 + + +/mob/living/carbon/changelinghorror/Stat() + ..() + statpanel("Status") + stat(null, text("Intent: []", a_intent)) + stat(null, text("Move Mode: []", m_intent)) + if(client && mind) + if(client.statpanel == "Status") + if(mind.changeling) + stat("Chemical Storage:", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") + stat("Absorbed DNA", mind.changeling.absorbedcount) + return + + +/mob/living/carbon/changelinghorror/say(var/message) + if (!message) + return + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "You cannot send IC messages (muted)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + + if (stat) + return + + horror_say(message) + + +/mob/living/carbon/changelinghorror/proc/horror_say(var/message) + log_say("[key_name(src)] : [message]") + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if(!message) + return + + var/message_a = say_quote(message) + var/rendered = "[name] [message_a]" + + + visible_message(rendered) + + + + + + + + diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/death.dm b/code/game/gamemodes/changeling/changeling-horror-mob/death.dm new file mode 100644 index 0000000000..1b285411d7 --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/death.dm @@ -0,0 +1,17 @@ + +/mob/living/carbon/changelinghorror/death(gibbed) + if(stat == DEAD) + return + if(healths) + healths.icon_state = "health5" + stat = DEAD + dizziness = 0 + jitteriness = 0 + + if(!gibbed) + update_canmove() + if(client) + if(blind) + blind.layer = 0 + + return ..(gibbed) diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/head_spider.dm b/code/game/gamemodes/changeling/changeling-horror-mob/head_spider.dm new file mode 100644 index 0000000000..db495a765d --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/head_spider.dm @@ -0,0 +1,81 @@ + +/mob/living/simple_animal/head_spider + name = "Unknown-Head Spider" + desc = "You can't quite make out who it looks like..." + icon = 'icons/mob/changelinghorror.dmi' + icon_state = "head_spider" + icon_living = "head_spider" + icon_dead = "head_spider_dead" + + speak_chance = 0 + turns_per_move = 5 + response_help = "thinks better of touching" + response_disarm = "cautiously shoves" + response_harm = "hits" + speed = 0 + maxHealth = 40 + health = 40 + + harm_intent_damage = 0 + melee_damage_lower = 0 + melee_damage_upper = 0 + attacktext = "leaps at" + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + factions = list("changeling") + + var/datum/dna/active_dna + +/mob/living/simple_animal/head_spider/proc/attempt_takeover(mob/living/carbon/Victim) + if(!istype(Victim)) + return + + if(ischangeling(Victim) || ishorror(Victim)) + src << "We will not Assimilate our own kind!" + return + + if(ishuman(Victim)) //Only humans have a head slot + var/mob/living/carbon/human/HV = Victim + if(HV.head) + if(HV.head.flags_inv & HIDEFACE) + src << "Their [HV.head.name] prevents you from attatching!" + return + + if(Victim.wear_mask) + if(Victim.wear_mask.flags_inv & MASKCOVERSMOUTH) + src << "Their [Victim.wear_mask.name] prevents you from Attatching!" + return + + //"Revive" previous changeling inside of Victim + + visible_message("The [name] is trying to climb onto [Victim]!","We are climbing [Victim], we must hold still!") + + if(!do_after(src,20)) + visible_message("The [name] falls off of [Victim]!","We have fallen!") + return + + + if(mind && mind.changeling) + Victim.visible_message("The [name] climbs onto [Victim]'s head!","The [name] climbs onto your head!") + Victim.ghostize(0) + mind.transfer_to(Victim) + Victim << "Assimilation Complete!" //Victim = ling here + qdel(src) + + return + + +/mob/living/simple_animal/head_spider/Die() + if(ticker && ticker.mode) + ticker.mode.check_win() //our mind is (should be) a Ling + + ..() diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/interactions.dm b/code/game/gamemodes/changeling/changeling-horror-mob/interactions.dm new file mode 100644 index 0000000000..e6ed13e6cf --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/interactions.dm @@ -0,0 +1,108 @@ + +/* + If it interacted with a Changeling horror, Here's why! + Feel free to undermine me and move things to their *Correct* places + - RR +*/ + + +/mob/living/carbon/human/attack_horror(mob/user as mob) + if(!user || !user.mind || !user.mind.changeling|| !ishorror(user)) + return + + if(user.stat) + return + + switch(user.a_intent) + if("help") + user.visible_message("[user] caresses [src] with one of it's many apendages.") + + if("harm") + var/mob/living/carbon/changelinghorror/CH = user + var/datum/dna/attacker_dna = CH.obtain_valid_dna() + var/attacker = attacker_dna.real_name + var/attack_verb = pick("punches","claws at","swipes at","slices","cuts") + var/attacking_body_part = pick("right arm","left arm") + var/dam_zone = pick("chest","l_hand", "r_hand", "l_lef", "r_leg","head") + var/obj/item/organ/limb/affecting = get_organ(ran_zone(dam_zone)) + var/DAMAGE = CH.Default_Damage*(CH.mind.changeling.absorbed_dna.len/2) + if(affecting) + apply_damage(DAMAGE, BRUTE, affecting, run_armor_check(affecting, "melee")) + visible_message("[CH] reaches out, [attacker]'s [attacking_body_part] [attack_verb] [src]'s [parse_zone(affecting.name)]!","[CH] reaches out, [attacker]'s [attacking_body_part] [attack_verb] your [parse_zone(affecting.name)]!") + else + visible_message("[CH] reaches out, [attacker]'s [attacking_body_part] misses [src]!","[CH] reaches out, [attacker]'s [attacking_body_part] misses you!") + + if ("grab") + if (user == src || anchored) + return + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(user, src ) + + user.put_in_active_hand(G) + + G.synch() + + LAssailant = user + + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + visible_message("[user] has grabbed [name] passively!") + + if ("disarm") + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + var/damage = 5 + if(prob(95)) + Weaken(15) + visible_message("[user] has knocked downn [name]!") + else + if(drop_item()) + visible_message("[user] has disarmed [name]!") + adjustBruteLoss(damage) + updatehealth() + return + +/mob/living/carbon/changelinghorror/proc/obtain_valid_dna() //Loop to ensure a non null DNA is chosen + var/datum/dna/chosen_dna = pick(mind.changeling.absorbed_dna) + + if(!isnull(chosen_dna)) + return chosen_dna + + return obtain_valid_dna() + +/obj/machinery/door/airlock/attack_horror(mob/user as mob) + var/delay = 30 + + if(!user || user.stat) + return + + if(!requiresID() || allowed(user)) + return + + if(arePowerSystemsOn() && !(stat & NOPOWER)) + user << "The airlock's motors resist our efforts to force it" + return + + else if(locked) + user << "The airlock's bolts prevent it from being forced." + return + + + if(!do_after(user,delay)) + return + + user.visible_message("[user] forces the door open!", "We force the door to open!", "You hear a metal screeching sound.") + open(1) + +/obj/item/attack_horror(mob/user as mob) + attack_hand(user) + +/obj/attack_horror(mob/user as mob) + attack_alien(user) + +/turf/simulated/wall/attack_horror(mob/user as mob) + + if(prob(hardness)) + user.visible_message("[user] smashes through the wall!","You smash through the wall!","You hear the sound of crunching metal.") + dismantle_wall(1) + return + else + user.visible_message("[user]'s claw smashes against the wall, but the wall remains!","Your claw smashes against the wall, but the wall remains!","You hear a metallic clang.") + return diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/life.dm b/code/game/gamemodes/changeling/changeling-horror-mob/life.dm new file mode 100644 index 0000000000..59dd2460da --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/life.dm @@ -0,0 +1,195 @@ + +/mob/living/carbon/changelinghorror/Life() + set invisibility = 0 + set background = BACKGROUND_ENABLED + if (notransform) return + ..() + + var/datum/gas_mixture/environment + if(loc) + environment = loc.return_air() + + if (stat != DEAD) + + handle_changeling() + + blinded = null + + if(environment) + handle_environment(environment) + + handle_fire() + + handle_regular_status_updates() + update_canmove() + + if(client) + handle_regular_hud_updates() + + for(var/obj/item/weapon/grab/G in src) + G.process() + + +/mob/living/carbon/changelinghorror/proc/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + var/environment_heat_capacity = environment.heat_capacity() + if(istype(get_turf(src), /turf/space)) + var/turf/heat_turf = get_turf(src) + environment_heat_capacity = heat_turf.heat_capacity + + if(!on_fire) + if((environment.temperature > (T0C + 50)) || (environment.temperature < (T0C + 10))) + var/transfer_coefficient = 1 + + handle_temperature_damage(HEAD, environment.temperature, environment_heat_capacity*transfer_coefficient) + + if(stat != 2) + bodytemperature += 0.1*(environment.temperature - bodytemperature)*environment_heat_capacity/(environment_heat_capacity + 270000) + + var/pressure = environment.return_pressure() + var/adjusted_pressure = calculate_affecting_pressure(pressure) + switch(adjusted_pressure) + if(HAZARD_HIGH_PRESSURE to INFINITY) + adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) + pressure_alert = 2 + if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) + pressure_alert = 1 + if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE) + pressure_alert = 0 + if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE) + pressure_alert = -1 + else + pressure_alert = -1 + return + +/mob/living/carbon/changelinghorror/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) + if(status_flags & GODMODE) return + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + + if(exposed_temperature > bodytemperature) + adjustFireLoss(20.0*discomfort) + + else + adjustFireLoss(5.0*discomfort) + + +/mob/living/carbon/changelinghorror/proc/handle_regular_status_updates() + updatehealth() + + if(stat == DEAD) + blinded = 1 + silent = 0 + else + if(health < 0 || health == 0) + death() + blinded = 1 + stat = DEAD + silent = 0 + return 1 + else + stat = CONSCIOUS + return 1 + + if(stunned) + AdjustStunned(-1) + + if(weakened) + weakened = max(weakened-1,0) + + return 1 + + +/mob/living/carbon/changelinghorror/proc/handle_regular_hud_updates() + + if (stat == 2) + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_LEVEL_TWO + else if (stat != 2) + sight &= ~SEE_TURFS + sight &= ~SEE_MOBS + sight &= ~SEE_OBJS + see_in_dark = 2 + see_invisible = SEE_INVISIBLE_LIVING + if(see_override) + see_invisible = see_override + + if (healths) + if (stat != 2) + switch(health) + if(200 to INFINITY) + healths.icon_state = "health0" + if(160 to 200) + healths.icon_state = "health1" + if(120 to 160) + healths.icon_state = "health2" + if(80 to 120) + healths.icon_state = "health3" + if(40 to 80) + healths.icon_state = "health4" + if(0 to 40) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + if(pressure) + pressure.icon_state = "pressure[pressure_alert]" + + if(pullin) pullin.icon_state = "pull[pulling ? 1 : 0]" + + if (toxin) toxin.icon_state = "tox[toxins_alert ? 1 : 0]" + if (oxygen) oxygen.icon_state = "oxy[oxygen_alert ? 1 : 0]" + if (fire) fire.icon_state = "fire[fire_alert ? 2 : 0]" + + if(bodytemp) + switch(bodytemperature) + if(345 to INFINITY) + bodytemp.icon_state = "temp4" + if(335 to 345) + bodytemp.icon_state = "temp3" + if(327 to 335) + bodytemp.icon_state = "temp2" + if(316 to 327) + bodytemp.icon_state = "temp1" + if(300 to 316) + bodytemp.icon_state = "temp0" + if(295 to 300) + bodytemp.icon_state = "temp-1" + if(280 to 295) + bodytemp.icon_state = "temp-2" + if(260 to 280) + bodytemp.icon_state = "temp-3" + else + bodytemp.icon_state = "temp-4" + + client.screen.Remove(global_hud.blurry,global_hud.druggy,global_hud.vimpaired) + + if (stat != 2) + if (machine) + if (!( machine.check_eye(src) )) + reset_view(null) + else + if(!client.adminobs) + reset_view(null) + + return 1 + +/mob/living/carbon/changelinghorror/proc/handle_changeling() + if(mind && mind.changeling) + mind.changeling.regenerate() + hud_used.lingchemdisplay.invisibility = 0 + hud_used.lingchemdisplay.maptext = "
[src.mind.changeling.chem_charges]
" + + +/mob/living/carbon/changelinghorror/handle_fire() + if(..()) + return + adjustFireLoss(20) //changeling horrors do NOT like fire + return + + diff --git a/code/game/gamemodes/changeling/changeling-horror-mob/update_icons.dm b/code/game/gamemodes/changeling/changeling-horror-mob/update_icons.dm new file mode 100644 index 0000000000..bc021cda87 --- /dev/null +++ b/code/game/gamemodes/changeling/changeling-horror-mob/update_icons.dm @@ -0,0 +1,61 @@ +//Changeling Horror Overlays///// +#define CH_FIRE_LAYER 1 +#define CH_TOTAL_LAYERS 1 +///////////////////////////////// + +/mob/living/carbon/changelinghorror + var/list/overlays_standing[CH_TOTAL_LAYERS] + +/mob/living/carbon/changelinghorror/regenerate_icons() + ..() + update_inv_hands(0) + update_fire() + update_icons() + update_transform() + //Hud Stuff + update_hud() + return + +/mob/living/carbon/changelinghorror/update_icons() + update_hud() + overlays.Cut() + for(var/image/I in overlays_standing) + overlays += I + +/mob/living/carbon/changelinghorror/update_inv_hands(var/update_icons=1) + if(handcuffed) + drop_r_hand() + drop_l_hand() + return + if(r_hand) + r_hand.screen_loc = ui_rhand + if(client && hud_used) + client.screen += r_hand + var/t_state = r_hand.item_state + if(!t_state) t_state = r_hand.icon_state + + if(l_hand) + l_hand.screen_loc = ui_lhand + if(client && hud_used) + client.screen += l_hand + var/t_state = l_hand.item_state + if(!t_state) t_state = l_hand.icon_state + + if(update_icons) + update_icons() + +/mob/living/carbon/changelinghorror/update_hud() + if(client) + client.screen |= contents + +/mob/living/carbon/changelinghorror/update_fire() + overlays -= overlays_standing[CH_FIRE_LAYER] + if(on_fire) + overlays_standing[CH_FIRE_LAYER] = image("icon"='icons/mob/OnFire.dmi', "icon_state"="Standing", "layer"= -CH_FIRE_LAYER) + overlays += overlays_standing[CH_FIRE_LAYER] + return + else + overlays_standing[CH_FIRE_LAYER] = null + +#undef CH_FIRE_LAYER +#undef CH_TOTAL_LAYERS diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 2709071b44..5362c40bf9 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -235,6 +235,11 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon" var/datum/dna/chosen_dna var/obj/effect/proc_holder/changeling/sting/chosen_sting var/space_suit_active = 0 + //Let's HorrorForm's look and be named uniquely + var/horror_name + var/horror_icon + //Used for weak changelings created from the Assimilant Power + var/assimilant = 0 /datum/changeling/New(var/gender=FEMALE) ..() @@ -248,7 +253,18 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon" else changelingID = "[honorific] [rand(1,999)]" absorbed_dna.len = dna_max + UniqueHorror() +/datum/changeling/proc/UniqueHorror() + var/list/prefixHorror = list("Shambling","Crawling","Raging","The","Wretched","Rotting") + var/list/suffixHorror = list("Abomination","Horror","Thing","Pile-Of-Flesh") //Yes you can end up as "The Thing" - RR + + if(assimilant) + horror_icon = "assimilant_[rand(1,2)]" + //Name stuff is handled in Horrorize() for Assimilants + else + horror_name = "[pick(prefixHorror)] [pick(suffixHorror)]" + horror_icon = "horror_[rand(1,4)]" /datum/changeling/proc/regenerate() chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) @@ -269,6 +285,8 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon" if(NOCLONE in target.mutations || HUSK in target.mutations) user << "DNA of [target] is ruined beyond usability!" return + if(ishorror(target)) + return if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway! user << "We could gain no benefit from absorbing a lesser creature." return diff --git a/code/game/gamemodes/changeling/changeling_power.dm b/code/game/gamemodes/changeling/changeling_power.dm index 8307aea0f2..73ae0ea2ca 100644 --- a/code/game/gamemodes/changeling/changeling_power.dm +++ b/code/game/gamemodes/changeling/changeling_power.dm @@ -13,6 +13,7 @@ var/req_dna = 0 //amount of dna needed to use this ability. Changelings always have atleast 1 var/req_human = 0 //if you need to be human to use this ability var/req_stat = CONSCIOUS // CONSCIOUS, UNCONSCIOUS or DEAD + var/req_horror = 0 //if you need to be in Horror Form to use this ability var/genetic_damage = 0 // genetic damage caused by using the sting. Nothing to do with cloneloss. var/max_genetic_damage = 100 // hard counter for spamming abilities. Not used/balanced much yet. @@ -46,11 +47,14 @@ //Fairly important to remember to return 1 on success >.< /obj/effect/proc_holder/changeling/proc/can_sting(var/mob/user, var/mob/target) - if(!ishuman(user) && !ismonkey(user)) //typecast everything from mob to carbon from this point onwards + if(!ishuman(user) && !ismonkey(user) && !ishorror(user)) //typecast everything from mob to carbon from this point onwards return 0 if(req_human && !ishuman(user)) user << "We cannot do that in this form!" return 0 + if(req_horror && !ishorror(user)) + user << "We cannot do that in this form!" + return 0 var/datum/changeling/c = user.mind.changeling if(c.chem_chargesWe require at least [chemical_cost] unit\s of chemicals to do that!
" @@ -71,10 +75,12 @@ //used in /mob/Stat() /obj/effect/proc_holder/changeling/proc/can_be_used_by(var/mob/user) - if(!ishuman(user) && !ismonkey(user)) + if(!ishuman(user) && !ismonkey(user) && !ishorror(user)) return 0 if(req_human && !ishuman(user)) return 0 + if(req_horror && !ishorror(user)) + return 0 return 1 diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index e1e257b927..1637883ada 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -343,7 +343,7 @@ var/list/sting_paths /mob/proc/make_changeling() if(!mind) return - if(!ishuman(src) && !ismonkey(src)) + if(!ishuman(src) && !ismonkey(src) && !ishorror(src)) return if(!mind.changeling) mind.changeling = new /datum/changeling(gender) @@ -373,7 +373,7 @@ var/list/sting_paths mimicing = "" /mob/proc/remove_changeling_powers(var/keep_free_powers=0) - if(ishuman(src) || ismonkey(src)) + if(ishuman(src) || ismonkey(src) || ishorror(src)) if(mind && mind.changeling) digitalcamo = 0 mind.changeling.reset() diff --git a/code/game/gamemodes/changeling/powers/horrorform.dm b/code/game/gamemodes/changeling/powers/horrorform.dm new file mode 100644 index 0000000000..4fcac57068 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/horrorform.dm @@ -0,0 +1,141 @@ + +/* +Horror form abilities, By RR +*/ + + +/////////////////////////// +/// Become Horror /// +/////////////////////////// + +/obj/effect/proc_holder/changeling/HorrorForm + name = "Unleash Horror Form" + desc = "Transform into a Shambling Abomination." + helptext = "Transform into a Shambling Abomination after a short delay, VERY OBVIOUS." + chemical_cost = 40 + dna_cost = 0 + req_human = 1 + req_stat = CONSCIOUS + +/obj/effect/proc_holder/changeling/HorrorForm/sting_action(var/mob/living/carbon/user) + if(!istype(user)) + return + + user.visible_message("[user] collapses into a mound of flesh!","We begin to rework our Genetic structure..","You hear the sound of writhing flesh.") + + var/mob/living/carbon/changelinghorror/Horror + + if(user.mind.changeling.assimilant) + Horror = user.Horrorize(1, "[user.dna.real_name]-Assimilant") + else + Horror = user.Horrorize(0, "[user.mind.changeling.horror_name]") + + Horror.dna = user.dna + Horror.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) + Horror.icon_state = Horror.mind.changeling.horror_icon + Horror.visible_message("the mound of flesh transforms into [Horror]!","We are Revealed!","You hear a Guttural screech.") + + playsound(get_turf(Horror), 'sound/hallucinations/growl2.ogg', 50, 1) + + feedback_add_details("changeling_powers","HF") + .=1 + qdel(user) + + +/////////////////////////// +/// Spawn Assimilant /// +/////////////////////////// + +/obj/effect/proc_holder/changeling/Assimilant + name = "Produce Assimilant" + desc = "Split a portion of our being off to form a seperate being." + helptext = "Lose one of your aborbed DNA to create an Ally with some of your abilities. WARNING, You lose the chosen DNA." + chemical_cost = 20 + dna_cost = 0 + req_horror = 1 + +/obj/effect/proc_holder/changeling/Assimilant/sting_action(var/mob/living/carbon/user) + if(!istype(user)) + return + + var/list/candidates = get_candidates(BE_ALIEN, ALIEN_AFK_BRACKET) + var/client/C = null + + if(candidates.len) + C = pick(candidates) + + if(!C) + user << "You cannot split your consciousness at the present time" + return + + var/datum/dna/CHOSEN = user.mind.changeling.select_dna("Choose a DNA to create an Assimilant from","Choose DNA") + + if(!CHOSEN) + return + + if(CHOSEN == user.dna) + user << "We cannot seperate our current DNA!" + return + + user.mind.changeling.absorbed_dna -= CHOSEN + + user << "We begin to seperate our Genetic structure..." + + if(!do_after(user,40)) + return + + if(C) + var/mob/living/carbon/human/Assimilant = new /mob/living/carbon/human (user.loc) + + C.mob.mind.transfer_to(Assimilant) + + playsound(get_turf(Assimilant), 'sound/hallucinations/growl1.ogg', 50, 1) + + var/datum/changeling/AMC = Assimilant.mind.changeling + + Assimilant.make_changeling() + AMC.assimilant = 1 + AMC.UniqueHorror() + Assimilant.remove_changeling_powers(0) + Assimilant.dna = CHOSEN + updateappearance(Assimilant) + Assimilant.real_name = "[Assimilant.dna.real_name]" + Assimilant.name = Assimilant.real_name + AMC.purchasedpowers += new /obj/effect/proc_holder/changeling/sting/extract_dna(null) + AMC.purchasedpowers += new /obj/effect/proc_holder/changeling/fakedeath(null) + AMC.purchasedpowers += new /obj/effect/proc_holder/changeling/absorbDNA(null) + AMC.purchasedpowers += new /obj/effect/proc_holder/changeling/transform(null) + AMC.purchasedpowers += new /obj/effect/proc_holder/changeling/HorrorForm(null) + Assimilant << "We have split off from the Whole!" + Assimilant << "We are weak and have only a select few abilities" + return 1 + + return + + +/////////////////////////// +/// Spawn Head Spider /// +/////////////////////////// + +/obj/effect/proc_holder/changeling/head_spider + name = "Produce Head Spider" + desc = "Remove our own head to attempt to claim a new body, free of charge" + helptext = "Become a head spider, last resort power" + chemical_cost = 0 //Free, because Revive is arguably a better power + dna_cost = 0 + req_stat = DEAD + + +/obj/effect/proc_holder/changeling/head_spider/sting_action(var/mob/living/carbon/user) + if(!istype(user)) + return + + var/mob/living/simple_animal/head_spider/HS = new (loc) + HS.active_dna = user.dna + HS.name = "[HS.active_dna.real_name]-Head Spider" + HS.desc = "it looks like the head of [HS.active_dna.real_name]!" + if(user.mind && user.mind.changeling) + user.mind.transfer_to(HS) + + .=1 + qdel(user) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 9eb840ccb8..035da77acb 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -126,6 +126,18 @@ proc/isorgan(A) return 1 return 0 +/proc/ischangeling(A) + if(ismob(A)) + var/mob/M = A + if(M.mind && M.mind.changeling) + return 1 + return 0 + +/proc/ishorror(A) //Changeling Horror Form + if(istype(A, /mob/living/carbon/changelinghorror)) + return 1 + return 0 + /proc/check_zone(zone) if(!zone) return "chest" switch(zone) @@ -332,7 +344,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp set name = "a-intent" set hidden = 1 - if(ishuman(src) || isalienadult(src) || isbrain(src)) + if(ishuman(src) || isalienadult(src) || isbrain(src) || ishorror(src)) switch(input) if("help", "disarm", "grab", "harm") a_intent = input diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 03fb40fb3a..7452b592dd 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -15,6 +15,7 @@ //Make mob invisible and spawn animation regenerate_icons() + overlays = null notransform = 1 canmove = 0 stunned = 1 @@ -121,8 +122,14 @@ animation.icon_state = "blank" animation.icon = 'icons/mob/mob.dmi' animation.master = src - flick("monkey2h", animation) - sleep(22) + + if(istype(src, /mob/living/carbon/monkey)) + flick("monkey2h", animation) + sleep(22) + else if (istype(src, /mob/living/carbon/changelinghorror)) + flick("morph_sack",animation) + sleep(20) + var/mob/living/carbon/human/O = new( loc ) for(var/obj/item/C in O.loc) O.equip_to_appropriate_slot(C) @@ -462,6 +469,53 @@ . = new_mob qdel(src) +/mob/proc/Horrorize(Assimilant, newname = null) + + if(notransform) + return + + for(var/obj/item/W in src) + unEquip(W) + + regenerate_icons() + overlays = null + notransform = 1 + canmove = 0 + stunned = 1 + icon = null + invisibility = 101 + var/atom/movable/overlay/animation = new( loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("morph_sack", animation) + sleep(20) + + qdel(animation) + + var/mob/living/carbon/changelinghorror/Horror + + if(Assimilant) + Horror = new /mob/living/carbon/changelinghorror/assimilant (loc) + + else + Horror = new /mob/living/carbon/changelinghorror (loc) + + if(newname) + Horror.real_name = newname + Horror.name = Horror.real_name + + if(suiciding) + Horror.suiciding = suiciding + + Horror.loc = loc + + if(mind) + mind.transfer_to(Horror) + + . = Horror + return + /* Certain mob types have problems and should not be allowed to be controlled by players. * * This proc is here to force coders to manually place their mob in this list, hopefully tested. diff --git a/icons/mob/changelinghorror.dmi b/icons/mob/changelinghorror.dmi new file mode 100644 index 0000000000..fe9aacd7d3 Binary files /dev/null and b/icons/mob/changelinghorror.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 28451ff9fc..be1dd22bca 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ