Changeling Morph (#10010)

This commit is contained in:
Geeves
2020-09-23 12:16:30 +02:00
committed by GitHub
parent df857171d0
commit ded43af1d7
11 changed files with 327 additions and 57 deletions
+1
View File
@@ -2102,6 +2102,7 @@
#include "code\modules\mob\living\simple_animal\hostile\krampus.dm"
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
#include "code\modules\mob\living\simple_animal\hostile\moghesfauna.dm"
#include "code\modules\mob\living\simple_animal\hostile\morph.dm"
#include "code\modules\mob\living\simple_animal\hostile\phoron_worm.dm"
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
#include "code\modules\mob\living\simple_animal\hostile\pra.dm"
+1 -1
View File
@@ -91,7 +91,7 @@
#define COLOR_OIL "#030303"
#define COLOR_ASH "#615C5B"
#define COLOR_SNOW "#9CADAD"
#define COLOR_LING_HIVEMIND "#94582e"
#define COLOR_LING_HIVEMIND "#583012"
// Blood colors
@@ -1,5 +1,3 @@
// READ: Don't use the apostrophe in name or desc. Causes script errors.
var/list/powers = typesof(/datum/power/changeling) - /datum/power/changeling //needed for the badmin verb for now
var/list/datum/power/changeling/powerinstances = list()
@@ -88,6 +86,12 @@ var/list/datum/power/changeling/powerinstances = list()
genomecost = 0
verbpath = /mob/proc/changeling_eject_hivemind
/datum/power/changeling/hivemind_morph
name = "Hivemind Release Morph"
desc = "We release a hivemind member as a morph at the cost of a limb. They will be able to crawl inside vents and disguise themselves as objects."
genomecost = 2
verbpath = /mob/living/carbon/human/proc/changeling_release_morph
//Stings and sting accessorries
//Rest in pieces, unfat sting. - Geeves
@@ -211,6 +215,7 @@ var/list/datum/power/changeling/powerinstances = list()
// Modularchangling, totally stolen from the new player panel. YAYY
//I'm too afraid to touch this, you win this time, oldcode - Geeves
// After an HTML course, I finally conquered this. Convert into VueUi eventually. - Geeves
/datum/changeling/proc/EvolutionMenu()//The new one
set category = "Changeling"
set desc = "Buy new abilities with the genomes we obtained."
@@ -233,38 +238,34 @@ var/list/datum/power/changeling/powerinstances = list()
var locked_tabs = new Array();
function updateSearch(){
function updateSearch() {
var filter_text = document.getElementById('filter');
var filter = filter_text.value.toLowerCase();
if(complete_list != null && complete_list != ""){
if(complete_list != null && complete_list != "") {
var mtbl = document.getElementById("maintable_data_archive");
mtbl.innerHTML = complete_list;
}
if(filter.value == ""){
if(filter.value == "") {
return;
}else{
} else {
var maintable_data = document.getElementById('maintable_data');
var ltr = maintable_data.getElementsByTagName("tr");
for ( var i = 0; i < ltr.length; ++i )
{
try{
try {
var tr = ltr\[i\];
if(tr.getAttribute("id").indexOf("data") != 0){
if(tr.getAttribute("id").indexOf("data") != 0) {
continue;
}
var ltd = tr.getElementsByTagName("td");
var td = ltd\[0\];
var lsearch = td.getElementsByTagName("b");
var search = lsearch\[0\];
//var inner_span = li.getElementsByTagName("span")\[1\] //Should only ever contain one element.
//document.write("<p>"+search.innerText+"<br>"+filter+"<br>"+search.innerText.indexOf(filter))
if ( search.innerText.toLowerCase().indexOf(filter) == -1 )
{
if ( search.innerText.toLowerCase().indexOf(filter) == -1 ) {
//document.write("a");
//ltr.removeChild(tr);
td.innerHTML = "";
@@ -283,29 +284,24 @@ var/list/datum/power/changeling/powerinstances = list()
}
function expand(id,name,desc,helptext,power,ownsthis){
clearAll();
var span = document.getElementById(id);
body = "<table><tr><td>";
body += "</td><td align='center'>";
body += "<font color='#79d39e' size='2'>"+desc+"</font><br>";
body += "<font size='2'><b>"+desc+"</b></font> <BR>"
if(helptext) {
body += "<font color='#f14c46' size='2'>"+helptext+"</font><br>";
}
body += "<font size='2'><font color = 'red'><b>"+helptext+"</b></font></font><BR>"
if(!ownsthis)
{
body += "<a href='?src=\ref[src];P="+power+"'>Evolve</a>"
if(!ownsthis) {
body += "<a href='?src=\ref[src];P="+power+"'>Evolve</a>";
}
body += "</td><td align='center'>";
body += "</td></tr></table>";
span.innerHTML = body
}
@@ -316,13 +312,13 @@ var/list/datum/power/changeling/powerinstances = list()
var id = span.getAttribute("id");
if(!(id.indexOf("item")==0))
if(!(id.indexOf("item") == 0))
continue;
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
for(var j = 0; j < locked_tabs.length; j++) {
if(locked_tabs\[j\]==id) {
pass = 0;
break;
}
@@ -331,9 +327,6 @@ var/list/datum/power/changeling/powerinstances = list()
if(pass != 1)
continue;
span.innerHTML = "";
}
}
@@ -341,17 +334,17 @@ var/list/datum/power/changeling/powerinstances = list()
function addToLocked(id,link_id,notice_span_id){
var link = document.getElementById(link_id);
var decision = link.getAttribute("name");
if(decision == "1"){
if(decision == "1") {
link.setAttribute("name","2");
}else{
} else {
link.setAttribute("name","1");
removeFromLocked(id,link_id,notice_span_id);
return;
}
var pass = 1;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
for(var j = 0; j < locked_tabs.length; j++) {
if(locked_tabs\[j\] == id) {
pass = 0;
break;
}
@@ -361,12 +354,9 @@ var/list/datum/power/changeling/powerinstances = list()
locked_tabs.push(id);
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "<font color='red'>Locked</font> ";
//link.setAttribute("onClick","attempt('"+id+"','"+link_id+"','"+notice_span_id+"');");
//document.write("removeFromLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
//document.write("aa - "+link.getAttribute("onClick"));
}
function attempt(ab){
function attempt(ab) {
return ab;
}
@@ -374,8 +364,8 @@ var/list/datum/power/changeling/powerinstances = list()
//document.write("a");
var index = 0;
var pass = 0;
for(var j = 0; j < locked_tabs.length; j++){
if(locked_tabs\[j\]==id){
for(var j = 0; j < locked_tabs.length; j++) {
if(locked_tabs\[j\] == id) {
pass = 1;
index = j;
break;
@@ -386,8 +376,6 @@ var/list/datum/power/changeling/powerinstances = list()
locked_tabs\[index\] = "";
var notice_span = document.getElementById(notice_span_id);
notice_span.innerHTML = "";
//var link = document.getElementById(link_id);
//link.setAttribute("onClick","addToLocked('"+id+"','"+link_id+"','"+notice_span_id+"')");
}
function selectTextField(){
@@ -440,9 +428,9 @@ var/list/datum/power/changeling/powerinstances = list()
ownsthis = 1
var/color = "#e6e6e6"
var/color = "#2b2b2b"
if(i%2 == 0)
color = "#f2f2f2"
color = "#363636"
dat += {"
@@ -450,11 +438,7 @@ var/list/datum/power/changeling/powerinstances = list()
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
<td align='center' bgcolor='[color]'>
<span id='notice_span[i]'></span>
<a id='link[i]'
onmouseover='expand("item[i]","[P.name]","[P.desc]","[P.helptext]","[P]",[ownsthis])'
>
<span id='search[i]'><b>Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost]</b></span>
</a>
<font color='#505dcf' id='link[i]' onmouseover='expand("item[i]","[html_encode(P.name)]","[html_encode(P.desc)]","[html_encode(P.helptext)]","[P]",[ownsthis])'><span id='search[i]'><b>Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost]</b></span></font>
<br><span id='item[i]'></span>
</td>
</tr>
@@ -476,7 +460,9 @@ var/list/datum/power/changeling/powerinstances = list()
</body></html>
"}
usr << browse(dat, "window=powers;size=900x480")
var/datum/browser/power_win = new(usr, "powers", "Changeling Powers", 900, 480)
power_win.set_content(dat)
power_win.open()
/datum/changeling/Topic(href, href_list)
@@ -53,4 +53,18 @@
/mob/abstract/hivemind/emote()
to_chat(src, SPAN_WARNING("You cannot emote."))
return
return
/mob/abstract/hivemind/proc/release_as_morph()
to_chat(src, SPAN_DANGER(FONT_LARGE("You are a morph, released by [changeling_mob]!")))
to_chat(src, SPAN_DANGER("As a morph, you can disguise as objects by alt-clicking on them."))
to_chat(src, SPAN_DANGER("You can eat people and items by clicking on them, but only if they're dead."))
relay_hivemind("<font color=[COLOR_LING_HIVEMIND]>Hivemind member [src] has been released into the outside world as a morph!</font>", changeling_mob)
log_and_message_admins("has released [src] as a morph.", changeling_mob, get_turf(changeling_mob))
var/mob/living/simple_animal/hostile/morph/M = new /mob/living/simple_animal/hostile/morph(get_turf(changeling_mob))
M.stop_thinking = TRUE // prevent the AI from taking over when the player ghosts
M.ckey = ckey
changeling_mob.mind.changeling.hivemind_members -= src
qdel(src)
@@ -9,7 +9,7 @@
if(!mind.changeling.hivemind_members.len)
return
var/chosen_player = input("Choose a hivemind member to eject.", "Eject") as null|anything in mind.changeling.hivemind_members
if(!chosen_player || chosen_player == "None")
if(!chosen_player)
return
var/mob/abstract/hivemind/M = mind.changeling.hivemind_members[chosen_player]
M.ghost() //Deuces
@@ -34,4 +34,47 @@
to_chat(ling, message)
/mob/proc/changeling_message_process(var/message)
return "<font color=[COLOR_LING_HIVEMIND]>[src] says, \"[message]\"</font>"
message = capitalize(message)
var/static/list/correct_punctuation = list("!" = TRUE, "." = TRUE, "?" = TRUE, "-" = TRUE, "~" = TRUE, "*" = TRUE, "/" = TRUE, ">" = TRUE, "\"" = TRUE, "'" = TRUE, "," = TRUE, ":" = TRUE, ";" = TRUE)
var/ending = copytext(message, length(message), (length(message) + 1))
if(ending && !correct_punctuation[ending])
message += "."
return "<font color=[COLOR_LING_HIVEMIND]><b>[src]</b> says, \"[message]\"</font>"
/mob/living/carbon/human/proc/changeling_release_morph()
set category = "Changeling"
set name = "Hivemind Release Morph"
set desc = "Releases a member of our internal hivemind as a morph, at the cost of one of our limbs."
if(!mind?.changeling)
return
if(!length(mind.changeling.hivemind_members))
to_chat(src, SPAN_WARNING("We have no internal hivemind members to release!"))
return
var/chosen_player = input("Choose a hivemind member to release as a morph.", "Hivemind Morph") as null|anything in mind.changeling.hivemind_members
if(!chosen_player)
return
var/list/selectable_limb = list()
for(var/organ_name in list(BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG))
var/obj/item/organ/external/limb = organs_by_name[organ_name]
if(limb && !limb.is_stump())
selectable_limb += limb
if(!length(selectable_limb))
to_chat(src, SPAN_WARNING("We have no limbs to sacrifice!"))
return
var/obj/item/organ/external/chosen_limb = input("Choose a limb to sacrifice.", "Limb Sacrifice") as null|anything in selectable_limb
if(!chosen_limb)
return
chosen_limb.droplimb(TRUE, DROPLIMB_BLUNT)
var/mob/abstract/hivemind/M = mind.changeling.hivemind_members[chosen_player]
M.release_as_morph()
return TRUE
@@ -205,6 +205,10 @@ mob/living/simple_animal/hostile/hitby(atom/movable/AM as mob|obj,var/speed = TH
/mob/living/simple_animal/hostile/think()
..()
if(stop_thinking)
return
switch(stance)
if(HOSTILE_STANCE_IDLE)
targets = ListTargets(10)
@@ -0,0 +1,205 @@
#define MORPH_COOLDOWN 50
/mob/living/simple_animal/hostile/morph
name = "morph"
desc = "A revolting, pulsating pile of flesh."
speak_emote = list("gurgles")
emote_hear = list("gurgles")
icon = 'icons/mob/npc/animal.dmi'
icon_state = "morph"
icon_living = "morph"
icon_dead = "morph_dead"
speed = 2.5
stop_automated_movement = TRUE
response_help = "pets"
response_disarm = "shoves"
response_harm = "harmlessly punches"
mob_size = 15
universal_speak = TRUE
universal_understand = TRUE
tameable = FALSE
status_flags = CANPUSH
pass_flags = PASSTABLE
maxHealth = 125
health = 125
melee_damage_lower = 12
melee_damage_upper = 16
see_in_dark = 8
see_invisible = SEE_INVISIBLE_NOLIGHTING
minbodytemp = 0
maxbodytemp = 350
min_oxy = 0
max_co2 = 0
max_tox = 0
wander = FALSE
attacktext = "glomped"
attack_sound = 'sound/effects/blobattack.ogg'
var/morphed = FALSE
var/melee_damage_disguised = 0
var/eat_while_disguised = FALSE
var/atom/movable/form = null
var/morph_time = 0
var/static/list/blacklist_typecache = typecacheof(list(/obj/screen, /obj/singularity, /mob/living/simple_animal/hostile/morph, /obj/effect))
/mob/living/simple_animal/hostile/morph/Initialize()
. = ..()
verbs += /mob/living/proc/ventcrawl
/mob/living/simple_animal/hostile/morph/examine(mob/user)
if(morphed)
. = form.examine(user)
if(get_dist(src, user) > 2)
return
to_chat(user, SPAN_WARNING("It doesn't look quite right..."))
else
return ..()
/mob/living/simple_animal/hostile/morph/proc/allowed(atom/movable/A)
return !is_type_in_typecache(A, blacklist_typecache) && (isobj(A) || ismob(A))
/mob/living/simple_animal/hostile/morph/proc/eat(var/atom/movable/A, var/delay = 20)
if(morphed && !eat_while_disguised)
to_chat(src, SPAN_WARNING("You can't eat anything while you are disguised!"))
return FALSE
if(A?.loc == src)
return FALSE
visible_message(SPAN_WARNING("\The [src] begins swallowing \the [A] whole!"), SPAN_NOTICE("You begin swallowing \the [A] whole."))
if(do_after(src, delay, act_target = A))
visible_message(SPAN_WARNING("\The [src] swallows \the [A] whole!"), SPAN_NOTICE("You swallow \the [A] whole."))
A.forceMove(src)
return TRUE
/mob/living/simple_animal/hostile/morph/AltClickOn(atom/movable/A)
if(morph_time <= world.time && !stat)
if(A == src)
restore()
else if(istype(A) && allowed(A))
assume(A)
else
to_chat(src, SPAN_WARNING("Your chameleon skin is still repairing itself!"))
/mob/living/simple_animal/hostile/morph/proc/assume(atom/movable/target)
if(morphed)
to_chat(src, SPAN_WARNING("You must restore to your original form first!"))
return
morphed = TRUE
form = target
visible_message(SPAN_WARNING("\The [src] suddenly twists and changes shape, becoming a copy of \the [target]!"), SPAN_NOTICE("You twist your body and assume the form of \the [target]."))
appearance = target.appearance
alpha = max(alpha, 150) //fucking chameleons
transform = initial(transform)
pixel_y = initial(pixel_y)
pixel_x = initial(pixel_x)
//Morphed is weaker
melee_damage_lower = melee_damage_disguised
melee_damage_upper = melee_damage_disguised
speed = 1
mob_size = MOB_MINISCULE
morph_time = world.time + MORPH_COOLDOWN
return
/mob/living/simple_animal/hostile/morph/proc/restore()
if(!morphed)
to_chat(src, SPAN_WARNING("You're already in your normal form!"))
return
morphed = FALSE
form = null
alpha = initial(alpha)
color = initial(color)
desc = initial(desc)
animate_movement = SLIDE_STEPS
maptext = null
visible_message(SPAN_WARNING("\The [src] suddenly collapses in on itself, dissolving into a pile of green flesh!"), SPAN_NOTICE("You reform to your normal body."))
name = initial(name)
icon = initial(icon)
icon_state = initial(icon_state)
cut_overlays()
overlays = null
//Baseline stats
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
speed = 2.5
mob_size = 15
morph_time = world.time + MORPH_COOLDOWN
/mob/living/simple_animal/hostile/morph/death(gibbed)
if(morphed)
visible_message(SPAN_WARNING("\The [src] twists and dissolves into a pile of green flesh!"), SPAN_DANGER("Your skin ruptures! Your flesh breaks apart! No disguise can ward off de--"))
restore()
barf_contents()
..()
/mob/living/simple_animal/hostile/morph/proc/barf_contents()
for(var/atom/movable/AM in src)
AM.forceMove(loc)
if(prob(90))
step(AM, pick(global.alldirs))
/mob/living/simple_animal/hostile/morph/UnarmedAttack(atom/A, proximity)
if(morphed && !melee_damage_disguised)
to_chat(src, SPAN_WARNING("You can't attack while disguised!"))
return
if(isliving(A)) //Eat Corpses to regen health
var/mob/living/L = A
if(L.stat == DEAD)
if(eat(L, 30))
adjustBruteLoss(-50)
return
else if(istype(A, /obj/item)) //Eat items just to be annoying
var/obj/item/I = A
if(!I.anchored)
eat(I, 20)
return
return ..()
/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P, def_zone)
..()
if (ismob(P.firer) && target_mob != P.firer)
target_mob = P.firer
stance = HOSTILE_STANCE_ATTACK
/mob/living/simple_animal/hostile/morph/attackby(obj/item/O, mob/user)
..()
if(morphed && user != src)
restore()
/mob/living/simple_animal/hostile/morph/hitby(atom/movable/AM, speed)
..()
if(morphed)
restore()
/mob/living/simple_animal/hostile/morph/attack_generic(mob/user, damage, attack_message)
..()
if(morphed)
restore()
/mob/living/simple_animal/hostile/morph/attack_hand(mob/living/carbon/human/M)
..()
if(morphed && M != src)
restore()
/mob/living/simple_animal/hostile/morph/start_ventcrawl()
if(morphed)
to_chat(src, SPAN_WARNING("You can't ventcrawl while disguised!"))
return
return ..()
/mob/living/simple_animal/hostile/morph/do_animate_chat(var/message, var/datum/language/language, var/small, var/list/show_to, var/duration, var/list/message_override)
INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, language, small, show_to, duration)
@@ -27,6 +27,7 @@
universal_speak = 0 //No, just no.
var/meat_amount = 0
var/meat_type
var/stop_thinking = FALSE // prevents them from doing any AI stuff whatsoever
var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals.
var/wander = 1 // Does the mob wander around when idle?
var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it.
@@ -239,6 +240,10 @@
/mob/living/simple_animal/think()
..()
if(stop_thinking)
return
if(!stop_automated_movement && wander && !anchored)
if(isturf(loc) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc.
if(turns_since_move >= turns_per_move && !(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled
+6 -3
View File
@@ -58,9 +58,12 @@ var/global/list/can_enter_vent_with = list(
/mob/living/proc/ventcrawl_carry()
for(var/atom/A in src.contents)
if(!is_allowed_vent_crawl_item(A))
to_chat(src, "<span class='warning'>You can't be carrying that when vent crawling!</span>")
return 0
return 1
to_chat(src, SPAN_WARNING("You can't be carrying that when vent crawling!"))
return FALSE
return TRUE
/mob/living/simple_animal/hostile/morph/ventcrawl_carry()
return TRUE
/obj/machinery/atmospherics/AltClick(mob/living/user)
if(is_type_in_list(src, ventcrawl_machinery) && user.can_ventcrawl())
+9
View File
@@ -0,0 +1,9 @@
author: Geeves, Sue
delete-after: True
changes:
- rscadd: "Lings can now evolve the ability to release members of their internal hivemind as morphs at the cost of their arm. The ability itself costs two genome points."
- rscadd: "Added morphs, creature capable of assuming the form of any object."
- tweak: "Messages spoken in the internal hivemind will automatically be capitalized and punctuated. Also improved the colour for readability."
- bugfix: "Fixed the script error in the Changeling DNA buy menu, improved the look of it."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 190 KiB