diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 997e63c8050f4..575eebae03d19 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/spaceman_dmm/${{ env.SPACEMAN_DMM_VERSION }} key: ${{ runner.os }}-spacemandmm-${{ env.SPACEMAN_DMM_VERSION }} @@ -49,7 +49,7 @@ jobs: run: | ~/dmdoc - name: Deploy - uses: JamesIves/github-pages-deploy-action@dc18a3c6b46d56484cb63f291becd7ed4f0269b9 + uses: JamesIves/github-pages-deploy-action@15de0f09300eea763baee31dff6c6184995c5f6a with: token: ${{ secrets.GITHUB_TOKEN }} branch: gh-pages diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fda9010d8f601..d6544e4c0605f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/spaceman_dmm/${{ env.SPACEMAN_DMM_VERSION }} key: ${{ runner.os }}-spacemandmm-${{ env.SPACEMAN_DMM_VERSION }} @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} @@ -92,14 +92,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} - name: Install rust_g dependencies run: ./scripts/install-rust_g-dependencies.sh - name: Setup rust_g cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/.byond/bin/librust_g.so key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" @@ -130,14 +130,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} - name: Install rust_g dependencies run: ./scripts/install-rust_g-dependencies.sh - name: Setup rust_g cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/.byond/bin/librust_g.so key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" @@ -168,14 +168,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup Cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ runner.os }}-byond-${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }} - name: Install rust_g dependencies run: ./scripts/install-rust_g-dependencies.sh - name: Setup rust_g cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 with: path: ~/.byond/bin/librust_g.so key: "rust_g-${{ env.RUST_G_REPO }}-${{ env.RUST_G_VERSION }}" diff --git a/baystation12.dme b/baystation12.dme index cbc43ffe2b6b0..599d6e5c7b72a 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -34,6 +34,7 @@ #include "code\__defines\atmos.dm" #include "code\__defines\atmospherics.dm" #include "code\__defines\byond_tracy.dm" +#include "code\__defines\changeling.dm" #include "code\__defines\chemistry.dm" #include "code\__defines\client.dm" #include "code\__defines\colors.dm" @@ -348,6 +349,8 @@ #include "code\datums\item_modifiers\space_suits.dm" #include "code\datums\item_modifiers\~defines.dm" #include "code\datums\licences\license.dm" +#include "code\datums\managed_browsers\_managed_browser.dm" +#include "code\datums\managed_browsers\changelingevolution.dm" #include "code\datums\mind\memory.dm" #include "code\datums\mind\mind.dm" #include "code\datums\move_intent\move_intent.dm" @@ -610,8 +613,48 @@ #include "code\game\gamemodes\calamity\calamity.dm" #include "code\game\gamemodes\changeling\absorbed_dna.dm" #include "code\game\gamemodes\changeling\changeling.dm" +#include "code\game\gamemodes\changeling\changeling_evolutiontree.dm" #include "code\game\gamemodes\changeling\changeling_powers.dm" -#include "code\game\gamemodes\changeling\modularchangling.dm" +#include "code\game\gamemodes\changeling\generic_equip_procs.dm" +#include "code\game\gamemodes\changeling\powers\absorb.dm" +#include "code\game\gamemodes\changeling\powers\armblade.dm" +#include "code\game\gamemodes\changeling\powers\armor.dm" +#include "code\game\gamemodes\changeling\powers\augmented_eyesight.dm" +#include "code\game\gamemodes\changeling\powers\bioelectrogenesis.dm" +#include "code\game\gamemodes\changeling\powers\blind_sting.dm" +#include "code\game\gamemodes\changeling\powers\boost_range.dm" +#include "code\game\gamemodes\changeling\powers\cryo_sting.dm" +#include "code\game\gamemodes\changeling\powers\darkvision.dm" +#include "code\game\gamemodes\changeling\powers\deaf_sting.dm" +#include "code\game\gamemodes\changeling\powers\death_sting.dm" +#include "code\game\gamemodes\changeling\powers\delayed_toxin_sting.dm" +#include "code\game\gamemodes\changeling\powers\electric_lockpick.dm" +#include "code\game\gamemodes\changeling\powers\endoarmor.dm" +#include "code\game\gamemodes\changeling\powers\engorged_glands.dm" +#include "code\game\gamemodes\changeling\powers\enrage.dm" +#include "code\game\gamemodes\changeling\powers\escape_restraints.dm" +#include "code\game\gamemodes\changeling\powers\extract_dna_sting.dm" +#include "code\game\gamemodes\changeling\powers\fabricate_clothing.dm" +#include "code\game\gamemodes\changeling\powers\fake_death.dm" +#include "code\game\gamemodes\changeling\powers\false_identity.dm" +#include "code\game\gamemodes\changeling\powers\fleshmend.dm" +#include "code\game\gamemodes\changeling\powers\hivemind.dm" +#include "code\game\gamemodes\changeling\powers\lsd_sting.dm" +#include "code\game\gamemodes\changeling\powers\mimic_voice.dm" +#include "code\game\gamemodes\changeling\powers\panacea.dm" +#include "code\game\gamemodes\changeling\powers\rapid_regen.dm" +#include "code\game\gamemodes\changeling\powers\rapid_synthesis.dm" +#include "code\game\gamemodes\changeling\powers\reattach_limb.dm" +#include "code\game\gamemodes\changeling\powers\recursive_enhancement.dm" +#include "code\game\gamemodes\changeling\powers\reinforced_tendons.dm" +#include "code\game\gamemodes\changeling\powers\revive.dm" +#include "code\game\gamemodes\changeling\powers\self_respiration.dm" +#include "code\game\gamemodes\changeling\powers\shriek.dm" +#include "code\game\gamemodes\changeling\powers\silence_sting.dm" +#include "code\game\gamemodes\changeling\powers\synaptizine_overdose.dm" +#include "code\game\gamemodes\changeling\powers\tentacle.dm" +#include "code\game\gamemodes\changeling\powers\transform.dm" +#include "code\game\gamemodes\changeling\powers\visible_camouflage.dm" #include "code\game\gamemodes\cult\cult.dm" #include "code\game\gamemodes\cult\cult_items.dm" #include "code\game\gamemodes\cult\cult_structures.dm" @@ -1532,7 +1575,10 @@ #include "code\modules\augment\passive\armor.dm" #include "code\modules\augment\passive\boost.dm" #include "code\modules\augment\passive\fluff.dm" +#include "code\modules\augment\passive\ling_lenses.dm" +#include "code\modules\augment\passive\lingcore.dm" #include "code\modules\augment\passive\nanoaura.dm" +#include "code\modules\augment\passive\ragecore.dm" #include "code\modules\augment\passive\boost\muscle.dm" #include "code\modules\augment\passive\boost\reflex.dm" #include "code\modules\augment\passive\boost\shooting.dm" @@ -3032,6 +3078,7 @@ #include "code\modules\shuttles\shuttles_multi.dm" #include "code\modules\skrell\skrell_rigs.dm" #include "code\modules\species\species.dm" +#include "code\modules\species\species_age_comparison.dm" #include "code\modules\species\species_attack.dm" #include "code\modules\species\species_getters.dm" #include "code\modules\species\species_grab.dm" diff --git a/code/__defines/changeling.dm b/code/__defines/changeling.dm new file mode 100644 index 0000000000000..7323244d7b72e --- /dev/null +++ b/code/__defines/changeling.dm @@ -0,0 +1,2 @@ +#define ABSORB_NONLETHAL 1 +#define ABSORB_LETHAL 2 diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 34f171523147f..ef75f71f548d0 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -97,6 +97,9 @@ #define Sp_CHARGES "charges" #define Sp_HOLDVAR "holdervar" +//changeling cost +#define CHANGELING_STASIS_COST 20 + //Voting-related #define VOTE_PROCESS_ABORT 1 #define VOTE_PROCESS_COMPLETE 2 @@ -105,3 +108,14 @@ #define VOTE_STATUS_PREVOTE 1 #define VOTE_STATUS_ACTIVE 2 #define VOTE_STATUS_COMPLETE 3 + +/* +Changeling Defines +*/ +#define CHANGELING_POWER_INHERENT "Inherent" +#define CHANGELING_POWER_ARMOR "Armor" +#define CHANGELING_POWER_STINGS "Stings" +#define CHANGELING_POWER_SHRIEKS "Shrieks" +#define CHANGELING_POWER_HEALTH "Health" +#define CHANGELING_POWER_ENHANCEMENTS "Enhancements" +#define CHANGELING_POWER_WEAPONS "Weapons" diff --git a/code/__defines/~mods/~master_defines.dm b/code/__defines/~mods/~master_defines.dm index 796d9225cd83d..6893379bdda96 100644 --- a/code/__defines/~mods/~master_defines.dm +++ b/code/__defines/~mods/~master_defines.dm @@ -99,3 +99,9 @@ #define ANNOUNCER_BLUESPACEJUMP_PREP 'mods/eris_announcer/sound/announcements/bluespacejump_prepare.ogg' #define ANNOUNCER_BLUESPACEJUMP_START 'mods/eris_announcer/sound/announcements/bluespacejump_start.ogg' // ERIS_ANNOUNCER - End + +//PRIMLANGS - Start +#define LANGUAGE_SIMPSKRELLIAN "Simplified Skrellian" +#define LANGUAGE_SIMPUNATHI "Iber'Unathi" +#define LANGUAGE_SIMPTAJARAN "Simplified Siik'Maas" +//PRIMLANGS - End diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 187145f271bac..120c1ccf5faa9 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -103,6 +103,52 @@ /proc/get_exposed_defense_zone(atom/movable/target) return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG, BP_CHEST, BP_GROIN) +/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = FALSE, progress = TRUE, ignore_movement = FALSE, incapacitation_affected = TRUE) + if(!user || !target) + return 0 + var/user_loc = user.loc + var/target_loc = target.loc + + var/holding = user.get_active_hand() + var/datum/progressbar/private/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = TRUE + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + if(!user || !target) + . = FALSE + break + if(uninterruptible) + continue + + if(!user || (user.incapacitated() && incapacitation_affected)) + . = FALSE + break + + if(user.loc != user_loc && !ignore_movement) + . = FALSE + break + + if(target.loc != target_loc && !ignore_movement) + . = FALSE + break + + if(user.get_active_hand() != holding) + . = FALSE + break + + if(target_zone && user.zone_sel.selecting != target_zone) + . = FALSE + break + + if (progbar) + qdel(progbar) /// Integer. Unique sequential ID from the `do_after` proc used to validate `DO_USER_UNIQUE_ACT` flag checks. /mob/var/do_unique_user_handle = 0 diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index 7b07703e89c74..43bf4f9dbbae6 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -252,9 +252,9 @@ //Changeling Abilities /obj/screen/ability/verb_based/changeling - icon_state = "ling_spell_base" - background_base_state = "ling" - + icon_state = "const_spell_base" + background_base_state = "const" +//use this to force add powers /obj/screen/movable/ability_master/proc/add_ling_ability(object_given, verb_given, name_given, ability_icon_given, arguments) if(!object_given) message_admins("ERROR: add_ling_ability() was not given an object in its arguments.") diff --git a/code/datums/extensions/chameleon.dm b/code/datums/extensions/chameleon.dm index ffacbddc2423e..36b5b2b2aa833 100644 --- a/code/datums/extensions/chameleon.dm +++ b/code/datums/extensions/chameleon.dm @@ -23,6 +23,7 @@ if (!chameleon_choices) var/chameleon_type = chameleon_base_type || holder.parent_type chameleon_choices = LAZYACCESS(chameleon_choices_by_type, chameleon_type) + if (!chameleon_choices) chameleon_choices = GenerateChameleonChoices(chameleon_type) LAZYSET(chameleon_choices_by_type, chameleon_type, chameleon_choices) @@ -69,8 +70,10 @@ return null /datum/extension/chameleon/proc/GenerateChameleonChoices(basetype) + var/choices = list() var/types = islist(basetype) ? basetype : typesof(basetype) + for (var/path in types) AddChameleonChoice(choices, path) return sortAssoc(choices) diff --git a/code/datums/managed_browsers/_managed_browser.dm b/code/datums/managed_browsers/_managed_browser.dm new file mode 100644 index 0000000000000..02fd99615d424 --- /dev/null +++ b/code/datums/managed_browsers/_managed_browser.dm @@ -0,0 +1,52 @@ +GLOBAL_VAR(managed_browser_id_ticker) + +// This holds information on managing a /datum/browser object. +// Managing can include things like persisting the state of specific information inside of this object, receiving Topic() calls, or deleting itself when the window is closed. +// This is useful for browser windows to be able to stand 'on their own' instead of being tied to something in the game world, like an object or mob. +/datum/managed_browser + var/client/my_client = null + var/browser_id = null + var/base_browser_id = null + + var/title = null + var/size_x = 200 + var/size_y = 400 + + var/display_when_created = TRUE + +/datum/managed_browser/New(client/new_client) + if(!new_client) + crash_with("Managed browser object was not given a client.") + return + if(!base_browser_id) + crash_with("Managed browser object does not have a base browser id defined in its type.") + return + + my_client = new_client + browser_id = "[base_browser_id]-[GLOB.managed_browser_id_ticker++]" + + if(display_when_created) + display() + +/datum/managed_browser/Destroy() + my_client = null + return ..() + +// Override if you want to have the browser title change conditionally. +// Otherwise it's easier to just change the title variable directly. +/datum/managed_browser/proc/get_title() + return title + +// Override to display the html information. +// It is suggested to build it with a list, and use list.Join() at the end. +// This helps prevent excessive concatination, which helps preserves BYOND's string tree from becoming a laggy mess. +/datum/managed_browser/proc/get_html() + return + +/datum/managed_browser/proc/display() + interact(get_html(), get_title(), my_client) + +/datum/managed_browser/proc/interact(html, title, client/C) + var/datum/browser/popup = new(C.mob, browser_id, title, size_x, size_y, src) + popup.set_content(html) + popup.open() diff --git a/code/datums/managed_browsers/changelingevolution.dm b/code/datums/managed_browsers/changelingevolution.dm new file mode 100644 index 0000000000000..340702906c25c --- /dev/null +++ b/code/datums/managed_browsers/changelingevolution.dm @@ -0,0 +1,165 @@ +/client + var/datum/managed_browser/changelingevolution/changelingevolution = null + +/datum/managed_browser/changelingevolution + base_browser_id = "evolution_tree" + title = "Evolution Tree" + size_x = 480 + size_y = 600 + var/textbody = null + +/datum/managed_browser/changelingevolution/New(client/new_client) + if(!new_client.mob || !new_client.mob.mind || !new_client.mob.mind.changeling) + message_admins("[new_client] tried to access changeling evolutions while not changeling.") + qdel(src) + + ..() + +/datum/managed_browser/changelingevolution/Destroy() + if(my_client) + my_client.changelingevolution = null + return ..() + +/datum/managed_browser/changelingevolution/get_html() + var/list/dat = list("") + var/geneticpoints_current = my_client.mob.mind.changeling.geneticpoints + var/geneticpoints_max = my_client.mob.mind.changeling.max_geneticpoints + + dat += "
Genetic Points Available: [geneticpoints_current] / [geneticpoints_max]
" + dat += "Obtain more by feeding on your own kind.

" + dat += "What am I?

" + dat += "Inherent" + dat += "Armor" + dat += "Weapons" + dat += "Stings" + dat += "Shrieks" + dat += "Health" + dat += "Enhancements
" + if(textbody) + dat += "" + dat += "[textbody]" + dat += "
" + dat += "" + + return dat.Join() + +/datum/managed_browser/changelingevolution/Topic(href, href_list[]) + if(!my_client) + return FALSE + + if(href_list["close"]) + return + + if(href_list["inherent"]) + generate_abilitylist(CHANGELING_POWER_INHERENT) + + if(href_list["armor"]) + generate_abilitylist(CHANGELING_POWER_ARMOR) + + if(href_list["weapons"]) + generate_abilitylist(CHANGELING_POWER_WEAPONS) + + if(href_list["stings"]) + generate_abilitylist(CHANGELING_POWER_STINGS) + + if(href_list["shrieks"]) + generate_abilitylist(CHANGELING_POWER_SHRIEKS) + + if(href_list["health"]) + generate_abilitylist(CHANGELING_POWER_HEALTH) + + if(href_list["enhancements"]) + generate_abilitylist(CHANGELING_POWER_ENHANCEMENTS) + + if(href_list["evolve"]) + var/datum/mind/M = my_client.mob.mind + var/datum/changeling/C = my_client.mob.mind.changeling + var/datum/power/changeling/Thepower = href_list["evolve"] + + for (var/datum/power/changeling/P in GLOB.powerinstances) + if(P.name == Thepower) + Thepower = P + break + + if(!istype(M)) + return + + if(!Thepower) + CRASH("Purchase failed. Inform a dev of this error.") + + if(Thepower in C.purchased_powers) + to_chat(M.current, "You already have this ability! Inform a dev of this error.") /// Should not be possible + return + + if(C.geneticpoints < Thepower.genomecost) + to_chat(M.current, "We cannot evolve this... yet. We must acquire more DNA.") + return + + C.purchased_powers += Thepower /// Set it to purchased + C.geneticpoints -= Thepower.genomecost + generate_abilitylist(Thepower.power_category) /// Refresh the UI + + my_client.mob.mind.changeling.purchasePower(M, Thepower) + + if(href_list["tutorial"]) + textbody = "
What am I?

" + textbody += "" + textbody += "You are a changeling, a creature empowered with genetic-based abilities that change your body in bizarre ways." + textbody += " It's probably best the crew doesn't know about your power -- at least not right away.

" + textbody += "What a changeling is, however, is up to you. Are you a strange alien impersonating crew? Are you a" + textbody += " normal crewmember infected with a parasite? An experiment gone wrong? It's up to you to make the story.

" + textbody += "Of course, you need to know how it works to begin with.

" + textbody += "Your abilities cost chemicals that your body will slowly regenerate with varying speeds based on enhancements obtained." + textbody += " There are a set of inherent abilities you will always have while the rest may be purchased through genomes.

" + textbody += "You may obtain more genomes if you find another changeling and absorb them, but this is not required. If you've found " + textbody += "your abilities aren't to your liking, you have up to two re-adapts available, and these may be refilled by absorbing anyone -- including monkeys.

" + textbody += "Good luck and remember, killing isn't always the end goal." + display() + +/datum/managed_browser/changelingevolution/proc/generate_abilitylist(cat) + var/list/ability_list = list() + var/info = "" + var/catname = "" + for(var/datum/power/changeling/P in GLOB.powerinstances) + if(P.power_category == cat) + ability_list[LIST_PRE_INC(ability_list)] = P + switch(cat) + if(CHANGELING_POWER_INHERENT) + catname = "Inherent" + info = "These powers are inherent to your kind and will always be accessible, provided you have the chemicals to use them." + if(CHANGELING_POWER_ARMOR) + catname = "Armor" + info = "These abilities will provide you with space protection -- and potentially armor." + if(CHANGELING_POWER_WEAPONS) + catname = "Weapons" + info = "These abilities will provide you the means to fight back." + if(CHANGELING_POWER_STINGS) + catname = "Stings" + info = "These abilities provide the means to sting organic beings for various effects -- though you must be close enough, and they must have exposed flesh." + if(CHANGELING_POWER_SHRIEKS) + catname = "Shrieks" + info = "These abilities enhance your vocal chords, empowering your screams." + if(CHANGELING_POWER_HEALTH) + catname = "Health" + info = "These abilities will enhance your health or aid you in mending your wounds." + if(CHANGELING_POWER_ENHANCEMENTS) + catname = "Enhancements" + info = "These abilities enhance you in various ways." + create_textbody(ability_list, catname, info) + +/datum/managed_browser/changelingevolution/proc/create_textbody(ability_list, cat, catinfo) + textbody = "
[cat] Skills
" + textbody += "[catinfo]


" + for(var/A in ability_list) + var/datum/power/changeling/powerdata = A + textbody += "
[initial(powerdata.name)]
" + textbody += "[initial(powerdata.desc)]

" + textbody += "[powerdata.helptext]
" + if(powerdata.enhancedtext != "") + textbody += "WHEN EHANCED: [powerdata.enhancedtext]
" + if(powerdata in my_client.mob.mind.changeling.purchased_powers) + textbody += "
This ability is already evolved!
" + else if(cat != "Inherent") + textbody += "
Evolve
" + textbody += "" + display() diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 07cce3b41eede..3d071b22387d5 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -82,7 +82,7 @@ if(current) //remove ourself from our old body's mind variable if(changeling) current.remove_changeling_powers() - current.verbs -= /datum/changeling/proc/EvolutionMenu + current.verbs -= /datum/changeling/proc/EvolutionTree current.mind = null SSnano.user_transferred(current, new_character) // transfer active NanoUI instances to new user @@ -97,9 +97,6 @@ if(learned_spells && length(learned_spells)) restore_spells(new_character) - if(changeling) - new_character.make_changeling() - if(active) new_character.key = key //now transfer the key to link the client to our new body diff --git a/code/datums/observation/see_in_dark_set.dm b/code/datums/observation/see_in_dark_set.dm index ea1235f92b0fc..cc7a9671b79d5 100644 --- a/code/datums/observation/see_in_dark_set.dm +++ b/code/datums/observation/see_in_dark_set.dm @@ -18,7 +18,7 @@ GLOBAL_DATUM_INIT(see_in_dark_set_event, /singleton/observ/see_in_dark_set, new) * See In Dark Set Handling * ***************************/ -/mob/proc/set_see_in_dark(new_see_in_dark) +/mob/proc/set_see_in_dark(new_see_in_dark, seedarkness) var/old_see_in_dark = sight if(old_see_in_dark != new_see_in_dark) see_in_dark = new_see_in_dark diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm index 68a4e5ac44750..77f60752c282f 100644 --- a/code/game/antagonist/station/changeling.dm +++ b/code/game/antagonist/station/changeling.dm @@ -27,8 +27,7 @@ GLOBAL_DATUM_INIT(changelings, /datum/antagonist/changeling, new) . = ..() if(. && player && player.current) player.current.remove_changeling_powers() - player.current.verbs -= /datum/changeling/proc/EvolutionMenu - player.current.remove_language(LANGUAGE_CHANGELING_GLOBAL) + player.current.verbs -= /datum/changeling/proc/EvolutionTree QDEL_NULL(player.changeling) /datum/antagonist/changeling/create_objectives(datum/mind/changeling) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 8d4c783f0729c..ef1d39e9e8f27 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -158,7 +158,9 @@ H.update_eyes() H.skin_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative. - + //Necessary for mirror to set plural properly + if(H.gender == PLURAL) + H.gender = PLURAL if(H.gender != NEUTER) if (dna.GetUIState(DNA_UI_GENDER)) H.gender = FEMALE diff --git a/code/game/gamemodes/changeling/absorbed_dna.dm b/code/game/gamemodes/changeling/absorbed_dna.dm index 850939253181a..907c64ace2bcb 100644 --- a/code/game/gamemodes/changeling/absorbed_dna.dm +++ b/code/game/gamemodes/changeling/absorbed_dna.dm @@ -3,12 +3,21 @@ var/datum/dna/dna var/speciesName var/list/languages + var/gender var/pronouns - -/datum/absorbed_dna/New(newName, newDNA, newSpecies, newLanguages, newPronouns) + var/list/flavour_texts + var/list/genMods + var/list/descriptors + var/list/icon_render_details +/datum/absorbed_dna/New(newName, newDNA, newSpecies, newLanguages, newGender , newPronouns, list/newFlavour, list/New_icon_render_keys, list/newBuild) ..() name = newName dna = newDNA speciesName = newSpecies languages = newLanguages + gender = newGender pronouns = newPronouns + flavour_texts = newFlavour ? newFlavour.Copy() : null + icon_render_details = New_icon_render_keys ? New_icon_render_keys : list() + //defaults to average height and build if none set + descriptors = newBuild ? newBuild.Copy() : null \ No newline at end of file diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index e6966e09426ac..831bcdbe6c8ef 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -11,7 +11,7 @@ No one knows where it came from. No one knows who it is or what it wants. One thing is for \ certain though... there is never just one of them. Good luck." config_tag = "changeling" - required_players = 5 + required_players = 12 required_enemies = 1 end_on_antag_death = FALSE antag_scaling_coeff = 15 diff --git a/code/game/gamemodes/changeling/changeling_evolutiontree.dm b/code/game/gamemodes/changeling/changeling_evolutiontree.dm new file mode 100644 index 0000000000000..fadbdcde30c6a --- /dev/null +++ b/code/game/gamemodes/changeling/changeling_evolutiontree.dm @@ -0,0 +1,368 @@ +// READ: Don't use the apostrophe in name or desc. Causes script errors. + +//Ling power's evolution menu entry datum should be contained alongside the mob proc for the actual power, in their own file. + +var/global/list/powers = typesof(/datum/power/changeling) - /datum/power/changeling //needed for the badmin verb for now +GLOBAL_LIST_EMPTY(powerinstances) +/mob/proc/generic_sting() + return +/datum/power //Could be used by other antags too + var/name = "Power" + var/desc = "Placeholder" + var/helptext = "" + var/enhancedtext = "" + /// Is it an active power, or passive? + var/isVerb = 1 + /// Path to a verb that contains the effects. + var/verbpath + /// Is this ability significant enough to dedicate screen space for a HUD button? + var/make_hud_button = 1 + /// icon_state for icons for the ability HUD. Must be in screen_spells.dmi. + var/ability_icon_state = null + var/is_sting = FALSE +/datum/power/changeling + var/allowduringlesserform = 0 + /// Cost for the changling to evolve this power. + var/genomecost = 500000 + /// _defines/gamemode.dm + var/power_category = null + /// only override for sting powers, used for boost range + var/sting_effect = /mob/proc/generic_sting + var/sting_duration = null + var/reagents_transferred = null + +// Modularchangling, totally stolen from the new player panel. YAYY +/datum/changeling/proc/EvolutionTree()//The new one + set name = "-Evolution Tree-" + set category = "Changeling" + set desc = "Adapt yourself carefully." + + if(!usr || !usr.mind || !usr.mind.changeling) return + src = usr.mind.changeling + + if(!length(GLOB.powerinstances)) + for(var/P in powers) + GLOB.powerinstances += new P() + + var/dat = "Changeling Evolution Tree" + + //javascript, the part that does most of the work~ + dat += {" + + + + + + + "} + + //body tag start + onload and onkeypress (onkeyup) javascript event calls + dat += "" + + //title + search bar + dat += {" + + + + + + + + +
+ Changeling Evolution Menu
+ Hover over a power to see more information
+ Current evolution points left to evolve with: [geneticpoints]
+ Absorb other changelings to acquire more evolution points +

+

+ Search: +
+ + "} + + //player table header + dat += {" + + "} + + var/i = 1 + for(var/datum/power/changeling/P in GLOB.powerinstances) + var/ownsthis = 0 + + if(P in purchased_powers) + ownsthis = 1 + + + var/color = "#e6e6e6" + if(i%2 == 0) + color = "#f2f2f2" + + + dat += {" + + + + + + "} + + i++ + + + //player table ending + dat += {" +
+ + + Evolve [P] - Cost: [ownsthis ? "Purchased" : P.genomecost] + +
+
+
+ + + + "} + + show_browser(usr, dat, "window=powers;size=900x480") + + +/datum/changeling/Topic(href, href_list) + ..() + if(!ismob(usr)) + return + + if(href_list["P"]) + var/datum/mind/M = usr.mind + if(!istype(M)) + return + purchasePower(M, href_list["P"]) + call(/datum/changeling/proc/EvolutionTree)() + + + +/datum/changeling/proc/purchasePower(datum/mind/M, Pname, remake_verbs = 1) + if(!M || !M.changeling) + return + + var/datum/power/changeling/Thepower = Pname + + + for (var/datum/power/changeling/P in GLOB.powerinstances) + if(P.name == Pname) + Thepower = P + break + + + if(!Thepower) + to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") + return + + if(Thepower in purchased_powers) + return + + + if(geneticpoints < Thepower.genomecost) + to_chat(M.current, "We cannot evolve this... yet. We must acquire more DNA.") + return + + geneticpoints -= Thepower.genomecost + + purchased_powers += Thepower + + if(Thepower.genomecost > 0) + purchased_powers_history.Add("[Pname] ([Thepower.genomecost] points)") + + if(Thepower.make_hud_button && Thepower.isVerb) + if(!M.current.ability_master) + M.current.ability_master = new /obj/screen/movable/ability_master(null, M.current) + M.current.ability_master.add_ling_ability( + object_given = M.current, + verb_given = Thepower.verbpath, + name_given = Thepower.name, + ability_icon_given = Thepower.ability_icon_state, + arguments = list() + ) + + if(!Thepower.isVerb && Thepower.verbpath) + call(M.current, Thepower.verbpath)() + else if(remake_verbs) + M.current.make_changeling() diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 312daa2f227d5..f728d006f7adb 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -1,9 +1,11 @@ + var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") /datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) var/list/datum/absorbed_dna/absorbed_dna = list() - var/list/absorbed_languages = list() + var/list/absorbed_languages = list() // Necessary because of set_species stuff var/absorbedcount = 0 + var/lingabsorbedcount = 1 //Starts at one, because that's us var/chem_charges = 20 var/chem_recharge_rate = 0.5 var/chem_storage = 50 @@ -11,17 +13,25 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E var/changelingID = "Changeling" var/geneticdamage = 0 var/isabsorbing = 0 - var/geneticpoints = 25 - var/purchasedpowers = list() + var/geneticpoints = 15 + var/max_geneticpoints = 15 + var/list/purchased_powers = list() + var/list/purchased_organs = list() + var/already_regenerating = FALSE var/mimicing = "" - -/datum/changeling/Destroy() - purchasedpowers = null - absorbed_languages.Cut() - absorbed_dna.Cut() - . = ..() - -/datum/changeling/New() + var/cloaked = 0 + var/absorbing_lethally = ABSORB_NONLETHAL + var/selected_ranged_sting = null; + var/tendons_reinforced = FALSE; + var/list/toxin_victims = list() + var/armor_deployed = 0 //This is only used for changeling_generic_equip_all_slots() at the moment. + var/recursive_enhancement = 0 //Used to power up other abilities from the ling power with the same name. + var/list/purchased_powers_history = list() //Used for round-end report, includes respec uses too. + var/last_shriek = null // world.time when the ling last used a shriek. + var/next_escape = 0 // world.time when the ling can next use Escape Restraints + var/thermal_sight = FALSE // Is our Vision Augmented? With thermals? + var/last_human_form = null +/datum/changeling/New(gender=FEMALE) ..() if(length(possible_changeling_IDs)) changelingID = pick(possible_changeling_IDs) @@ -53,50 +63,83 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite changeling.absorbed_dna += newDNA - +//ling creation, add unique organ here //Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human /mob/proc/make_changeling() if(!mind) return if(!mind.changeling) mind.changeling = new /datum/changeling(gender) - verbs += /datum/changeling/proc/EvolutionMenu - add_language(LANGUAGE_CHANGELING_GLOBAL) + verbs.Add(/datum/changeling/proc/EvolutionTree) + verbs.Add(/mob/proc/toggle_absorb_type) + add_language("Changeling") var/lesser_form = !ishuman(src) - - if(!length(powerinstances)) + var/has_core = FALSE + if(!length(GLOB.powerinstances)) for(var/P in powers) - powerinstances += new P() + GLOB.powerinstances += new P() // Code to auto-purchase free powers. - for(var/datum/power/changeling/P in powerinstances) + for(var/datum/power/changeling/P in GLOB.powerinstances) if(!P.genomecost) // Is it free? - if(!(P in mind.changeling.purchasedpowers)) // Do we not have it already? - mind.changeling.purchasePower(mind, P.name, 0)// Purchase it. Don't remake our verbs, we're doing it after this. + if(!(P in mind.changeling.purchased_powers)) // Do we not have it already? + mind.changeling.purchased_powers += P /// Add it. + mind.changeling.purchasePower(mind, P, 0)// Purchase it. Don't remake our verbs, we're doing it after this. - for(var/datum/power/changeling/P in mind.changeling.purchasedpowers) + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) if(P.isVerb) if(lesser_form && !P.allowduringlesserform) continue if(!(P in src.verbs)) - src.verbs += P.verbpath + verbs.Add(P.verbpath) + if(P.make_hud_button) + if(!src.ability_master) + src.ability_master = new /obj/screen/movable/ability_master(null, src) + src.ability_master.add_ling_ability( + object_given = src, + verb_given = P.verbpath, + name_given = P.name, + ability_icon_given = P.ability_icon_state, + arguments = list() + ) for(var/language in languages) mind.changeling.absorbed_languages |= language + + var/mob/living/carbon/human/H = src if(istype(H)) - var/datum/absorbed_dna/newDNA = new(H.real_name, H.dna, H.species.name, H.languages) + var/saved_dna = H.dna.Clone() /// Prevent transform from breaking. + var/datum/absorbed_dna/newDNA = new(H.real_name, saved_dna, H.species.name, H.languages, H.gender, H.pronouns, H.flavor_texts, H.icon_render_keys, H.descriptors) absorbDNA(newDNA) - + for(var/obj/item/organ/internal/augment/lingcore/C in H.internal_organs) + has_core++ + if(has_core == 0 && istype(src,/mob/living/carbon/human)) + var/obj/item/organ/external/chest = H.get_organ(BP_CHEST) + var/obj/item/organ/internal/augment/core = new /obj/item/organ/internal/augment/lingcore + core.forceMove(src) + core.replaced(src, chest) + core = null + var/obj/item/organ/external/parent = H.get_organ(BP_CHEST) + var/obj/item/organ/internal/brain/mbrain = H.internal_organs_by_name[BP_BRAIN] + mbrain.parent_organ = BP_CHEST + mbrain.forceMove(parent) + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + for(var/obj/item/organ/internal/I in head.internal_organs) + if(istype(I,/obj/item/organ/internal/brain)) + head.internal_organs.Remove(I) return 1 //removes our changeling verbs /mob/proc/remove_changeling_powers() if(!mind || !mind.changeling) return - for(var/datum/power/changeling/P in mind.changeling.purchasedpowers) + for(var/datum/power/changeling/P in mind.changeling.purchased_powers) if(P.isVerb) - verbs -= P.verbpath + verbs.Remove(P.verbpath) + var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_PROC_REF(P.verbpath) + if(C) + ability_master.remove_ability(C) //Helper proc. Does all the checks and stuff for us to avoid copypasta @@ -111,694 +154,116 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E return if(src.stat > max_stat) - to_chat(src, SPAN_WARNING("We are incapacitated.")) + to_chat(src, "We are incapacitated.") return if(length(changeling.absorbed_dna) < required_dna) - to_chat(src, SPAN_WARNING("We require at least [required_dna] samples of compatible DNA.")) + to_chat(src, "We require at least [required_dna] samples of compatible DNA.") return if(changeling.chem_charges < required_chems) - to_chat(src, SPAN_WARNING("We require at least [required_chems] units of chemicals to do that!")) + to_chat(src, "We require at least [required_chems] units of chemicals to do that!") return if(changeling.geneticdamage > max_genetic_damage) - to_chat(src, SPAN_WARNING("Our genomes are still reassembling. We need time to recover first.")) + to_chat(src, "Our genomes are still reassembling. We need time to recover first.") return return changeling - //Used to dump the languages from the changeling datum into the actual mob. /mob/proc/changeling_update_languages(updated_languages) - languages = list() for(var/language in updated_languages) languages += language //This isn't strictly necessary but just to be safe... - add_language(LANGUAGE_CHANGELING_GLOBAL) - - return - -//Absorbs the victim's DNA making them uncloneable. Requires a strong grip on the victim. -//Doesn't cost anything as it's the most basic ability. -/mob/proc/changeling_absorb_dna() - set category = "Changeling" - set name = "Absorb DNA" - - var/datum/changeling/changeling = changeling_power(0,0,100) - if(!changeling) return - - var/obj/item/grab/G = src.get_active_hand() - if(!istype(G)) - to_chat(src, SPAN_WARNING("We must be grabbing a creature in our active hand to absorb them.")) - return - - var/mob/living/carbon/human/T = G.affecting - if(!istype(T)) - to_chat(src, SPAN_WARNING("[T] is not compatible with our biology.")) - return - - if((T.species.species_flags & SPECIES_FLAG_NO_SCAN) || T.isSynthetic()) - to_chat(src, SPAN_WARNING("We cannot extract DNA from this creature!")) - return - - if(MUTATION_HUSK in T.mutations) - to_chat(src, SPAN_WARNING("This creature's DNA is ruined beyond useability!")) - return - - if(!G.can_absorb()) - to_chat(src, SPAN_WARNING("We must have a tighter grip to absorb this creature.")) - return - - if(changeling.isabsorbing) - to_chat(src, SPAN_WARNING("We are already absorbing!")) - return - - var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting) - if(!affecting) - to_chat(src, SPAN_WARNING("They are missing that body part!")) - - changeling.isabsorbing = 1 - for(var/stage = 1, stage<=3, stage++) - switch(stage) - if(1) - to_chat(src, SPAN_NOTICE("This creature is compatible. We must hold still...")) - if(2) - to_chat(src, SPAN_NOTICE("We extend a proboscis.")) - src.visible_message(SPAN_WARNING("[src] extends a proboscis!")) - if(3) - to_chat(src, SPAN_NOTICE("We stab [T] with the proboscis.")) - src.visible_message(SPAN_DANGER("[src] stabs [T] with the proboscis!")) - to_chat(T, SPAN_DANGER("You feel a sharp stabbing pain!")) - affecting.take_external_damage(39, 0, DAMAGE_FLAG_SHARP, "large organic needle") - if(!do_after(src, 15 SECONDS, T, DO_PUBLIC_UNIQUE)) - to_chat(src, SPAN_WARNING("Our absorption of [T] has been interrupted!")) - changeling.isabsorbing = 0 - return - - to_chat(src, SPAN_NOTICE("We have absorbed [T]!")) - src.visible_message(SPAN_DANGER("[src] sucks the fluids from [T]!")) - to_chat(T, SPAN_DANGER("You have been absorbed by the changeling!")) - changeling.chem_charges += 10 - changeling.geneticpoints += 2 - - //Steal all of their languages! - for(var/language in T.languages) - if(!(language in changeling.absorbed_languages)) - changeling.absorbed_languages += language - - changeling_update_languages(changeling.absorbed_languages) - - var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.pronouns) - absorbDNA(newDNA) - - if(mind && T.mind) - T.mind.CopyMemories(mind) - - if(T.mind && T.mind.changeling) - if(T.mind.changeling.absorbed_dna) - for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot - if(changeling.GetDNA(dna_data.name)) - continue - absorbDNA(dna_data) - changeling.absorbedcount++ - T.mind.changeling.absorbed_dna.Cut(2) - - if(T.mind.changeling.purchasedpowers) - for(var/datum/power/changeling/Tp in T.mind.changeling.purchasedpowers) - if(Tp in changeling.purchasedpowers) - continue - else - changeling.purchasedpowers += Tp - - if(!Tp.isVerb) - call(Tp.verbpath)() - else - src.make_changeling() - - changeling.chem_charges += T.mind.changeling.chem_charges - changeling.geneticpoints += T.mind.changeling.geneticpoints - T.mind.changeling.chem_charges = 0 - T.mind.changeling.geneticpoints = 0 - T.mind.changeling.absorbedcount = 0 - - changeling.absorbedcount++ - changeling.isabsorbing = 0 - - T.death(0) - T.ChangeToHusk() - return 1 - - -//Change our DNA to that of somebody we've absorbed. -/mob/proc/changeling_transform() - set category = "Changeling" - set name = "Transform (5)" - - var/datum/changeling/changeling = changeling_power(5,1,0) - if(!changeling) return - - var/list/names = list() - for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) - names += "[DNA.name]" - - var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names - if(!S) return - - var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) - if(!chosen_dna) - return - - changeling.chem_charges -= 5 - changeling.geneticdamage = 30 - - var/S_name = chosen_dna.speciesName - var/datum/species/S_dat = all_species[S_name] - var/changeTime = 2 SECONDS - if(mob_size != S_dat.mob_size) - src.visible_message(SPAN_WARNING("[src]'s body begins to twist, their mass changing rapidly!")) - changeTime = 8 SECONDS - else - src.visible_message(SPAN_WARNING("[src]'s body begins to twist, changing rapidly!")) - - if(!do_after(src, changeTime, do_flags = DO_DEFAULT | DO_USER_UNIQUE_ACT)) - to_chat(src, SPAN_NOTICE("You fail to change shape.")) - return - handle_changeling_transform(chosen_dna) - - src.verbs -= /mob/proc/changeling_transform - spawn(10) - src.verbs += /mob/proc/changeling_transform - - changeling_update_languages(changeling.absorbed_languages) - return 1 - -/mob/proc/handle_changeling_transform(datum/absorbed_dna/chosen_dna) - src.visible_message(SPAN_WARNING("[src] transforms!")) - - src.dna = chosen_dna.dna - src.real_name = chosen_dna.name - src.flavor_text = "" - src.pronouns = chosen_dna.pronouns - - if(ishuman(src)) - var/mob/living/carbon/human/H = src - var/newSpecies = chosen_dna.speciesName - H.set_species(newSpecies,1) - H.b_type = chosen_dna.dna.b_type - H.sync_organ_dna() - - domutcheck(src, null) - src.UpdateAppearance() - - -//Transform into a monkey. -/mob/proc/changeling_lesser_form() - set category = "Changeling" - set name = "Lesser Form (1)" - - var/datum/changeling/changeling = changeling_power(1,0,0) - if(!changeling) return - - if(src.has_brain_worms()) - to_chat(src, SPAN_WARNING("We cannot perform this ability at the present time!")) - return - - var/mob/living/carbon/human/H = src - - if(!istype(H) || !H.species.primitive_form) - to_chat(src, SPAN_WARNING("We cannot perform this ability in this form!")) - return - - changeling.chem_charges-- - H.visible_message(SPAN_WARNING("[H] transforms!")) - changeling.geneticdamage = 30 - to_chat(H, SPAN_WARNING("Our genes cry out!")) - H = H.monkeyize() - return 1 - -//Transform into a human -/mob/proc/changeling_lesser_transform() - set category = "Changeling" - set name = "Transform (1)" - - var/datum/changeling/changeling = changeling_power(1,1,0) - if(!changeling) return - - if(HAS_TRANSFORMATION_MOVEMENT_HANDLER(src)) - return - - var/list/names = list() - for(var/datum/dna/DNA in changeling.absorbed_dna) - names += "[DNA.real_name]" - - var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names - if(!S) return - - var/datum/dna/chosen_dna = changeling.GetDNA(S) - if(!chosen_dna) - return - - var/mob/living/carbon/human/C = src - - changeling.chem_charges-- - C.remove_changeling_powers() - C.visible_message(SPAN_WARNING("[C] transforms!")) - C.dna = chosen_dna.Clone() - - var/list/implants = list() - for (var/obj/item/implant/I in C) //Still preserving implants - implants += I - - ADD_TRANSFORMATION_MOVEMENT_HANDLER(C) - C.icon = null - C.ClearOverlays() - C.set_invisibility(INVISIBILITY_ABSTRACT) - var/atom/movable/fake_overlay/animation = new /atom/movable/fake_overlay(src) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - flick("monkey2h", animation) - sleep(48) - qdel(animation) - - for(var/obj/item/W in src) - C.drop_from_inventory(W) - - var/mob/living/carbon/human/O = new /mob/living/carbon/human( src ) - if (C.dna.GetUIState(DNA_UI_GENDER)) - O.gender = FEMALE - else - O.gender = MALE - O.dna = C.dna.Clone() - C.dna = null - O.real_name = chosen_dna.real_name - - for(var/obj/T in C) - qdel(T) - - O.dropInto(C.loc) - - O.UpdateAppearance() - domutcheck(O, null) - O.setToxLoss(C.getToxLoss()) - O.adjustBruteLoss(C.getBruteLoss()) - O.setOxyLoss(C.getOxyLoss()) - O.adjustFireLoss(C.getFireLoss()) - O.set_stat(C.stat) - for (var/obj/item/implant/I in implants) - I.forceMove(O) - I.implanted = O - - C.mind.transfer_to(O) - O.make_changeling() - O.changeling_update_languages(changeling.absorbed_languages) - qdel(C) - return 1 - - -//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. -/mob/proc/changeling_fakedeath() - set category = "Changeling" - set name = "Regenerative Stasis (20)" - - var/datum/changeling/changeling = changeling_power(20,1,100,DEAD) - if(!changeling) return - - var/mob/living/carbon/C = src - if(!C.stat && alert("Are we sure we wish to fake our death?",,"Yes","No") == "No")//Confirmation for living changelings if they want to fake their death - return - to_chat(C, SPAN_NOTICE("We will attempt to regenerate our form.")) - C.status_flags |= FAKEDEATH //play dead - C.UpdateLyingBuckledAndVerbStatus() - C.remove_changeling_powers() - - C.emote("gasp") - - spawn(rand(800,2000)) - if(changeling_power(20,1,100,DEAD)) - // charge the changeling chemical cost for stasis - changeling.chem_charges -= 20 - - to_chat(C, SPAN_NOTICE(FONT_GIANT("We are ready to rise. Use the Revive verb when you are ready."))) - C.verbs += /mob/proc/changeling_revive - return 1 - -/mob/proc/changeling_revive() - set category = "Changeling" - set name = "Revive" - - var/mob/living/carbon/C = src - // restore us to health - C.revive() - // remove our fake death flag - C.status_flags &= ~(FAKEDEATH) - // let us move again - C.UpdateLyingBuckledAndVerbStatus() - // re-add out changeling powers - C.make_changeling() - // sending display messages - to_chat(C, SPAN_NOTICE("We have regenerated.")) - C.verbs -= /mob/proc/changeling_revive - - -//Boosts the range of your next sting attack by 1 -/mob/proc/changeling_boost_range() - set category = "Changeling" - set name = "Ranged Sting (10)" - set desc="Your next sting ability can be used against targets 2 squares away." - - var/datum/changeling/changeling = changeling_power(10,0,100) - if(!changeling) return 0 - changeling.chem_charges -= 10 - to_chat(src, SPAN_NOTICE("Your throat adjusts to launch the sting.")) - changeling.sting_range = 2 - src.verbs -= /mob/proc/changeling_boost_range - spawn(5) src.verbs += /mob/proc/changeling_boost_range - return 1 - - -//Recover from stuns. -/mob/proc/changeling_unstun() - set category = "Changeling" - set name = "Epinephrine Sacs (45)" - set desc = "Removes all stuns" - - var/datum/changeling/changeling = changeling_power(45,0,100,UNCONSCIOUS) - if(!changeling) return 0 - changeling.chem_charges -= 45 - - var/mob/living/carbon/human/C = src - C.set_stat(CONSCIOUS) - C.SetParalysis(0) - C.SetStunned(0) - C.SetWeakened(0) - C.lying = 0 - C.UpdateLyingBuckledAndVerbStatus() - - src.verbs -= /mob/proc/changeling_unstun - spawn(5) src.verbs += /mob/proc/changeling_unstun - return 1 + add_language("Changeling") - -//Speeds up chemical regeneration -/mob/proc/changeling_fastchemical() - src.mind.changeling.chem_recharge_rate *= 2 - return 1 - -//Increases macimum chemical storage -/mob/proc/changeling_engorgedglands() - src.mind.changeling.chem_storage += 25 - return 1 - - -//Prevents AIs tracking you but makes you easily detectable to the human-eye. -/mob/proc/changeling_digitalcamo() - set category = "Changeling" - set name = "Toggle Digital Camoflague" - set desc = "The AI can no longer track us, but we will look different if examined. Has a constant cost while active." - - var/datum/changeling/changeling = changeling_power() - if(!changeling) return 0 - - var/mob/living/carbon/human/C = src - if(C.digitalcamo) to_chat(C, SPAN_NOTICE("We return to normal.")) - else to_chat(C, SPAN_NOTICE("We distort our form to prevent AI-tracking.")) - C.digitalcamo = !C.digitalcamo - - spawn(0) - while(C && C.digitalcamo && C.mind && C.mind.changeling) - C.mind.changeling.chem_charges = max(C.mind.changeling.chem_charges - 1, 0) - sleep(40) - - src.verbs -= /mob/proc/changeling_digitalcamo - spawn(5) src.verbs += /mob/proc/changeling_digitalcamo - return 1 - - -//Starts healing you every second for 10 seconds. Can be used whilst unconscious. -/mob/proc/changeling_rapidregen() - set category = "Changeling" - set name = "Rapid Regeneration (30)" - set desc = "Begins rapidly regenerating. Does not effect stuns or chemicals." - - var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS) - if(!changeling) return 0 - src.mind.changeling.chem_charges -= 30 - - var/mob/living/carbon/human/C = src - spawn(0) - for(var/i = 0, i<10,i++) - if(C) - C.adjustBruteLoss(-10) - C.adjustToxLoss(-10) - C.adjustOxyLoss(-10) - C.adjustFireLoss(-10) - sleep(10) - - src.verbs -= /mob/proc/changeling_rapidregen - spawn(5) src.verbs += /mob/proc/changeling_rapidregen - return 1 - -// HIVE MIND UPLOAD/DOWNLOAD DNA - -var/global/list/datum/absorbed_dna/hivemind_bank = list() - -/mob/proc/changeling_hiveupload() - set category = "Changeling" - set name = "Hive Channel (10)" - set desc = "Allows you to channel DNA in the airwaves to allow other changelings to absorb it." - - var/datum/changeling/changeling = changeling_power(10,1) - if(!changeling) return - - var/list/names = list() - for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) - var/valid = 1 - for(var/datum/absorbed_dna/DNB in hivemind_bank) - if(DNA.name == DNB.name) - valid = 0 - break - if(valid) - names += DNA.name - - if(length(names) <= 0) - to_chat(src, SPAN_NOTICE("The airwaves already have all of our DNA.")) - return - - var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names - if(!S) return - - var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) - if(!chosen_dna) - return - - var/datum/species/spec = all_species[chosen_dna.speciesName] - - if(spec && spec.species_flags & SPECIES_FLAG_NEED_DIRECT_ABSORB) - to_chat(src, SPAN_NOTICE("That species must be absorbed directly.")) - return - - changeling.chem_charges -= 10 - hivemind_bank += chosen_dna - to_chat(src, SPAN_NOTICE("We channel the DNA of [S] to the air.")) - return 1 - -/mob/proc/changeling_hivedownload() - set category = "Changeling" - set name = "Hive Absorb (20)" - set desc = "Allows you to absorb DNA that is being channeled in the airwaves." - - var/datum/changeling/changeling = changeling_power(20,1) - if(!changeling) return - - var/list/names = list() - for(var/datum/absorbed_dna/DNA in hivemind_bank) - if(!(changeling.GetDNA(DNA.name))) - names[DNA.name] = DNA - - if(length(names) <= 0) - to_chat(src, SPAN_NOTICE("There's no new DNA to absorb from the air.")) - return - - var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names - if(!S) return - var/datum/dna/chosen_dna = names[S] - if(!chosen_dna) - return - - changeling.chem_charges -= 20 - absorbDNA(chosen_dna) - to_chat(src, SPAN_NOTICE("We absorb the DNA of [S] from the air.")) - return 1 - -// Fake Voice - -/mob/proc/changeling_mimicvoice() - set category = "Changeling" - set name = "Mimic Voice" - set desc = "Shape our vocal glands to form a voice of someone we choose. We cannot regenerate chemicals when mimicing." - - - var/datum/changeling/changeling = changeling_power() - if(!changeling) return - - if(changeling.mimicing) - changeling.mimicing = "" - to_chat(src, SPAN_NOTICE("We return our vocal glands to their original location.")) - return - - var/mimic_voice = sanitize(input(usr, "Enter a name to mimic.", "Mimic Voice", null), MAX_NAME_LEN) - if(!mimic_voice) - return - - changeling.mimicing = mimic_voice - - to_chat(src, SPAN_NOTICE("We shape our glands to take the voice of [mimic_voice], this will stop us from regenerating chemicals while active.")) - to_chat(src, SPAN_NOTICE("Use this power again to return to our original voice and reproduce chemicals again.")) - - spawn(0) - while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing) - src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0) - sleep(40) - if(src && src.mind && src.mind.changeling) - src.mind.changeling.mimicing = "" ////////// //STINGS// //They get a pretty header because there's just so fucking many of them ;_; ////////// +/turf/proc/AdjacentTurfsRangedSting() + //Yes this is snowflakey, but I couldn't get it to work any other way.. -Luke + var/list/allowed = list( + /obj/structure/table, + /obj/structure/closet, + /obj/structure/cable, + /obj/structure/disposalpipe, + /obj/machinery, + /mob + ) + + var/L[] = new() + for(var/turf/simulated/t in oview(src,1)) + var/add = 1 + if(t.density) + add = 0 + if(add && LinkBlocked(src,t)) + add = 0 + if(add && TurfBlockedNonWindow(t)) + add = 0 + for(var/obj/O in t) + if(O.density) + add = 0 + break + if(istype(O, /obj/machinery/door)) + //not sure why this doesn't fire on LinkBlocked() + add = 0 + break + for(var/type in allowed) + if (istype(O, type)) + add = 1 + break + if(!add) + break + if(add) + L.Add(t) + return L + + /mob/proc/sting_can_reach(mob/M as mob, sting_range = 1) if(M.loc == src.loc) return 1 //target and source are in the same thing if(!isturf(src.loc) || !isturf(M.loc)) - to_chat(src, SPAN_WARNING("We cannot reach \the [M] with a sting!")) + to_chat(src, "We cannot reach \the [M] with a sting!") return 0 //One is inside, the other is outside something. // Maximum queued turfs set to 25; I don't *think* anything raises sting_range above 2, but if it does the 25 may need raising - if(!AStar(src.loc, M.loc, TYPE_PROC_REF(/turf, AdjacentTurfs), TYPE_PROC_REF(/turf, Distance), max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail - to_chat(src, SPAN_WARNING("We cannot find a path to sting \the [M] by!")) + if(!AStar(src.loc, M.loc, /turf/proc/AdjacentTurfsRangedSting, /turf/proc/Distance, max_nodes=25, max_node_depth=sting_range)) //If we can't find a path, fail + to_chat(src, "We cannot find a path to sting \the [M] by!") return 0 return 1 //Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities) -/mob/proc/changeling_sting(required_chems=0, verb_path, loud, sting_name = "unnamed sting") +/mob/proc/changeling_sting(required_chems=0, verb_path) var/datum/changeling/changeling = changeling_power(required_chems) + var/mob/living/carbon/human/ling = src if(!changeling) return var/list/victims = list() - for(var/mob/living/carbon/human/C in oview(changeling.sting_range)) + for(var/mob/living/carbon/C in oview(changeling.sting_range)) victims += C - var/mob/living/carbon/human/T = input(src, "Who will we sting?") as null|anything in victims + var/mob/living/carbon/T = input(src, "Who will we sting?") as null|anything in victims - if(!T) return + if(!T) + return + if(T.isSynthetic()) + to_chat(src, "We are unable to pierce the outer shell of [T].") + return if(!(T in view(changeling.sting_range))) return if(!sting_can_reach(T, changeling.sting_range)) return if(!changeling_power(required_chems)) return - var/obj/item/organ/external/target_limb = T.get_organ(src.zone_sel.selecting) - if (!target_limb) - to_chat(src, SPAN_WARNING("[T] is missing that limb.")) + + if(!(T.can_inject(ling,ling.zone_sel.selecting, FALSE) == CAN_INJECT)) + to_chat(src,SPAN_WARNING("We are unable to pierce the thick material covering [T].")) return changeling.chem_charges -= required_chems changeling.sting_range = 1 src.verbs -= verb_path spawn(10) src.verbs += verb_path - if(!loud) - to_chat(src, SPAN_NOTICE("We stealthily sting [T].")) - else - visible_message(SPAN_DANGER("[src] fires an organic shard into [T]!")) - admin_attack_log(src, T, "Stung their victim using [sting_name]", "Was stung using [sting_name]", "stung using [sting_name]") - - for(var/obj/item/clothing/clothes in list(T.head, T.wear_mask, T.wear_suit, T.w_uniform, T.gloves, T.shoes)) - if(istype(clothes) && (clothes.body_parts_covered & target_limb.body_part) && (clothes.item_flags & ITEM_FLAG_THICKMATERIAL)) - to_chat(src, SPAN_WARNING("[T]'s armor has protected them.")) - return //thick clothes will protect from the sting - if(T.isSynthetic() || BP_IS_ROBOTIC(target_limb)) return - if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting - to_chat(T, SPAN_WARNING("You feel a tiny prick.")) + to_chat(src, "We stealthily sting [T].") + if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting + to_chat(T, "You feel a tiny prick.") return - - -/mob/proc/changeling_lsdsting() - set category = "Changeling" - set name = "Hallucination Sting (15)" - set desc = "Causes terror in the target." - - var/mob/living/carbon/human/T = changeling_sting(15, TYPE_PROC_REF(/mob, changeling_lsdsting), sting_name = "Hallucination Sting") - if(!T) return 0 - spawn(rand(300,600)) - if(T) T.hallucination(400, 80) - return 1 - -/mob/proc/changeling_silence_sting() - set category = "Changeling" - set name = "Silence sting (10)" - set desc="Sting target" - - var/mob/living/carbon/human/T = changeling_sting(10, TYPE_PROC_REF(/mob, changeling_silence_sting), sting_name = "Silence Sting") - if(!T) return 0 - T.silent += 30 - return 1 - -/mob/proc/changeling_blind_sting() - set category = "Changeling" - set name = "Blind sting (20)" - set desc="Sting target" - - var/mob/living/carbon/human/T = changeling_sting(20, TYPE_PROC_REF(/mob, changeling_blind_sting), sting_name = "Blind Sting") - if(!T) return 0 - to_chat(T, SPAN_DANGER("Your eyes burn horrificly!")) - T.disabilities |= NEARSIGHTED - spawn(300) T.disabilities &= ~NEARSIGHTED - T.eye_blind = 10 - T.eye_blurry = 20 - return 1 - -/mob/proc/changeling_deaf_sting() - set category = "Changeling" - set name = "Deaf sting (5)" - set desc="Sting target:" - - var/mob/living/carbon/human/T = changeling_sting(5, TYPE_PROC_REF(/mob, changeling_deaf_sting), sting_name = "Deaf Sting") - if(!T) return 0 - to_chat(T, SPAN_DANGER("Your ears pop and begin ringing loudly!")) - T.ear_deaf += 15 - return 1 - -/mob/proc/changeling_DEATHsting() - set category = "Changeling" - set name = "Death Sting (40)" - set desc = "Causes spasms onto death." - var/loud = 1 - - var/mob/living/carbon/human/T = changeling_sting(40, TYPE_PROC_REF(/mob, changeling_DEATHsting), loud, sting_name = "Death Sting") - if(!T) return 0 - to_chat(T, SPAN_DANGER("You feel a small prick and your chest becomes tight.")) - T.make_jittery(400) - if(T.reagents) T.reagents.add_reagent(/datum/reagent/lexorin, 10) - return 1 - -/mob/proc/changeling_extract_dna_sting() - set category = "Changeling" - set name = "Extract DNA Sting (40)" - set desc="Stealthily sting a target to extract their DNA." - - var/datum/changeling/changeling = null - if(src.mind && src.mind.changeling) - changeling = src.mind.changeling - if(!changeling) - return 0 - - var/mob/living/carbon/human/T = changeling_sting(40, TYPE_PROC_REF(/mob, changeling_extract_dna_sting), sting_name = "Extract DNA Sting") - if(!T) return 0 - if((MUTATION_HUSK in T.mutations) || (T.species.species_flags & SPECIES_FLAG_NO_SCAN)) - to_chat(src, SPAN_WARNING("We cannot extract DNA from this creature!")) - return 0 - - if(T.species.species_flags & SPECIES_FLAG_NEED_DIRECT_ABSORB) - to_chat(src, SPAN_NOTICE("That species must be absorbed directly.")) - return - - var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages) - absorbDNA(newDNA) - return 1 diff --git a/code/game/gamemodes/changeling/generic_equip_procs.dm b/code/game/gamemodes/changeling/generic_equip_procs.dm new file mode 100644 index 0000000000000..803c4ea0d4a11 --- /dev/null +++ b/code/game/gamemodes/changeling/generic_equip_procs.dm @@ -0,0 +1,295 @@ +///This is a generic proc that should be called by other ling armor procs to equip them. +/mob/proc/changeling_generic_armor(armor_type, helmet_type, boot_type, chem_cost) + + if(!ishuman(src)) + return FALSE + var/list/species_restricted = list("exclude", SPECIES_NABBER, SPECIES_ADHERENT,SPECIES_VOX) + var/mob/living/carbon/human/M = src + + if(istype(M.wear_suit, armor_type) || istype(M.head, helmet_type) || istype(M.shoes, boot_type)) + chem_cost = 0 + + var/datum/changeling/changeling = changeling_power(chem_cost, 1, 100, CONSCIOUS) + + if(!changeling) + return + if(M.species in species_restricted) + to_chat(M,SPAN_WARNING("Our current form is not suited to such a transformation.")) + return + + //First, check if we're already wearing the armor, and if so, take it off. + if(istype(M.wear_suit, armor_type) || istype(M.head, helmet_type) || istype(M.shoes, boot_type)) + M.visible_message("[M] casts off their [M.wear_suit.name]!", + "We cast off our [M.wear_suit.name]", + "You hear the organic matter ripping and tearing!") + if(istype(M.wear_suit, armor_type)) + qdel(M.wear_suit) + if(istype(M.head, helmet_type)) + qdel(M.head) + if(istype(M.shoes, boot_type)) + qdel(M.shoes) + M.update_inv_wear_suit() + M.update_inv_head() + M.update_hair() + M.update_inv_shoes() + return TRUE + + if(M.head || M.wear_suit) //Make sure our slots aren't full + to_chat(src, "We require nothing to be on our head, and we cannot wear any external suits, or shoes.") + return FALSE + + var/obj/item/clothing/suit/A = new armor_type(src) + src.equip_to_slot_or_del(A, slot_wear_suit) + + var/obj/item/clothing/suit/H = new helmet_type(src) + src.equip_to_slot_or_del(H, slot_head) + + var/obj/item/clothing/shoes/B = new boot_type(src) + src.equip_to_slot_or_del(B, slot_shoes) + + src.mind.changeling.chem_charges -= chem_cost + if(istype(M.wear_suit,/obj/item/clothing/suit/space/changeling/armored)) + playsound(src, 'sound/effects/ling_horror.ogg', 30, 1) + src.visible_message(SPAN_DANGER("\the [src] lets out out a monstrous roar of fury!")) + else + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + M.update_inv_wear_suit() + M.update_inv_head() + M.update_hair() + M.update_inv_shoes() + M.mind.changeling.armor_deployed = TRUE + return TRUE + +/mob/proc/changeling_generic_equip_all_slots(list/stuff_to_equip, cost) + var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) + if(!changeling) + return + + if(!ishuman(src)) + return FALSE + + var/mob/living/carbon/human/M = src + + var/success = FALSE + + //First, check if we're already wearing the armor, and if so, take it off. + + if(M.mind.changeling.armor_deployed) + if(M.head && stuff_to_equip["head"]) + if(istype(M.head, stuff_to_equip["head"])) + qdel(M.head) + success = TRUE + + if(M.wear_id && stuff_to_equip["wear_id"]) + if(istype(M.wear_id, stuff_to_equip["wear_id"])) + qdel(M.wear_id) + success = TRUE + + if(M.wear_suit && stuff_to_equip["wear_suit"]) + if(istype(M.wear_suit, stuff_to_equip["wear_suit"])) + qdel(M.wear_suit) + success = TRUE + + if(M.gloves && stuff_to_equip["gloves"]) + if(istype(M.gloves, stuff_to_equip["gloves"])) + qdel(M.gloves) + success = TRUE + if(M.shoes && stuff_to_equip["shoes"]) + if(istype(M.shoes, stuff_to_equip["shoes"])) + qdel(M.shoes) + success = TRUE + + if(M.belt && stuff_to_equip["belt"]) + if(istype(M.belt, stuff_to_equip["belt"])) + qdel(M.belt) + success = TRUE + + if(M.glasses && stuff_to_equip["glasses"]) + if(istype(M.glasses, stuff_to_equip["glasses"])) + qdel(M.glasses) + success = TRUE + + if(M.wear_mask && stuff_to_equip["wear_mask"]) + if(istype(M.wear_mask, stuff_to_equip["wear_mask"])) + qdel(M.wear_mask) + success = TRUE + + if(M.back && stuff_to_equip["back"]) + if(istype(M.back, stuff_to_equip["back"])) + for(var/atom/movable/AM in M.back.contents) //Dump whatever's in the bag before deleting. + AM.forceMove(src.loc) + qdel(M.back) + success = TRUE + + if(M.w_uniform && stuff_to_equip["w_uniform"]) + if(istype(M.w_uniform, stuff_to_equip["w_uniform"])) + qdel(M.w_uniform) + success = TRUE + + if(success) + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[src] pulls on their clothes, peeling it off along with parts of their skin attached!", + "We remove and deform our equipment.") + M.mind.changeling.armor_deployed = 0 + src.update_icon() + return success + + else + + to_chat(M, "We begin growing our new equipment...") + + var/list/grown_items_list = list() + + var/t = stuff_to_equip["head"] + if(!M.head && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_head) + grown_items_list.Add("a helmet") + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["w_uniform"] + if(!M.w_uniform && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_w_uniform) + grown_items_list.Add("a uniform") + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["gloves"] + if(!M.gloves && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_gloves) + grown_items_list.Add("some gloves") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["shoes"] + if(!M.shoes && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_shoes) + grown_items_list.Add("shoes") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["belt"] + if(!M.belt && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_belt) + grown_items_list.Add("a belt") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["glasses"] + if(!M.glasses && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_glasses) + grown_items_list.Add("some glasses") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["wear_mask"] + if(!M.wear_mask && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_wear_mask) + grown_items_list.Add("a mask") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["back"] + if(!M.back && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_back) + grown_items_list.Add("a backpack") + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["wear_suit"] + if(!M.wear_suit && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_wear_suit) + grown_items_list.Add("an exosuit") + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + t = stuff_to_equip["wear_id"] + if(!M.wear_id && t) + var/I = new t + M.equip_to_slot_or_del(I, slot_wear_id) + grown_items_list.Add("an ID card") + playsound(src, 'sound/effects/splat.ogg', 30, 1) + success = TRUE + sleep(1 SECOND) + + var/feedback = english_list(grown_items_list, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + + to_chat(M, "We have grown [feedback].") + + if(success) + M.mind.changeling.armor_deployed = 1 + M.mind.changeling.chem_charges -= 10 + src.update_icon() + return success + +///This is a generic proc that should be called by other ling weapon procs to equip them. +/mob/proc/changeling_generic_weapon(weapon_type, make_sound = TRUE, cost) + + var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) + if(!changeling) + return + + if(!ishuman(src)) + return FALSE + if (!src || src.incapacitated() || src.lying) + to_chat(src,SPAN_WARNING("We cannot do this while we are stunned.")) + return FALSE + var/mob/living/carbon/human/M = src + if(M.l_hand && M.r_hand) //Make sure our hands aren't full. + to_chat(src, SPAN_WARNING("We do not have any free hands we can shape into a weapon!")) + return FALSE + + + var/obj/item/weapon/W = new weapon_type(src) + if(!M.put_in_hands(W)) + to_chat(src, SPAN_WARNING("We cannot shape a missing limb into a weapon!")) + qdel(W) + return FALSE + + src.mind.changeling.chem_charges -= cost + if(make_sound) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + return TRUE + +///Special proc for the ranged sting to pass information about the type of sting +/mob/proc/changeling_equip_ranged(weapon_type, make_sound = FALSE, cost = 10, sting_type) + + var/datum/changeling/changeling = changeling_power(cost,1,100,CONSCIOUS) + if(!changeling) + return + + if(!ishuman(src)) + return FALSE + + var/mob/living/carbon/human/M = src + + if(M.l_hand && M.r_hand ) //Make sure our hands aren't full. + to_chat(src, SPAN_WARNING("We do not have a free hand to shape into a weapon. Drop something first.")) + return FALSE + + var/obj/item/gun/projectile/changeling/W = new weapon_type(src) + src.put_in_hands(W) + W.selected_sting = sting_type + src.mind.changeling.selected_ranged_sting = sting_type + src.mind.changeling.chem_charges -= cost + if(make_sound) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + return TRUE diff --git a/code/game/gamemodes/changeling/modularchangling.dm b/code/game/gamemodes/changeling/modularchangling.dm index d4bd05f7e9350..6d1fb2485e378 100644 --- a/code/game/gamemodes/changeling/modularchangling.dm +++ b/code/game/gamemodes/changeling/modularchangling.dm @@ -461,7 +461,7 @@ var/global/list/datum/power/changeling/powerinstances = list() if(isnull(Thepower)) - to_chat(M.current, "This is awkward. Changeling power purchase failed, please report this bug to a coder!") + CRASH("This is awkward. Changeling power purchase failed, please report this bug to a coder!") return if(Thepower in purchasedpowers) diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm new file mode 100644 index 0000000000000..abebe16b7ffb4 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -0,0 +1,135 @@ +/datum/power/changeling/absorb_dna + name = "Absorb DNA" + desc = "Permits us to syphon the DNA from a human. They become one with us, and we become stronger if they were of our kind." + ability_icon_state = "ling_absorb_dna" + genomecost = 0 + power_category = CHANGELING_POWER_INHERENT + verbpath = /mob/living/proc/changeling_absorb_dna + +/** + * Absorbs the victim's DNA. Requires a strong grip on the victim. + * Doesn't cost anything as it's the most basic ability. + */ +/mob/proc/toggle_absorb_type() + set category = "Changeling" + set name = "Toggle Absorb Type" + set desc = "Allows us to determine whether we wish to kill targets we absorb" + if(src.mind.changeling) + if(src.mind.changeling.absorbing_lethally == ABSORB_NONLETHAL) + to_chat(src,SPAN_INFO("We will now kill creatures whose DNA we sample.")) + src.mind.changeling.absorbing_lethally = ABSORB_LETHAL + return + if(src.mind.changeling.absorbing_lethally == ABSORB_LETHAL) + to_chat(src,SPAN_INFO("We will no longer kill creatures whose DNA we sample.")) + src.mind.changeling.absorbing_lethally = ABSORB_NONLETHAL + return +/mob/living/proc/changeling_absorb_dna(mob/living/carbon/M) + set category = "Changeling" + set name = "Absorb DNA" + var/list/names = list() + + var/datum/changeling/changeling = changeling_power(0,0,100) + if(!changeling) return + + var/obj/item/grab/G = src.get_active_hand() + if(!istype(G)) + to_chat(src, "We must be grabbing a creature in our active hand to absorb them.") + return + + var/mob/living/carbon/human/T = G.affecting + for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) + names += "[DNA.name]" + if(T.real_name in names) + to_chat(src,SPAN_NOTICE("We have already sampled the DNA of this creature. There is nothing further to learn here.")) + return + if(T == src) + to_chat(src,SPAN_WARNING("We would have to be very silly to try absorbing ourselves.")) + return + if(!istype(T) || T.isSynthetic() || istype(T.species,/datum/species/vox)) + to_chat(src, "\The [T] is not compatible with our biology.") + return + + if(MUTATION_HUSK in T.mutations) //Lings can always absorb other lings, unless someone beat them to it first. + if(!T.mind.changeling || T.mind.changeling && T.mind.changeling.geneticpoints < 0) + to_chat(src, "This creature's DNA is ruined beyond useability!") + return + + if(!G.force_danger()) + to_chat(src, "We must have a tighter grip to absorb this creature.") + return + + if(changeling.isabsorbing) + to_chat(src, "We are already absorbing!") + return + + changeling.isabsorbing = 1 + for(var/stage = 1, stage<=3, stage++) + switch(stage) + if(1) + to_chat(src, "This creature is compatible. We must hold still...") + if(2) + to_chat(src, "We extend a proboscis.") + src.visible_message("[src] extends a proboscis!") + if(3) + to_chat(src, "We stab [T] with the proboscis.") + src.visible_message("[src] stabs [T] with the proboscis!") + to_chat(T, "You feel a sharp stabbing pain!") + admin_attack_log(src,T,"Absorbed (changeling)") + var/obj/item/organ/external/affecting = T.get_organ(src.zone_sel.selecting) + T.apply_damage(50, DAMAGE_BRUTE, affecting , damage_flags = DAMAGE_FLAG_SHARP, used_weapon="organic needle", armor_pen=50) + + + if(!do_mob(src, T, 150, progress= TRUE) || !G.force_danger()) + to_chat(src, "Our absorption of the DNA of \the [T] has been interrupted!") + changeling.isabsorbing = 0 + return + + to_chat(src, "We have absorbed the DNA of \the [T]!") + + + changeling.chem_charges += 10 + + + //add pronouns to this line + var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.gender, T.pronouns, T.flavor_texts, T.icon_render_keys, T.descriptors) + absorbDNA(newDNA) + + if(T.mind && T.mind.changeling) + if(T.mind.changeling.absorbed_dna) + for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot + if(dna_data in changeling.absorbed_dna) + continue + absorbDNA(dna_data) + changeling.absorbedcount++ + LIST_RESIZE(T.mind.changeling.absorbed_dna, 1) + + // This is where lings get boosts from eating eachother + if(T.mind.changeling.lingabsorbedcount) + for(var/a = 1 to T.mind.changeling.lingabsorbedcount) + changeling.lingabsorbedcount++ + changeling.geneticpoints += 6 + changeling.max_geneticpoints += 6 + + to_chat(src, SPAN_NOTICE("We have feasted upon a fellow changeling, and grown stronger.")) + + T.mind.changeling.chem_charges = 0 + T.mind.changeling.geneticpoints = -1 + T.mind.changeling.max_geneticpoints = -1 //To prevent revival. + T.mind.changeling.absorbedcount = 0 + T.mind.changeling.lingabsorbedcount = 0 + T.death(0) + T.Drain() + return TRUE + changeling.absorbedcount++ + changeling.isabsorbing = 0 + to_chat(T, SPAN_DANGER("You feel everything go black, as \the [src] releases you.")) + if(changeling.absorbing_lethally == ABSORB_LETHAL) + T.death(0) + T.Drain() + changeling.geneticpoints += 1 + changeling.max_geneticpoints += 1 + if(changeling.absorbing_lethally == ABSORB_NONLETHAL) + changeling.geneticpoints += 2 + changeling.max_geneticpoints += 2 + T.Sleeping(200) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm new file mode 100644 index 0000000000000..4f4b1c8a1a382 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -0,0 +1,329 @@ + +/datum/power/changeling/arm_hammer + name = "Arm Hammer" + desc = "We reform one of our arms into a weighty hammer capable of smashing quickly through obstacles or victims alike. We can slam enemies aside with it on disarm intent, sending them flying." + helptext = "We may retract our armhammer by using the ability again with the hammer in our active hand." + ability_icon_state = "ling_hammer" + enhancedtext = "The hammer will have further armor peneratration." + genomecost = 2 + power_category = CHANGELING_POWER_WEAPONS + verbpath = /mob/proc/changeling_arm_hammer +/datum/power/changeling/arm_blade + name = "Arm Blade" + desc = "We reform one of our arms into a deadly blade capable of slicing through armor. It can pry open airlocks, breaking them in the process." + helptext = "We may retract our armblade by using the ability again with the blade in our active hand." + enhancedtext = "The blade will have greater armor peneratration and parry more effectively." + ability_icon_state = "ling_blade" + genomecost = 2 + power_category = CHANGELING_POWER_WEAPONS + verbpath = /mob/proc/changeling_arm_blade + +//HAMMERTIME +/mob/proc/changeling_arm_hammer() + set category = "Changeling" + set name = "Arm Hammer (15)" + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/melee/changeling/arm_hammer)) + to_chat(src,SPAN_WARNING("We shrink our arm back to its normal size.")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + if(changeling_generic_weapon(/obj/item/melee/changeling/arm_hammer/greater, cost = 15)) + //to_chat(src, "We prepare an extra sharp blade.") + return 1 + else + if(changeling_generic_weapon(/obj/item/melee/changeling/arm_hammer, cost = 15)) + return 1 + return 0 + +//Grows a scary, and powerful arm blade. +/mob/proc/changeling_arm_blade() + set category = "Changeling" + set name = "Arm Blade (20)" + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/melee/changeling/arm_blade)) + to_chat(src,SPAN_WARNING("We retract our blade back into our body.")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + if(changeling_generic_weapon(/obj/item/melee/changeling/arm_blade/greater, cost = 20)) + //to_chat(src, "We prepare an extra sharp blade.") + return 1 + + else + if(changeling_generic_weapon(/obj/item/melee/changeling/arm_blade, cost = 20)) + return 1 + return 0 + +//Claws +/datum/power/changeling/claw + name = "Claw" + desc = "We reform one of our arms into a deadly claw. We can instantly aggressively grab enemies with it on grab intent." + helptext = "We may retract our claw by using the ability again with the claw in our active hand." + enhancedtext = "The claw will have armor peneratration." + ability_icon_state = "ling_claw" + genomecost = 1 + power_category = CHANGELING_POWER_WEAPONS + verbpath = /mob/proc/changeling_claw + +//Grows a scary, and powerful claw. +/mob/proc/changeling_claw() + set category = "Changeling" + set name = "Claw (15)" + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/melee/changeling/claw)) + to_chat(src,SPAN_WARNING("We reform our claw back into an ordinary appendage.")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + if(changeling_generic_weapon(/obj/item/melee/changeling/claw/greater, 1, cost = 15)) + to_chat(src, SPAN_NOTICE("We prepare an extra sharp claw.")) + return 1 + + else + if(changeling_generic_weapon(/obj/item/melee/changeling/claw, 1, cost = 15)) + return 1 + return 0 + +/obj/item/melee/changeling + name = "arm weapon" + desc = "A grotesque weapon made out of bone and flesh that cleaves through people as a hot knife through butter." + icon = 'icons/obj/weapons/melee_physical.dmi' + icon_state = "ling_blade" + item_icons = list( + icon_l_hand = 'icons/mob/onmob/items/lefthand.dmi', + icon_r_hand = 'icons/mob/onmob/items/righthand.dmi', + ) + item_state = "arm_blade" + w_class = ITEM_SIZE_HUGE + force = 5 + hitsound = 'sound/weapons/bladeslice.ogg' + anchored = TRUE + throwforce = 0 //Just to be on the safe side + throw_range = 0 + throw_speed = 0 + var/mob/living/creator //This is just like ninja swords, needed to make sure dumb shit that removes the sword doesn't make it stay around. + var/weapType = "weapon" + var/weapLocation = "arm" + canremove = FALSE + base_parry_chance = 40 // The base chance for the weapon to parry. + +/obj/item/melee/changeling/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + if(ismob(loc)) + src.creator = loc + + +/obj/item/melee/changeling/Destroy() + STOP_PROCESSING(SSobj, src) + creator = null + return ..() +/obj/item/melee/changeling/Process() + var/mob/living/carbon/human/H = creator + if ( H.handcuffed || (H.stat != CONSCIOUS)) + qdel(src) + if(!istype(loc,/mob)) + src.visible_message(SPAN_DANGER("\The [src] rapidly decays and melts into a puddle of slime!")) + new /obj/decal/cleanable/ling_vomit(src.loc) + qdel(src) + ..() +/obj/item/melee/changeling/arm_hammer + name = "arm hammer" + desc = "A hammer made out of flesh and bone, heavy enough to smash through armor and people alike." + icon_state = "ling_hammer" + item_state = "ling_hammer" + force = 25 + hitsound = 'sound/weapons/genhit3.ogg' + armor_penetration = 30 + sharp = FALSE + edge = FALSE + attack_verb = list("attacked", "struck", "smashed", "clubbed", "beaten", "hit", "battered", "smacked") + base_parry_chance = 50 + var/last_slam = null + var/cooldown = 7 SECONDS +/obj/item/melee/changeling/arm_hammer/greater + name = "arm greathammer" + desc = "A massive hammer made out of flesh and bone, heavy enough to smash through armor and people alike." + armor_penetration = 50 + +/obj/item/melee/changeling/arm_hammer/use_before(atom/target,mob/user) + if (user.a_intent == I_DISARM) + if ((last_slam + cooldown > world.time)) + to_chat(user, SPAN_WARNING("We are still recovering from our last slam attack.")) + return + if(istype(target,/mob/living/carbon)) + var/mob/living/carbon/M = target + user.do_attack_animation(M) + var/target_zone = check_zone(user.zone_sel.selecting) + M.apply_damage(force, DAMAGE_BRUTE, target_zone) + user.visible_message(SPAN_DANGER("\the [user] slams their arm hammer into \the [M] in a furious blow, sending them flying!")) + M.throw_at(get_edge_target_turf(user, user.dir), 5, 5) + playsound(src, 'sound/weapons/punch1.ogg', 30, 1) + last_slam = world.time + + if(istype(target,/turf/simulated/wall) || istype(target,/obj/structure) && (user.a_intent != I_HELP)) + target.damage_health(rand(25, 75), DAMAGE_BRUTE) + +/obj/item/melee/changeling/arm_blade + name = "arm blade" + desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." + icon_state = "ling_blade" + item_state = "arm_blade" + force = 35 + armor_penetration = 15 + sharp = TRUE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "ripped", "diced", "cut") + base_parry_chance = 60 + +/obj/item/melee/changeling/arm_blade/use_before(atom/target,mob/user) + if (istype(target, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = target + if(A.locked) + to_chat(user,SPAN_WARNING("We cannot force open an airlock held in place by bolts.")) + return + A.visible_message(SPAN_DANGER("\The [user] forces \the [src] between \the [A], causing the metal to creak!")) + playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1) + if (do_after(user, 5 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS) && !A.locked) + A.welded = FALSE + A.update_icon() + playsound(A, 'sound/effects/meteorimpact.ogg', 100, 1) + A.visible_message(SPAN_DANGER("\The [user] tears \the [A] open with \a [src]!")) + addtimer(new Callback(A, /obj/machinery/door/airlock/.proc/open, TRUE), 0) + A.open() + A.set_broken(TRUE) + + if (istype(target, /obj/machinery/door/firedoor)) + var/obj/machinery/door/firedoor/A = target + playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1) + if (do_after(user, 2 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS)) + A.visible_message(SPAN_DANGER("\The [user] pries \the [A] wide open effortlessly!")) + A.open(TRUE) + if (istype(target, /obj/machinery/door/blast)) + var/obj/machinery/door/blast/A = target + if(!istype(user.get_inactive_hand(),/obj/item/melee/changeling/arm_blade)) + to_chat(user,SPAN_WARNING("We require an armblade in both arms to be able to exert enough force to pry a blast door open.")) + return + A.visible_message(SPAN_DANGER("\The [user] forces both armblades between \the [A], prying with incredible strength!")) + playsound(A, 'sound/machines/airlock_creaking.ogg', 100, 1) + if (do_after(user, 20 SECONDS, A, DO_DEFAULT | DO_USER_UNIQUE_ACT | DO_PUBLIC_PROGRESS) ) + A.update_icon() + playsound(A, 'sound/effects/meteorimpact.ogg', 100, 1) + A.visible_message(SPAN_DANGER("\The [user] tears \the [A] wide open!")) + A.force_open() + +/obj/item/melee/changeling/arm_blade/greater + name = "arm greatblade" + desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." + armor_penetration = 30 + base_parry_chance = 70 + +/obj/item/melee/changeling/claw + name = "hand claw" + item_state = "ling_claw" + desc = "A grotesque claw made out of bone and flesh that cleaves through people as a hot knife through butter." + icon_state = "ling_claw" + force = 20 + sharp = FALSE + edge = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + base_parry_chance = 50 + var/last_grab = null + var/cooldown = 5 SECONDS +/obj/item/melee/changeling/claw/use_before(mob/living/M,mob/user) + + if(user.a_intent == I_GRAB) + if ((last_grab + cooldown > world.time)) + to_chat(user, SPAN_WARNING("We are still recovering from our last grab attack.")) + return + if(istype(M,/mob/living/carbon) && (user.mind.changeling.chem_charges > 10)) + var/mob/living/carbon/human/H = user + if(!user.get_inactive_hand()) + user.swap_hand() + H.species.attempt_grab(H,M) + var/obj/item/grab/holding = H.get_active_hand() + if(istype(holding,/obj/item/grab)) + holding.upgrade(bypass_cooldown = TRUE) + last_grab = world.time + +/obj/item/melee/changeling/claw/greater + name = "hand greatclaw" + force = 25 + armor_penetration = 20 + base_parry_chance = 60 + +/datum/power/changeling/arm_shield + name = "Arm Shield" + desc = "We reform one of our arms into a resilient shield to protect ourselves from harm. The shield can deflect projectiles, but not reliably." + helptext = "We may retract our shield by using the ability again with the shield in hand." + enhancedtext = "The shield will be more resistant to damage." + ability_icon_state = "ling_shield" + genomecost = 2 + power_category = CHANGELING_POWER_WEAPONS + verbpath = /mob/proc/changeling_arm_shield + +/mob/proc/changeling_arm_shield() + set category = "Changeling" + set name = "Arm Shield (20)" + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/shield/riot/changeling)) + to_chat(src,SPAN_WARNING("We retract our claw back into our body.")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + if(changeling_generic_weapon(/obj/item/shield/riot/changeling/greater)) + //to_chat(src, "We prepare an extra sharp blade.") + return 1 + + else + if(changeling_generic_weapon(/obj/item/shield/riot/changeling)) + return 1 + return 0 + +/obj/item/shield/riot/changeling + name = "chitin shield" + item_state = "ling_shield" + icon_state = "ling_shield" + desc = "A monstrously thick and bulky mess of fleshy chitin, covered in shards of bone." + throwforce = 0 + throw_speed = 0 + throw_range = 0 + anchored = TRUE + var/mob/living/creator //This is just like ninja swords, needed to make sure dumb shit that removes the sword doesn't make it stay around. + var/weapType = "weapon" + var/weapLocation = "arm" + canremove = FALSE +/obj/item/shield/riot/changeling/greater + name = "chitin greatshield" + max_block = 50 + can_block_lasers = TRUE + +/obj/item/shield/riot/changeling/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + if(ismob(loc)) + src.creator = loc + +/obj/item/shield/riot/changeling/Destroy() + STOP_PROCESSING(SSobj, src) + creator = null + return ..() + +/obj/item/shield/riot/changeling/Process() + var/mob/living/carbon/human/H = creator + if ( H.handcuffed || (H.stat != CONSCIOUS)) + qdel(src) + if(!istype(loc,/mob)) + src.visible_message(SPAN_DANGER("\The [src] rapidly decays and melts into a puddle of slime!")) + new /obj/decal/cleanable/ling_vomit(src.loc) + qdel(src) + ..() diff --git a/code/game/gamemodes/changeling/powers/armor.dm b/code/game/gamemodes/changeling/powers/armor.dm new file mode 100644 index 0000000000000..eeb9429c550f1 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/armor.dm @@ -0,0 +1,196 @@ +/datum/power/changeling/space_suit + name = "Organic Space Suit" + desc = "We grow an organic suit to protect ourselves from space exposure." + helptext = "To remove the suit, use the ability again." + ability_icon_state = "ling_space_suit" + genomecost = 1 + verbpath = /mob/proc/changeling_spacesuit + +/mob/proc/changeling_spacesuit() + set category = "Changeling" + set name = "Organic Space Suit (20)" + if(changeling_generic_armor(/obj/item/clothing/suit/space/changeling,/obj/item/clothing/head/helmet/space/changeling,/obj/item/clothing/shoes/magboots/changeling, 20)) + return TRUE + return FALSE + +/datum/power/changeling/armor + name = "Chitinous Spacearmor" + desc = "We turn our skin into tough chitin to protect us from damage and space exposure." + helptext = "To remove the armor, use the ability again." + ability_icon_state = "ling_armor" + genomecost = 3 + verbpath = /mob/proc/changeling_spacearmor + +/mob/proc/changeling_spacearmor() + set category = "Changeling" + set name = "Chitinous Spacearmor (20)" + + if(changeling_generic_armor(/obj/item/clothing/suit/space/changeling/armored,/obj/item/clothing/head/helmet/space/changeling/armored,/obj/item/clothing/shoes/magboots/changeling/armored, 20)) + + return TRUE + return FALSE + +//Space suit + +/obj/item/clothing/suit/space/changeling + name = "organic suit" + icon_state = "lingspacesuit" + desc = "A thin, skintight mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel." + allowed = list(/obj/item/device/flashlight, /obj/item/tank/oxygen_emergency, /obj/item/tank/oxygen_emergency_extended) + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) //No armor at all. + canremove = FALSE + can_breach = FALSE + flags_inv = BLOCKHAIR | HIDETAIL + var/remove_on_respec = TRUE + +/obj/item/clothing/suit/space/changeling/Initialize() + . = ..() + slowdown_per_slot[slot_wear_suit] = 0 + START_PROCESSING(SSobj, src) + if(ismob(loc)) + to_chat(src,SPAN_WARNING("[loc.name]\'s flesh splits and twists, forming into a thin fleshy membrane around their body!")) + +/obj/item/clothing/suit/space/changeling/dropped(mob/user) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + +/obj/item/clothing/suit/space/changeling/Destroy() + + STOP_PROCESSING(SSobj, src) + + return ..() + +/obj/item/clothing/head/helmet/space/changeling + name = "flesh mass" + icon_state = "lingspacehelmet" + desc = "A covering of pressure and temperature-resistant organic tissue with a translucent, glass-like front made of organic tissue." + flags_inv = HIDEEARS|BLOCKHEADHAIR //Again, no THICKMATERIAL. + body_parts_covered = HEAD|FACE|EYES + var/remove_on_respec = TRUE + canremove = 0 + flags_inv = BLOCKHAIR | HIDETAIL | HIDEFACE + + +/obj/item/clothing/head/helmet/space/changeling/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/shoes/magboots/changeling + desc = "A suction cupped mass of flesh, shaped like a foot." + name = "fleshy grippers" + icon_state = "lingspacesuit" + action_button_name = "Toggle Grippers" + canremove = 0 + online_slowdown = 3 + var/remove_on_respec = TRUE + +/obj/item/clothing/shoes/magboots/changeling/set_slowdown() + slowdown_per_slot[slot_shoes] = shoes? max(0, shoes.slowdown_per_slot[slot_shoes]): 0 //So you can't put on magboots to make you walk faster. + if (magpulse) + slowdown_per_slot[slot_shoes] += online_slowdown + +/obj/item/clothing/shoes/magboots/changeling/attack_self(mob/user) + if(magpulse) + item_flags &= ~ITEM_FLAG_NOSLIP + magpulse = 0 + set_slowdown() + force = 3 + to_chat(user, "We release our grip on the floor.") + else + item_flags |= ITEM_FLAG_NOSLIP + magpulse = 1 + set_slowdown() + force = 5 + to_chat(user, "We cling to the terrain below us.") + + +/obj/item/clothing/shoes/magboots/changeling/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +//Armor + +/obj/item/clothing/suit/space/changeling/armored + name = "chitinous mass" + desc = "A protective shell of muscle, bone and chitin, designed for violence and violence alone." + icon_state = "lingarmor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + //It costs 3 points, so it should be very protective. + //armor = list(melee = 75, bullet = 65, laser = 60, energy = 60, bomb = 60, bio = 0, rad = 0) + armor = list( + melee = ARMOR_MELEE_VERY_HIGH, + bullet = ARMOR_BALLISTIC_RESISTANT, + laser = ARMOR_LASER_HANDGUNS, + energy = ARMOR_ENERGY_RESISTANT, + bomb = ARMOR_BOMB_PADDED, + ) + siemens_coefficient = 0.3 + can_breach = FALSE + flags_inv = BLOCKHAIR | HIDETAIL + //max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE + slowdown_general = 0 + +/obj/item/clothing/suit/space/changeling/armored/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + slowdown_per_slot[slot_wear_suit] = 0 + if(ismob(loc)) + to_chat(src,SPAN_WARNING("Our muscles twist and our bones crack with a crunching noise as we form claws, teeth and armor!")) + + /* + loc.visible_message("[loc.name]\'s flesh turns black, quickly transforming into a hard, chitinous mass!", + "We harden our flesh, creating a suit of armor!", + "You hear organic matter ripping and tearing!") + */ +/obj/item/clothing/suit/space/changeling/armored/Destroy() + + + STOP_PROCESSING(SSobj, src) + + return ..() + + +/obj/item/clothing/head/helmet/space/changeling/armored + name = "chitinous mass" + desc = "An enormous eyeless maw of teeth that hungers." + icon_state = "lingarmorhelmet" + armor = list( + melee = ARMOR_MELEE_VERY_HIGH, + bullet = ARMOR_BALLISTIC_RESISTANT, + laser = ARMOR_LASER_HANDGUNS, + energy = ARMOR_ENERGY_RESISTANT, + bomb = ARMOR_BOMB_PADDED, + ) + siemens_coefficient = 0.3 + max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE + flags_inv = BLOCKHAIR | HIDETAIL | HIDEFACE + +/obj/item/clothing/shoes/magboots/changeling/armored + desc = "A tough, hard mass of chitin, with long talons for digging into terrain." + name = "chitinous talons" + icon_state = "lingchameleon" + action_button_name = "Toggle Talons" + +/obj/item/clothing/gloves/combat/changeling //Combined insulated/fireproof gloves + name = "chitinous gauntlets" + desc = "Very resilient gauntlets made out of black chitin. It looks very durable, and can probably resist electrical shock in addition to the elements." + icon_state = "lingarmorgloves" + armor = list(melee = 75, bullet = 60, laser = 60,energy = 60, bomb = 60, bio = 0, rad = 0) //No idea if glove armor gets checked + siemens_coefficient = 0 + +/obj/item/clothing/shoes/boots/combat/changeling //Noslips + name = "chitinous boots" + desc = "Footwear made out of a hard, black chitinous material. The bottoms of these appear to have spikes that can protrude or extract itself into and out \ + of the floor at will, granting the wearer stability." + icon_state = "lingchameleon" + armor = list( + melee = ARMOR_MELEE_VERY_HIGH, + bullet = ARMOR_BALLISTIC_RESISTANT, + laser = ARMOR_LASER_HANDGUNS, + energy = ARMOR_ENERGY_RESISTANT, + bomb = ARMOR_BOMB_PADDED, + ) + siemens_coefficient = 0.3 + cold_protection = FEET + min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE + heat_protection = FEET + max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm new file mode 100644 index 0000000000000..bc6c03d541dee --- /dev/null +++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm @@ -0,0 +1,42 @@ +//Augmented Eyesight: Gives you thermal vision. Also, higher DNA cost because of how powerful it is. +//needs to be fixed to act per tick, polaris port didn't work in that respect + +/datum/power/changeling/augmented_eyesight + name = "Augmented Eyesight" + desc = "Allows for extra sensory information in the eyes, increasing photon reception capabilities for a short span of time." + helptext = "Grants us temporary thermal vision, allowing us to track organics beyond walls." + ability_icon_state = "ling_augmented_eyesight" + genomecost = 2 + verbpath = /mob/proc/changeling_augmented_eyesight + +/mob/proc/changeling_augmented_eyesight() + set category = "Changeling" + set name = "Augmented Eyesight (3 per tick)" + set desc = "We evolve our eyes to sense the infrared temporarily." + + var/datum/changeling/changeling = changeling_power(3,0,100,CONSCIOUS) + var/mob/living/carbon/human/H = src + var/obj/item/organ/external/parent = H.get_organ(BP_HEAD) + var/has_organ = FALSE; + for(var/obj/item/organ/internal/augment/ling_lenses/eyes in H.internal_organs) + has_organ++ + if(!changeling) + return FALSE + if(!has_organ) + var/obj/item/organ/internal/augment/eyes = new /obj/item/organ/internal/augment/ling_lenses + eyes.forceMove(src) + eyes.replaced(src, parent) + eyes = null + has_organ++ + if(has_organ) + if(H.seedarkness) + H.seedarkness = FALSE + for(var/obj/item/organ/internal/augment/ling_lenses/eyes in H.internal_organs) + if(!eyes.is_active) + to_chat(src, SPAN_NOTICE("We feel a minute twitch in our eyes, and a hidden layer to the world is revealed.")) + eyes.is_active = TRUE + else + to_chat(src,SPAN_NOTICE("Our lenses retract, causing us to lose our augmented vision.")) + eyes.is_active = FALSE + changeling.chem_charges -= 5 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm new file mode 100644 index 0000000000000..d145130362eec --- /dev/null +++ b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm @@ -0,0 +1,219 @@ +/datum/power/changeling/bioelectrogenesis + name = "Bioelectrogenesis" + desc = "We reconfigure a large number of cells in our body to generate an electric charge. \ + On demand, we can attempt to recharge anything in our active hand, or we can touch someone with an electrified hand, shocking them." + helptext = "We can shock someone by grabbing them and using this ability, or using the ability with an empty hand and touching them. \ + Shocking someone costs ten chemicals per use." + enhancedtext = "Shocking biologicals without grabbing only requires five chemicals, and has more disabling power." + ability_icon_state = "ling_bioelectrogenesis" + genomecost = 2 + verbpath = /mob/living/carbon/human/proc/changeling_bioelectrogenesis + +//Recharge whatever's in our hand, or shock people. +/mob/living/carbon/human/proc/changeling_bioelectrogenesis() + set category = "Changeling" + set name = "Bioelectrogenesis (15 + 10/shock)" + set desc = "Recharges anything in your hand, or shocks people." + var/obj/held_item = get_active_hand() + if (istype(held_item, /obj/item/weapon/electric_hand)) + qdel(held_item) + return 0 + var/datum/changeling/changeling = changeling_power(20,0,100,CONSCIOUS) + + + var/last_shock = null + var/cooldown = 3 SECONDS + if(!changeling) + return FALSE + + if(!held_item) + if(src.mind.changeling.recursive_enhancement) + if(changeling_generic_weapon(/obj/item/weapon/electric_hand/efficent,0)) + to_chat(src, "We will shock others more efficently.") + return TRUE + else + if(changeling_generic_weapon(/obj/item/weapon/electric_hand,0)) //Chemical cost is handled in the equip proc. + return TRUE + return FALSE + else + // Handle glove conductivity. + var/obj/item/clothing/gloves/gloves = src.gloves + var/siemens = 1 + if(gloves) + siemens = gloves.siemens_coefficient + if((last_shock + cooldown > world.time)) + to_chat(src,SPAN_DANGER("Our bioelectric hand is still recharging a voltage powerful enough to shock someone!")) + return FALSE + //If we're grabbing someone, electrocute them. + var/obj/item/grab/G = src.get_active_hand() + if(istype(G)) + if(G.affecting) + G.affecting.electrocute_act(10 * siemens, src, 1.0, BP_CHEST, 0) + var/agony = 80 * siemens //Does more than if hit with an electric hand, since grabbing is slower. + G.affecting.stun_effect_act(0, agony, BP_CHEST, src) + + admin_attack_log(src,G.affecting,"Changeling shocked") + + if(siemens) + visible_message("Arcs of electricity strike [G.affecting]!", + "Our hand channels raw electricity into [G.affecting].", + "You hear sparks!") + last_shock = world.time + else + to_chat(src, "Our gloves block us from shocking \the [G.affecting].") + src.mind.changeling.chem_charges -= 10 + return 1 + + //Otherwise, charge up whatever's in their hand. + else + //This checks both the active hand, and the contents of the active hand's held item. + var/success = 0 + var/list/L = new() //We make a new list to avoid copypasta. + + //Check our hand. + if(istype(held_item,/obj/item/cell)) + L.Add(held_item) + + //Now check our hand's item's contents, so we can recharge guns and other stuff. + for(var/obj/item/cell/cell in held_item.contents) + L.Add(cell) + + //Now for the actual recharging. + for(var/obj/item/cell/cell in L) + visible_message("Some sparks fall out from \the [src.name]\'s [held_item]!", + "Our hand channels raw electricity into \the [held_item].", + "You hear sparks!") + var/i = 10 + if(siemens) + while(i) + cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. + if(cell.charge > cell.maxcharge) + cell.charge = cell.maxcharge + break + if(siemens) + var/T = get_turf(src) + new /obj/sparks(T) + held_item.update_icon() + i-- + sleep(1 SECOND) + success = 1 + if(success == 0) //If we couldn't do anything with the ability, don't deduct the chemicals. + to_chat(src, "We are unable to affect \the [held_item].") + else + src.mind.changeling.chem_charges -= 10 + return success + +/obj/item/weapon/electric_hand + name = "electrified hand" + desc = "You could probably shock someone badly if you touched them, or recharge something." + icon = 'icons/obj/weapons/melee_energy.dmi' + icon_state = "electric_hand" + canremove = FALSE + var/shock_cost = 10 + var/agony_amount = 60 + var/electrocute_amount = 10 + var/last_shock = null + var/cooldown = 5 SECONDS +/obj/item/weapon/electric_hand/efficent + name = "supercharged hand" + desc = "Power! Unlimited Power!" + shock_cost = 5 + agony_amount = 80 + electrocute_amount = 20 + +/obj/item/weapon/electric_hand/Initialize() + . = ..() + if(ismob(loc)) + src.visible_message(SPAN_DANGER("[src]\'s hand crackles with electricity, sparks flying about!")) + var/T = get_turf(src) + new /obj/sparks(T) + +/obj/item/weapon/electric_hand/afterattack(atom/target, mob/living/carbon/human/user, proximity) + if(!target) + return + if(!proximity) + return + + // Handle glove conductivity. + var/obj/item/clothing/gloves/gloves = user.gloves + var/siemens = 1 + if(gloves) + siemens = gloves.siemens_coefficient + + //Excuse the copypasta. + if(istype(target,/mob/living/carbon)) + var/mob/living/carbon/C = target + + if(user.mind.changeling.chem_charges < shock_cost) + to_chat(user, SPAN_WARNING("We require at least 10 chemicals to electrocute [C]!")) + return FALSE + if((last_shock + cooldown > world.time)) + to_chat(user,SPAN_DANGER("Our bioelectric hand is still recharging a voltage powerful enough to shock someone!")) + return FALSE + C.electrocute_act(electrocute_amount * siemens,src,1.0,BP_CHEST) + C.stun_effect_act(0, agony_amount * siemens, BP_CHEST, src) + + admin_attack_log(user,C,"Shocked with [src]") + + if(siemens) + to_chat(user, SPAN_NOTICE("Our hand channels raw electricity into [C]")) + src.visible_message(SPAN_DANGER("Arcs of electricity strike [C]!")) + last_shock = world.time + //visible_message("Arcs of electricity strike [C]!", + //"Our hand channels raw electricity into [C]", + //"You hear sparks!") + else + to_chat(src, SPAN_WARNING("Our gloves block us from shocking \the [C].")) + //qdel(src) //Since we're no longer a one hit stun, we need to stick around. + user.mind.changeling.chem_charges -= shock_cost + return TRUE + + else if(istype(target,/mob/living/silicon)) + var/mob/living/silicon/S = target + + if(user.mind.changeling.chem_charges < 10) + to_chat(src, "We require at least 10 chemicals to electrocute [S]!") + return FALSE + + S.electrocute_act(60,src,0.75) //If only they had surge protectors. + if(siemens) + visible_message("Arcs of electricity strike [S]!", + "Our hand channels raw electricity into [S]", + "You hear sparks!") + to_chat(S, "Warning: Electrical surge detected!") + //qdel(src) + user.mind.changeling.chem_charges -= 10 + return TRUE + + else + if(istype(target,/obj)) + var/success = 0 + var/obj/T = target + //We can also recharge things we touch, such as APCs or hardsuits. + for(var/obj/item/cell/cell in T.contents) + src.visible_message(SPAN_WARNING("[src]\'s hand crackles, sparks darting from it to \the [cell]")) + /*visible_message("Some sparks fall out from \the [target]!", + "Our hand channels raw electricity into \the [target].", + "You hear sparks!")*/ + var/i = 10 + if(siemens) + while(i) + cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. + if(cell.charge > cell.maxcharge) + cell.charge = cell.maxcharge + break //No point making sparks if the cell's full. + // if(!Adjacent(T)) + // break + if(siemens) + var/Turf = get_turf(src) + new /obj/sparks(Turf) + T.update_icon() + i-- + sleep(1 SECOND) + success = 1 + break + if(success == 0) + to_chat(src, "We are unable to affect \the [target].") + else + qdel(src) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/blind_sting.dm b/code/game/gamemodes/changeling/powers/blind_sting.dm new file mode 100644 index 0000000000000..8e5f178db6b59 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/blind_sting.dm @@ -0,0 +1,33 @@ +/datum/power/changeling/blind_sting + name = "Blind Sting" + desc = "We silently sting a human, completely blinding them for a short time." + enhancedtext = "Duration is extended." + ability_icon_state = "ling_sting_blind" + genomecost = 2 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_blind_sting + sting_effect = /mob/proc/ling_blind + is_sting = TRUE +/mob/proc/apply_blind() + src.disabilities &= ~NEARSIGHTED +/mob/proc/ling_blind(mob/living/carbon/M, duration) + addtimer(new Callback(M,/mob/.proc/apply_blind),duration) + M.eye_blind = 10 + M.eye_blurry = 20 +/mob/proc/changeling_blind_sting() + set category = "Changeling" + set name = "Blind sting (20)" + set desc="Sting target" + + var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting) + if(!T) + return FALSE + admin_attack_log(src,T,"Blind sting (changeling)") + to_chat(T, "You are surrounded by darkness. You cannot see!") + T.disabilities |= NEARSIGHTED + var/duration = 300 + if(src.mind.changeling.recursive_enhancement) + duration = duration + 150 + to_chat(src, "They will be deprived of sight for longer.") + ling_blind(T,duration) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/boost_range.dm b/code/game/gamemodes/changeling/powers/boost_range.dm new file mode 100644 index 0000000000000..cae0ecaa359de --- /dev/null +++ b/code/game/gamemodes/changeling/powers/boost_range.dm @@ -0,0 +1,123 @@ +/datum/power/changeling/boost_range + name = "Boost Range" + desc = "We evolve the ability to fire biological darts from our hand to deliver our sting." + helptext = "Allows us to use the effects of our stings at a distance" + enhancedtext = "The range is extended to five tiles." + ability_icon_state = "ling_sting_boost_range" + genomecost = 1 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_boost_range + +//Boosts the range of your next sting attack by 1 + +/obj/item/ammo_casing/chemdart/changeling + name = "organic dart" + desc = "A needle thin dart formed of bone." + icon_state = "ling_dart" + icon = 'icons/obj/projectiles.dmi' + caliber = CALIBER_DART + projectile_type = /obj/item/projectile/bullet/chemdart/changeling + leaves_residue = FALSE +/obj/item/projectile/bullet/chemdart/changeling + name = "organic dart" + fire_sound = 'sound/weapons/Genhit.ogg' + icon_state = "ling_dart" + life_span = 3 + icon = 'icons/obj/projectiles.dmi' +/obj/item/projectile/bullet/chemdart/changeling/on_hit(atom/target, blocked = 0, def_zone) + if(blocked < 100 && isliving(target)) + var/mob/living/L = target + if (src.firer == target) + return + var/datum/power/changeling/sting = src.firer.mind.changeling.selected_ranged_sting + //use firer ling mind to set ling selected ranged sting + if(L.can_inject(null, def_zone) == CAN_INJECT) + call(sting.sting_effect)(target,sting.sting_duration) + + +/obj/item/gun/projectile/changeling + name = "thin mass" + desc = "A thin, narrow and hollow mass of flesh protruding from the palm. It twitches and pulses, eager to strike." + can_reload = FALSE + screen_shake = FALSE + space_recoil = FALSE + has_safety = FALSE + silenced = TRUE + auto_eject = FALSE + handle_casings = CLEAR_CASINGS + max_shells = 1 + starts_loaded = TRUE + has_safety = FALSE + silenced = TRUE + icon = 'icons/obj/guns/dartgun.dmi' + item_state = null + icon_state = "ling_dart" + fire_sound = 'sound/weapons/Genhit.ogg' + anchored = TRUE + throwforce = 0 //Just to be on the safe side + throw_range = 0 + throw_speed = 0 + screen_shake = FALSE + space_recoil = FALSE + var/mob/living/creator //This is just like ninja swords, needed to make sure dumb shit that removes the sword doesn't make it stay around. + var/weapType = "weapon" + var/weapLocation = "arm" + canremove = FALSE + jam_chance = 0 + ammo_type = /obj/item/ammo_casing/chemdart/changeling + var/list/available_stings = list() + var/selected_sting = null + +/obj/item/gun/projectile/changeling/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + if(ismob(loc)) + src.creator = loc + +/obj/item/gun/projectile/changeling/Destroy() + STOP_PROCESSING(SSobj, src) + creator = null + return ..() + +/obj/item/gun/projectile/changeling/Process() + var/mob/living/carbon/human/H = creator + + if ( H.handcuffed || (H.stat != CONSCIOUS) || (length(loaded) == 0)) + //playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + to_chat(creator,SPAN_WARNING("We retract our ranged stinger back into our body.")) + qdel(src) + +/mob/proc/changeling_boost_range() + set category = "Changeling" + set name = "Ranged Stinger (20)" + set desc="We transform one of our hands so that it can fire organic darts at targets." + + var/list/available_stings = list() + var/datum/changeling/changeling = changeling_power(10,0,100) + if(!changeling) + return TRUE + //changeling.chem_charges -= 10 + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/gun/projectile/changeling)) + to_chat(src,SPAN_WARNING("We retract our ranged stinger back into our body.")) + //playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + for (var/datum/power/changeling/power in src.mind.changeling.purchased_powers) + if (power.is_sting) + available_stings.Add(power) + if(length(available_stings) == 0) + to_chat(src,SPAN_WARNING("We do not have any stings that we can fire at range!")) + return FALSE + var/datum/power/changeling/sting= input(src,"Select a sting.", "Select") as null|anything in available_stings + if(sting) + var/sting_name = sting.name + to_chat(src,SPAN_WARNING("We prepare to fire a [sting_name]")) + if(src.mind.changeling.recursive_enhancement) + if(changeling_equip_ranged(/obj/item/gun/projectile/changeling, sting_type = sting)) + return 1 + + else + if(changeling_equip_ranged(/obj/item/gun/projectile/changeling, sting_type = sting)) + return 1 + return 0 diff --git a/code/game/gamemodes/changeling/powers/cryo_sting.dm b/code/game/gamemodes/changeling/powers/cryo_sting.dm new file mode 100644 index 0000000000000..c51ffdd7fb5a3 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/cryo_sting.dm @@ -0,0 +1,38 @@ +/datum/power/changeling/cryo_sting + name = "Cryogenic Sting" + desc = "We silently sting a biological with a cocktail of chemicals that freeze them." + helptext = "Does not provide a warning to the victim, though they will likely realize they are suddenly freezing. Has \ + a three minute cooldown between uses." + enhancedtext = "Increases the amount of chemicals injected." + ability_icon_state = "ling_sting_cryo" + genomecost = 1 + verbpath = /mob/proc/changeling_cryo_sting + sting_effect = /mob/proc/ling_cryo + is_sting = TRUE + +/mob/proc/cryo_cooldown() + to_chat(src, "Our cryogenic string is ready to be used once more.") + src.verbs |= /mob/proc/changeling_cryo_sting + +/mob/proc/ling_cryo(mob/living/carbon/M, duration) + M.reagents.add_reagent(/datum/reagent/toxin/cryotoxin, 10) + +/mob/proc/changeling_cryo_sting() + set category = "Changeling" + set name = "Cryogenic Sting (20)" + set desc = "Chills and freezes a biological creature." + + var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_cryo_sting) + if(!T) + return FALSE + admin_attack_log(src,T,"Cryo sting (changeling)") + var/inject_amount = 10 + if(src.mind.changeling.recursive_enhancement) + inject_amount = inject_amount * 1.5 + to_chat(src, "We inject extra chemicals.") + if(T.reagents) + T.reagents.add_reagent(/datum/reagent/toxin/cryotoxin, inject_amount) + src.verbs -= /mob/proc/changeling_cryo_sting + addtimer(new Callback(src,/mob/.proc/cryo_cooldown), 3 MINUTES) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/darkvision.dm b/code/game/gamemodes/changeling/powers/darkvision.dm new file mode 100644 index 0000000000000..011403d6c8361 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/darkvision.dm @@ -0,0 +1,30 @@ +/datum/power/changeling/darksight + name = "Dark Sight" + desc = "We change the composition of our eyes, banishing the shadows from our vision." + helptext = "We will be able to see in the dark." + ability_icon_state = "ling_augmented_eyesight" + genomecost = 0 + power_category = CHANGELING_POWER_INHERENT + verbpath = /mob/proc/changeling_darksight + +/mob/proc/changeling_darksight() + set category = "Changeling" + set name = "Toggle Darkvision" + set desc = "We are able see in the dark." + + var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + if(!changeling) + return 0 + + if(istype(src,/mob/living/carbon)) + var/mob/living/carbon/C = src + //disables thermals if we have it to prevent permanent double stacking + src.sight &= ~(SEE_MOBS) + if(C.seedarkness) + to_chat(C, SPAN_NOTICE("We no longer need light to see.")) + C.seedarkness = 0 + else + to_chat(C, SPAN_NOTICE("We allow the shadows to return.")) + C.seedarkness = 1 + + return 0 diff --git a/code/game/gamemodes/changeling/powers/deaf_sting.dm b/code/game/gamemodes/changeling/powers/deaf_sting.dm new file mode 100644 index 0000000000000..7b93982c4c769 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/deaf_sting.dm @@ -0,0 +1,29 @@ +/datum/power/changeling/deaf_sting + name = "Deaf Sting" + desc = "We silently sting a human, completely deafening them for a short time." + enhancedtext = "Deafness duration is extended." + ability_icon_state = "ling_sting_deafen" + genomecost = 1 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_deaf_sting + sting_effect = /mob/proc/ling_deaf + sting_duration = 300 + is_sting = TRUE +/mob/proc/ling_deaf(mob/living/carbon/M, duration) + M.ear_deaf += duration +/mob/proc/changeling_deaf_sting() + set category = "Changeling" + set name = "Deaf sting (5)" + set desc="Sting target:" + + var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting) + if(!T) + return FALSE + admin_attack_log(src,T,"Deaf sting (changeling)") + var/duration = 300 + if(src.mind.changeling.recursive_enhancement) + duration = duration + 100 + to_chat(src, "They will be unable to hear for a little longer.") + to_chat(T, "Your ears pop and begin ringing loudly!") + ling_deaf(T,duration) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/death_sting.dm b/code/game/gamemodes/changeling/powers/death_sting.dm new file mode 100644 index 0000000000000..aebbb4905ec8e --- /dev/null +++ b/code/game/gamemodes/changeling/powers/death_sting.dm @@ -0,0 +1,23 @@ +/datum/power/changeling/DeathSting + name = "Death Sting" + desc = "We silently sting a human, filling him with potent chemicals. His rapid death is all but assured." + ability_icon_state = "ling_sting_death" + genomecost = 7 + verbpath = /mob/proc/changeling_DEATHsting + is_sting = TRUE +/mob/proc/changeling_DEATHsting() + set category = "Changeling" + set name = "Death Sting (40)" + set desc = "Causes spasms onto death." + + var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting) + if(!T) + return FALSE + admin_attack_log(src,T,"Death sting (changeling)") + to_chat(T, "You feel a small prick and your chest becomes tight.") + T.silent = 10 + T.Paralyse(10) + T.make_jittery(100) + if(T.reagents) + T.reagents.add_reagent(/datum/reagent/lexorin, 40) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm b/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm new file mode 100644 index 0000000000000..536e64ed5299d --- /dev/null +++ b/code/game/gamemodes/changeling/powers/delayed_toxin_sting.dm @@ -0,0 +1,50 @@ +/datum/power/changeling/delayed_toxic_sting + name = "Delayed Toxic Sting" + desc = "We silently sting a biological, causing a significant amount of toxins after a few minutes, allowing us to not \ + implicate ourselves. While rarely lethal, highly effective for making organics leave their post to seek medical attention." + helptext = "The toxin takes effect in about two minutes. Multiple applications within the two minutes will not cause increased toxicity." + enhancedtext = "The toxic damage is doubled." + ability_icon_state = "ling_sting_del_toxin" + genomecost = 1 + verbpath = /mob/proc/changeling_delayed_toxic_sting + sting_effect = /mob/proc/ling_delay_toxin + is_sting = TRUE +//disgusting copypasta, but the alternative is a new prop to ling powers just for this one sting +/mob/proc/ling_delay_toxin(mob/living/carbon/M, null) + sleep(2 MINUTES) + M.adjustToxLoss(rand(20,30)) +/mob/proc/changeling_delayed_toxic_sting() + set category = "Changeling" + set name = "Delayed Toxic Sting (20)" + set desc = "Injects the target with a toxin that will take effect after a few minutes." + + var/effect_delay = 2 MINUTES + var/damage = rand(20,30) + var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_delayed_toxic_sting) + if(!T) + return FALSE + admin_attack_log(src,T,"Delayed toxic sting (changeling)") + if(src.mind.changeling.recursive_enhancement) + //type_to_give = /datum/modifier/delayed_toxin_sting/strong + to_chat(src, "Our toxin will be extra potent, when it strikes.") + damage = rand(40,60) + src.mind.changeling.recursive_enhancement = FALSE + if(!(T in src.mind.changeling.toxin_victims)) + src.mind.changeling.toxin_victims.Add(T) + sleep(effect_delay) + if(!(T in src.mind.changeling.toxin_victims)) + return FALSE + T.adjustToxLoss(damage) + src.mind.changeling.toxin_victims.Remove(T) + + //var/test = new /datum/delayed_toxin_sting() + //var/sting_delay = /datum/modifier/delayed_toxin_sting + + + + + //T.add_modifier(type_to_give, 10 SECONDS) + + + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/electric_lockpick.dm b/code/game/gamemodes/changeling/powers/electric_lockpick.dm new file mode 100644 index 0000000000000..1641c06c08966 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/electric_lockpick.dm @@ -0,0 +1,99 @@ +/datum/power/changeling/electric_lockpick + name = "Electric Lockpick" + desc = "We discreetly evolve a finger to be able to send a small electric charge. \ + We can open most electrical locks, but it will be obvious when we do so." + helptext = "Use the ability, then touch something that utilizes an electrical locking system, to open it. Each use costs 10 chemicals." + ability_icon_state = "ling_electric_lockpick" + genomecost = 3 + verbpath = /mob/proc/changeling_electric_lockpick + +//Emag-lite +/mob/proc/changeling_electric_lockpick() + set category = "Changeling" + set name = "Electric Lockpick (5 + 10/use)" + set desc = "Bruteforces open most electrical locking systems, at 10 chemicals per use." + + + + var/obj/held_item = get_active_hand() + if (istype(held_item, /obj/item/weapon/finger_lockpick)) + //to_chat(src,SPAN_WARNING("We cease charging our hand with electricity")) + //playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(held_item) + return 0 + var/datum/changeling/changeling = changeling_power(5,0,100,CONSCIOUS) + if(!changeling) + return FALSE + + if(!held_item) + if(changeling_generic_weapon(/obj/item/weapon/finger_lockpick,0,5)) //Chemical cost is handled in the equip proc. + return TRUE + return FALSE + +/obj/item/weapon/finger_lockpick + name = "finger lockpick" + desc = "This finger appears to be an organic datajack." + icon = 'icons/obj/weapons/melee_energy.dmi' + icon_state = "electric_lockpick" + canremove = FALSE +/obj/item/weapon/finger_lockpick/Initialize() + . = ..() + if(ismob(loc)) + to_chat(loc, "We shape our finger to fit inside electronics, and are ready to force them open.") +/obj/item/weapon/finger_lockpick/use_before(atom/target, mob/living/user, click_parameters) + + if(!target) + return + if(!target.Adjacent(user)) + return + if(!user.mind.changeling) + return + + var/datum/changeling/ling_datum = user.mind.changeling + var/breach_secure = TRUE + if(ling_datum.chem_charges < 10) + to_chat(user, "We require at least 10 chem charges to do that.") + return + + //Airlocks require an ugly block of code, but we don't want to just call emag_act(), since we don't want to break airlocks forever. + if(istype(target,/obj/machinery/door)) + if(istype(target,/obj/machinery/door/airlock/highsecurity) || istype(target,/obj/machinery/door/airlock/vault)) + to_chat(user, SPAN_WARNING("This door's security is complex. It will take us longer to determine the charge needed to breach it.")) + breach_secure = do_after(user, (15 SECONDS + rand(0, 5 SECONDS) + rand(0, 5 SECONDS)), target, do_flags = (DO_DEFAULT | DO_BOTH_UNIQUE_ACT) & ~DO_SHOW_PROGRESS) + var/obj/machinery/door/door = target + to_chat(user, "We send an electrical pulse up our finger, and into \the [target], attempting to open it.") + + if(door.density && door.operable() && breach_secure) + door.do_animate("spark") + sleep(6) + //More typechecks, because windoors can't be locked. Fun. + if(istype(target,/obj/machinery/door/airlock)) + + var/obj/machinery/door/airlock/airlock = target + + if(airlock.locked) //Check if we're bolted. + airlock.unlock() + to_chat(user, "We've unlocked \the [airlock]. Another pulse is requried to open it.") + else //We're not bolted, so open the door already. + airlock.open() + to_chat(user, "We've opened \the [airlock].") + else + door.open() //If we're a windoor, open the windoor. + to_chat(user, "We've opened \the [door].") + else //Probably broken or no power. + to_chat(user, "The door does not respond to the pulse.") + door.add_fingerprint(user) + log_and_message_admins("finger-lockpicked \an [door].") + ling_datum.chem_charges -= 10 + return TRUE + + else if(istype(target,/obj/structure/closet/secure_closet)) //This should catch everything else we might miss, without a million typechecks. + var/obj/O = target + to_chat(user, "We send an electrical pulse up our finger, and into \the [O].") + O.add_fingerprint(user) + O.emag_act(1,user,src) + log_and_message_admins("finger-lockpicked \an [O].") + ling_datum.chem_charges -= 10 + + return TRUE + return FALSE diff --git a/code/game/gamemodes/changeling/powers/endoarmor.dm b/code/game/gamemodes/changeling/powers/endoarmor.dm new file mode 100644 index 0000000000000..8a5fbc84ab768 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/endoarmor.dm @@ -0,0 +1,23 @@ +/datum/power/changeling/endoarmor + name = "Endoarmor" + desc = "We grow hard plating underneath our skin, granting us subtle armor organics will not notice." + helptext = "The armor will not provide us any protection from lasers or fire." + genomecost = 1 + isVerb = 0 + verbpath = /mob/proc/changeling_endoarmor + +/mob/proc/changeling_endoarmor() + if(ishuman(src)) + + var/mob/living/carbon/human/H = src + var/obj/item/organ/internal/augment/armor = new /obj/item/organ/internal/augment/armor/changeling + //H.add_modifier(/datum/modifier/endoarmor) + var/obj/item/organ/external/parent = H.get_organ(BP_CHEST) + armor.forceMove(src) + armor.replaced(src, parent) + src.mind.changeling.purchased_organs.Add(armor) + armor = null + playsound(src, 'sound/effects/corpsecube.ogg',35,1) + to_chat(src, SPAN_NOTICE("Our flesh knits and cracks as the endoarmor forms beneath our skin.")) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/engorged_glands.dm b/code/game/gamemodes/changeling/powers/engorged_glands.dm new file mode 100644 index 0000000000000..4300fa2850e8e --- /dev/null +++ b/code/game/gamemodes/changeling/powers/engorged_glands.dm @@ -0,0 +1,13 @@ +/datum/power/changeling/EngorgedGlands + name = "Engorged Chemical Glands" + desc = "Our chemical glands swell, permitting us to store more chemicals inside of them." + helptext = "Allows us to store an extra 30 units of chemicals." + genomecost = 1 + isVerb = 0 + verbpath = /mob/proc/changeling_engorgedglands + +//Increases macimum chemical storage +/mob/proc/changeling_engorgedglands() + src.mind.changeling.chem_storage += 30 + //src.mind.changeling.chem_recharge_rate *= 2 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/enrage.dm b/code/game/gamemodes/changeling/powers/enrage.dm new file mode 100644 index 0000000000000..fb45843189435 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/enrage.dm @@ -0,0 +1,43 @@ +/datum/power/changeling/enrage + name = "Enrage" + desc = "We evolve modifications to our mind and body, allowing us to call on intense periods of rage for our benefit." + helptext = "Renders us nearly immune to pain and superhumanly fast for one minute, and losing the ability to \ + be accurate at ranged while active. Afterwards, we will suffer extreme amounts of exhaustion, slowing us and potentially causing us to even pass out." + enhancedtext = "The length of our rage will be extended by an additional 30 seconds." + ability_icon_state = "ling_berserk" + genomecost = 2 + power_category = CHANGELING_POWER_ENHANCEMENTS + allowduringlesserform = 1 + verbpath = /mob/living/proc/changeling_berserk + +// Makes the ling very upset. +/mob/living/proc/changeling_berserk() + set category = "Changeling" + set name = "Enrage (30)" + set desc = "Causes you to go Berserk." + + var/datum/changeling/changeling = changeling_power(30,0,100) + var/mob/living/carbon/human/H = src + var/obj/item/organ/external/parent = H.get_organ(BP_GROIN) + var/has_organ = 0; + if(!changeling) + return 0 + + for(var/obj/item/organ/internal/augment/changeling/ragecore/rage in H.internal_organs) + has_organ++ + if(has_organ == 0) + var/obj/item/organ/internal/augment/changeling/rage = new /obj/item/organ/internal/augment/changeling/ragecore + rage.forceMove(src) + rage.replaced(src, parent) + rage = null + has_organ++ + if(has_organ == 1) + for(var/obj/item/organ/internal/augment/changeling/ragecore/rage in H.internal_organs) + if(src.mind.changeling.recursive_enhancement) + rage.ticks_remaining = 90; + src.mind.changeling.recursive_enhancement = FALSE + else + rage.ticks_remaining = 60; + to_chat(src,SPAN_WARNING("Our adrenal glands release a surge of energy, pushing our body to its limits!")) + changeling.chem_charges -= 30 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/escape_restraints.dm b/code/game/gamemodes/changeling/powers/escape_restraints.dm new file mode 100644 index 0000000000000..8ba5de26fd773 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/escape_restraints.dm @@ -0,0 +1,57 @@ +/datum/power/changeling/escape_restraints + name = "Escape Restraints" + desc = "We evolve more complex joints" + helptext = "We can instantly escape from most restraints and bindings, but we cannot do it often." + enhancedtext = "More frequent escapes." + ability_icon_state = "ling_escape_restraints" + genomecost = 2 + verbpath = /mob/proc/changeling_escape_restraints + +//Escape Cuffs. By design this does not escape from straight jackets +/mob/proc/changeling_escape_restraints() + set category = "Changeling" + set name = "Escape Restraints (40)" + set desc = "Removes handcuffs and legcuffs instantly." + + var/escape_cooldown = 5 MINUTES //This is used later to prevent spamming + var/mob/living/carbon/human/C = src + var/datum/changeling/changeling = changeling_power(40,0,100,CONSCIOUS) + if(!changeling) + return 0 + if(world.time < changeling.next_escape) + to_chat(src, "We are still recovering from our last escape...") + return 0 + if(!(C.handcuffed || istype(C.wear_suit,/obj/item/clothing/suit/straight_jacket))) // No need to waste chems if there's nothing to break out of + to_chat(C, "We are are not restrained in a way we can escape...") + return 0 + + changeling.chem_charges -= 40 + + to_chat(C,"We contort our extremities and slip our cuffs.") + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + if(C.handcuffed) + var/obj/item/weapon/W = C.handcuffed + C.unEquip(C.handcuffed) + //C.handcuffed = null + if(C.buckled && C.buckled.buckle_require_restraints) + C.buckled.unbuckle_mob() + C.drop_from_inventory(C.handcuffed) + if (C.client) + C.client.screen -= W + W.forceMove(C.loc) + W.dropped(C) + if(W) + W.layer = initial(W.layer) + if(istype(C.wear_suit, /obj/item/clothing/suit/straight_jacket)) + var/obj/item/clothing/suit/straight_jacket/SJ = C.wear_suit + SJ.forceMove(C.loc) + SJ.dropped(C) + C.wear_suit = null + escape_cooldown *= 1.5 // Straight jackets are tedious compared to cuffs. + + if(src.mind.changeling.recursive_enhancement) + escape_cooldown *= 0.5 + + changeling.next_escape = world.time + escape_cooldown //And now we set the timer + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/extract_dna_sting.dm b/code/game/gamemodes/changeling/powers/extract_dna_sting.dm new file mode 100644 index 0000000000000..ba1394461a0f7 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/extract_dna_sting.dm @@ -0,0 +1,40 @@ +/datum/power/changeling/extractdna + name = "Extract DNA" + desc = "We stealthily sting a target and extract the DNA from them." + helptext = "Will give you the DNA of your target, allowing you to transform into them. Does not count towards absorb objectives." + ability_icon_state = "ling_sting_extract" + genomecost = 0 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_extract_dna_sting + +/mob/proc/changeling_extract_dna_sting() + set category = "Changeling" + set name = "Extract DNA Sting (40)" + set desc="Stealthily sting a target to extract their DNA." + + var/datum/changeling/changeling = null + if(src.mind && src.mind.changeling) + changeling = src.mind.changeling + if(!changeling) + return FALSE + + var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting) + + if(!T) + return + + if(!istype(T) || T.isSynthetic()) + to_chat(src, "\The [T] is not compatible with our biology.") + return FALSE + + if(MUTATION_HUSK in T.mutations) + to_chat(src, "This creature's DNA is ruined beyond useability!") + return FALSE + + admin_attack_log(src,T,"DNA extraction sting (changeling)") + + var/saved_dna = T.dna.Clone() /// Prevent transforming bugginess. + var/datum/absorbed_dna/newDNA = new(T.real_name, saved_dna, T.species.name, T.languages, T.gender, T.pronouns, T.flavor_texts, T.icon_render_keys, T.descriptors ) + absorbDNA(newDNA) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm new file mode 100644 index 0000000000000..82162217f0923 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm @@ -0,0 +1,303 @@ +var/global/list/changeling_fabricated_clothing = list( + "w_uniform" = /obj/item/clothing/under/chameleon/changeling, + "head" = /obj/item/clothing/head/chameleon/changeling, + "wear_suit" = /obj/item/clothing/suit/chameleon/changeling, + "shoes" = /obj/item/clothing/shoes/chameleon/changeling, + "gloves" = /obj/item/clothing/gloves/chameleon/changeling, + "wear_mask" = /obj/item/clothing/mask/chameleon/changeling, + "glasses" = /obj/item/clothing/glasses/chameleon/changeling, + "back" = /obj/item/storage/backpack/chameleon/changeling, + "wear_id" = /obj/item/card/id/syndicate/changeling + ) + +/datum/power/changeling/fabricate_clothing + name = "Fabricate Clothing" + desc = "We reform our flesh to resemble various cloths, leathers, and other materials, allowing us to quickly create a disguise. \ + We cannot be relieved of this clothing by others. We start" + helptext = "The disguise we create offers no defensive ability. Each equipment slot that is empty will be filled with fabricated equipment. \ + To remove our new fabricated clothing, use this ability again." + ability_icon_state = "ling_fabricate_clothing" + genomecost = 1 + verbpath = /mob/proc/changeling_fabricate_clothing + +//Grows biological versions of chameleon clothes. +/mob/proc/changeling_fabricate_clothing() + set category = "Changeling" + set name = "Fabricate Clothing (10)" + + if(changeling_generic_equip_all_slots(changeling_fabricated_clothing, cost = 10)) + return TRUE + return FALSE + +/obj/item/clothing/under/chameleon/changeling + name = "malformed flesh" + icon_state = "lingchameleon" + item_state = "lingchameleon" + worn_state = "lingchameleon" + desc = "The flesh all around us has grown a new layer of cells that can shift appearance and create a biological fabric that cannot be distinguished from \ + ordinary cloth, allowing us to make ourselves appear to wear almost anything." + origin_tech = list() //The base chameleon items have origin technology, which we will inherit if we don't null out this variable. + canremove = 0 //Since this is essentially flesh impersonating clothes, tearing someone's skin off as if it were clothing isn't possible. + +/obj/item/clothing/under/chameleon/changeling/emp_act(severity) //As these are purely organic, EMP does nothing to them. + ..() + return + +/obj/item/clothing/under/chameleon/changeling/verb/shred() //Remove individual pieces if needed. + set name = "Shred Jumpsuit" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + qdel(H.wear_id) + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) +/obj/item/clothing/under/chameleon/changeling/verb/secrete_accessory() + set name = "Secrete Accessory" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.l_hand && H.r_hand) //Make sure our hands aren't full. + to_chat(H, SPAN_WARNING("Our hands are full. Drop something first.")) + return FALSE + var/A = new /obj/item/clothing/accessory/chameleon/changeling + H.put_in_hands(A) + to_chat(H, SPAN_WARNING("We pluck off a piece of our flesh, and prepare to shape it in the form of some organic trinket.")) +/obj/item/clothing/head/chameleon/changeling + name = "malformed head" + icon_state = "lingchameleon" + item_state = "lingchameleon" + flags_inv = HIDEFACE | HIDEEYES + desc = "Our head is swelled with a large quantity of rapidly shifting skin cells. We can reform our head to resemble various hats and \ + helmets that biologicals are so fond of wearing." + origin_tech = list() + canremove = 0 + +/obj/item/clothing/head/chameleon/changeling/emp_act(severity) + ..() + return + +/obj/item/clothing/head/chameleon/changeling/verb/shred() //The copypasta is real. + set name = "Shred Helmet" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/clothing/suit/chameleon/changeling + name = "chitinous chest" + icon_state = "lingchameleon" + item_icons = list( + slot_l_hand_str = 'icons/mob/onmob/items/lefthand_spacesuits.dmi', + slot_r_hand_str = 'icons/mob/onmob/items/righthand_spacesuits.dmi', + ) + item_state = "lingchameleon" + desc = "The cells in our chest are rapidly shifting, ready to reform into material that can resemble most pieces of clothing." + origin_tech = list() + canremove = FALSE + +/obj/item/clothing/suit/chameleon/changeling/emp_act(severity) + ..() + return + +/obj/item/clothing/suit/chameleon/changeling/verb/shred() + set name = "Shred Suit" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/clothing/shoes/chameleon/changeling + name = "malformed feet" + icon_state = "lingchameleon" + item_state = "lingchameleon" + desc = "Our feet are overlayed with another layer of flesh and bone on top. We can reform our feet to resemble various boots and shoes." + origin_tech = list() + canremove = FALSE + +/obj/item/clothing/shoes/chameleon/changeling/emp_act() + ..() + return + +/obj/item/clothing/shoes/chameleon/changeling/verb/shred() + set name = "Shred Shoes" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/storage/backpack/chameleon/changeling + name = "backpack" + icon_state = "lingchameleon" + item_icons = list( + slot_l_hand_str = 'icons/mob/onmob/items/lefthand_backpacks.dmi', + slot_r_hand_str = 'icons/mob/onmob/items/righthand_backpacks.dmi', + ) + item_state = "lingchameleon" + desc = "A large pouch imbedded in our back, it can shift form to resemble many common backpacks that other biologicals are fond of using." + origin_tech = list() + canremove = FALSE + item_flags = ITEM_FLAG_IS_CHAMELEON_ITEM + +/obj/item/storage/backpack/chameleon/changeling/emp_act() + ..() + return + +/obj/item/storage/backpack/chameleon/changeling/verb/shred() + set name = "Shred Backpack" + set category = "Chameleon Items" + if(ishuman(loc)) + if(length(contents)) + to_chat(loc,SPAN_WARNING("We cannot shred our storage while there are items still inside.")) + return + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + for(var/atom/movable/AM in src.contents) //Dump whatever's in the bag before deleting. + AM.forceMove(get_turf(loc)) + qdel(src) + +/obj/item/clothing/gloves/chameleon/changeling + name = "malformed hands" + icon_state = "lingchameleon" + item_state = "lingchameleon" + desc = "Our hands have a second layer of flesh on top. We can reform our hands to resemble a large variety of fabrics and materials that biologicals \ + tend to wear on their hands. Remember that these won't protect your hands from harm." + origin_tech = list() + canremove = FALSE + +/obj/item/clothing/gloves/chameleon/changeling/emp_act() + ..() + return + +/obj/item/clothing/gloves/chameleon/changeling/verb/shred() + set name = "Shred Gloves" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/clothing/mask/chameleon/changeling + name = "chitin visor" + flags_inv = HIDEFACE | HIDEEYES + icon_state = "lingchameleon" + item_state = "lingchameleon" + desc = "A transparent visor of brittle chitin covers our face. We can reform it to resemble various masks that biologicals use. It can also utilize internal \ + tanks.." + origin_tech = list() + canremove = FALSE + +/obj/item/clothing/mask/chameleon/changeling/emp_act() + ..() + return + +/obj/item/clothing/mask/chameleon/changeling/verb/shred() + set name = "Shred Mask" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) +/obj/item/clothing/accessory/chameleon/changeling + name = "malformed lump" + icon_state = "ling_accessory" + item_state = "ling_accessory" + desc = "A small, rapidly shifting and twitching clump of flesh, with veins pulsing throughout." + origin_tech = list() + throwforce = 0 //Just to be on the safe side + throw_range = 0 + throw_speed = 0 + +/obj/item/clothing/accessory/chameleon/changeling/attack_hand() + src.visible_message(SPAN_WARNING("\The [src] melts upon being touched!")) + new /obj/decal/cleanable/ling_vomit(loc) + qdel(src) + +/obj/item/clothing/accessory/chameleon/changeling/emp_act() + ..() + return + +/obj/item/clothing/accessory/chameleon/changeling/verb/shred() + set name = "Shred Accessory" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/clothing/glasses/chameleon/changeling + name = "chitin goggles" + icon_state = "lingchameleon" + item_state = "lingchameleon" + desc = "A transparent piece of eyewear made out of brittle chitin. We can reform it to resemble various glasses and goggles." + origin_tech = list() + canremove = FALSE + +/obj/item/clothing/glasses/chameleon/changeling/emp_act() + ..() + return + +/obj/item/clothing/glasses/chameleon/changeling/verb/shred() + set name = "Shred Glasses" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/card/id/syndicate/changeling + name = "identification card" + desc = "An identification card issued to SolGov crewmembers aboard the SEV Torch." + icon_state = "changeling" + assignment = "Harvester" + origin_tech = list() + electronic_warfare = 1 //The lack of RFID stuff makes it hard for AIs to track, I guess. *handwaves* + registered_user = null + access = null + canremove = FALSE + +/obj/item/card/id/syndicate/changeling/Initialize() + . = ..() + if(ismob(loc)) + registered_user = loc + access = null + +/obj/item/card/id/syndicate/changeling/verb/shred() + set name = "Shred ID Card" + set category = "Chameleon Items" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + playsound(src, 'sound/effects/splat.ogg', 30, 1) + visible_message("[H] tears off [src]!", + "We remove [src].") + qdel(src) + +/obj/item/card/id/syndicate/changeling/Click() //Since we can't hold it in our hands, and attack_hand() doesn't work if it in inventory... + if(!registered_user) + registered_user = usr + usr.set_id_info(src) + ui_interact(registered_user) + ..() +/obj/item/card/id/syndicate/changeling/dropped(mob/user) + if(!ismob(loc)) + return diff --git a/code/game/gamemodes/changeling/powers/fake_death.dm b/code/game/gamemodes/changeling/powers/fake_death.dm new file mode 100644 index 0000000000000..4e433200e213d --- /dev/null +++ b/code/game/gamemodes/changeling/powers/fake_death.dm @@ -0,0 +1,42 @@ +/datum/power/changeling/fakedeath + name = "Regenerative Stasis" + desc = "We become weakened to a death-like state, where we will rise again from death." + helptext = "Can be used before or after death. Duration varies greatly." + ability_icon_state = "ling_regenerative_stasis" + genomecost = 0 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_fakedeath +/mob/proc/finish_revive() + //The ling will now be able to choose when to revive + verbs.Add(/mob/proc/changeling_revive) + new /obj/changeling_revive_holder(src) + to_chat(src, "We are ready to rise. Use the Revive verb when you are ready.") +//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. +/mob/proc/changeling_fakedeath() + set category = "Changeling" + set name = "Regenerative Stasis (20)" + + var/datum/changeling/changeling = changeling_power(CHANGELING_STASIS_COST,1,100,DEAD) + if(!changeling) + return + + var/mob/living/carbon/C = src + + if(changeling.max_geneticpoints < 0) //Absorbed by another ling + to_chat(src, "We have no genomes, not even our own, and cannot regenerate.") + return 0 + + if(!C.stat && alert("Are we sure we wish to regenerate? We will appear to be dead while doing so.","Revival","Yes","No") == "No") + return + to_chat(C, "We will attempt to regenerate our form.") + + C.UpdateLyingBuckledAndVerbStatus() + C.remove_changeling_powers() + C.status_flags |= FAKEDEATH + changeling.chem_charges -= CHANGELING_STASIS_COST + + if(C.stat != DEAD) + C.adjustOxyLoss(C.maxHealth * 2) + addtimer(new Callback(src,/mob/.proc/finish_revive),rand(2 MINUTES, 4 MINUTES)) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/false_identity.dm b/code/game/gamemodes/changeling/powers/false_identity.dm new file mode 100644 index 0000000000000..7c9c919626b90 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/false_identity.dm @@ -0,0 +1,34 @@ +/datum/power/changeling/false_identity + name = "False Identity" + desc = "We twist our body like clay into an identity of our own making." + ability_icon_state = "ling_transform" + genomecost = 1 + power_category = CHANGELING_POWER_INHERENT + verbpath = /mob/proc/changeling_false_identity + +/mob/proc/changeling_false_identity() + set category = "Changeling" + set name = "False Identity (10)" + set desc="Twists our body into a new false identity" + var/datum/changeling/changeling = changeling_power(10,0,100,CONSCIOUS) + var/mob/living/carbon/human/C = src + C.mind.changeling.last_human_form = C.real_name + C.change_appearance(APPEARANCE_BASIC, state = GLOB.z_state) + C.visible_message(SPAN_DANGER("\The [src] lets out a vile crunching noise, as their body begins to reshape itself!")) + playsound(C.loc, 'sound/effects/corpsecube.ogg', 60) + var/response = input(C, "What would you like to call your new self?", "Name change") as null | text + response = sanitize(response, MAX_NAME_LEN) + if (!response) + return + C.real_name = response + C.SetName(response) + C.dna.real_name = response + C.sync_organ_dna() + var/obj/item/organ/external/head/H = C.organs_by_name[BP_HEAD] + if (istype(H) && H.forehead_graffiti) + H.forehead_graffiti = null + if (C.mind) + C.mind.name = C.name + if(changeling) + changeling.chem_charges -= 10 + //to_world("[C.mind.changeling.last_human_form]") diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm new file mode 100644 index 0000000000000..62df05b940edc --- /dev/null +++ b/code/game/gamemodes/changeling/powers/fleshmend.dm @@ -0,0 +1,48 @@ +/datum/power/changeling/fleshmend + name = "Fleshmend" + desc = "Begins a slow regeneration of our form. Does not effect stuns or chemicals." + helptext = "Can be used while unconscious." + enhancedtext = "Healing is twice as effective." + ability_icon_state = "ling_fleshmend" + genomecost = 1 + verbpath = /mob/proc/changeling_fleshmend +/mob/proc/end_fleshmend() + to_chat(src, "Our regeneration has slowed to normal levels.") + src.verbs += /mob/proc/changeling_fleshmend + var/datum/changeling/changeling = src.mind.changeling + changeling.already_regenerating = FALSE +//Starts healing you every second for 50 seconds. Can be used whilst unconscious. +/mob/proc/changeling_fleshmend() + set category = "Changeling" + set name = "Fleshmend (10)" + set desc = "Begins a slow rengeration of our form. Does not effect stuns or chemicals." + set waitfor = FALSE + var/datum/changeling/changeling = changeling_power(10,0,100,UNCONSCIOUS) + if(!changeling) + return + var/mob/living/carbon/human/C = src + if(C.on_fire) + to_chat(src,SPAN_DANGER("We cannot regenerate while engulfed in flames!")) + return + if(changeling.already_regenerating) + to_chat(src,SPAN_DANGER("We are already regenerating our flesh.")) + return + src.mind.changeling.chem_charges -= 10 + var/heal_amount = 2 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + heal_amount = heal_amount * 2 + to_chat(src, "We will heal much faster.") + + to_chat(src, "We begin to heal ourselves.") + changeling.already_regenerating = TRUE + for(var/i = 0, i<50,i++) + if(C && !C.on_fire) + C.adjustBruteLoss(-heal_amount) + C.adjustOxyLoss(-heal_amount) + C.adjustFireLoss(-heal_amount) + C.regenerate_icons() + sleep(1 SECOND) + + src.verbs -= /mob/proc/changeling_fleshmend + addtimer(new Callback(src,/mob/.proc/end_fleshmend), 50 SECONDS) diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm new file mode 100644 index 0000000000000..1eff9067366fe --- /dev/null +++ b/code/game/gamemodes/changeling/powers/hivemind.dm @@ -0,0 +1,77 @@ +// Hivemind + +/datum/power/changeling/hive_upload + name = "Hive Channel" + desc = "We can channel a DNA into the airwaves, allowing our fellow changelings to absorb it and transform into it as if they acquired the DNA themselves." + helptext = "Allows other changelings to absorb the DNA you channel from the airwaves. Will not help them towards their absorb objectives." + genomecost = 0 + make_hud_button = 0 + verbpath = /mob/proc/changeling_hiveupload + +/datum/power/changeling/hive_download + name = "Hive Absorb" + desc = "We can absorb a single DNA from the airwaves, allowing us to use more disguises with help from our fellow changelings." + helptext = "Allows you to absorb a single DNA and use it. Does not count towards your absorb objective." + genomecost = 0 + make_hud_button = 0 + verbpath = /mob/proc/changeling_hivedownload + +// HIVE MIND UPLOAD/DOWNLOAD DNA +GLOBAL_LIST_EMPTY(hivemind_bank) + +/mob/proc/changeling_hiveupload() + set category = "Changeling" + set name = "Hive Channel (10)" + set desc = "Allows you to channel DNA in the airwaves to allow other changelings to absorb it." + + var/datum/changeling/changeling = changeling_power(10,1) + if(!changeling) return + + var/list/names = list() + for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) + if(!(DNA in GLOB.hivemind_bank)) + names += DNA.name + + if(!length(names)) + to_chat(src, "The airwaves already have all of our DNA.") + return + + var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names + if(!S) return + + var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) + if(!chosen_dna) + return + + changeling.chem_charges -= 10 + GLOB.hivemind_bank += chosen_dna + to_chat(src, "We channel the DNA of [S] to the air.") + return TRUE + +/mob/proc/changeling_hivedownload() + set category = "Changeling" + set name = "Hive Absorb (20)" + set desc = "Allows you to absorb DNA that is being channeled in the airwaves." + + var/datum/changeling/changeling = changeling_power(20,1) + if(!changeling) return + + var/list/names = list() + for(var/datum/absorbed_dna/DNA in GLOB.hivemind_bank) + if(!(DNA in changeling.absorbed_dna)) + names[DNA.name] = DNA + + if(!length(names)) + to_chat(src, "There's no new DNA to absorb from the air.") + return + + var/S = input("Select a DNA absorb from the air: ", "Absorb DNA", null) as null|anything in names + if(!S) return + var/datum/absorbed_dna/chosen_dna = names[S] + if(!chosen_dna) + return + + changeling.chem_charges -= 20 + absorbDNA(chosen_dna) + to_chat(src, "We absorb the DNA of [S] from the air.") + return TRUE diff --git a/code/game/gamemodes/changeling/powers/lsd_sting.dm b/code/game/gamemodes/changeling/powers/lsd_sting.dm new file mode 100644 index 0000000000000..fdd0235358d44 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/lsd_sting.dm @@ -0,0 +1,26 @@ +//This only exists to be abused, so it's highly recommended to ensure this file is unchecked. +/datum/power/changeling/LSDSting + name = "Hallucination Sting" + desc = "We evolve the ability to sting a target with a powerful hallunicationary chemical." + helptext = "The target does not notice they have been stung. The effect occurs after 30 to 60 seconds." + genomecost = 1 + verbpath = /mob/proc/changeling_lsdsting + sting_effect = /mob/proc/ling_lsd + is_sting = TRUE + sting_duration = 400 +/mob/proc/time_lsd(T) + if(T) + ling_lsd(T,400) +/mob/proc/ling_lsd(mob/living/carbon/M, duration) + M.hallucination(duration,80) +/mob/proc/changeling_lsdsting() + set category = "Changeling" + set name = "Hallucination Sting (15)" + set desc = "Causes terror in the target." + + var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting) + if(!T) return FALSE + admin_attack_log(src,T,"Hallucination sting (changeling)") + addtimer(new Callback(T,/mob/.proc/time_lsd), 400) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/mimic_voice.dm b/code/game/gamemodes/changeling/powers/mimic_voice.dm new file mode 100644 index 0000000000000..48c70fe87b1ff --- /dev/null +++ b/code/game/gamemodes/changeling/powers/mimic_voice.dm @@ -0,0 +1,39 @@ +/datum/power/changeling/mimicvoice + name = "Mimic Voice" + desc = "We shape our vocal glands to sound like a desired voice." + helptext = "Will turn your voice into the name that you enter. We must constantly expend chemicals to maintain our form like this" + ability_icon_state = "ling_mimic_voice" + genomecost = 1 + verbpath = /mob/proc/changeling_mimicvoice + +// Fake Voice + +/mob/proc/changeling_mimicvoice() + set category = "Changeling" + set name = "Mimic Voice" + set desc = "Shape our vocal glands to form a voice of someone we choose. We cannot regenerate chemicals when mimicing." + set waitfor = FALSE + + var/datum/changeling/changeling = changeling_power() + if(!changeling) return + + if(changeling.mimicing) + changeling.mimicing = "" + to_chat(src, "We return our vocal glands to their original location.") + return + + var/mimic_voice = sanitize(input(usr, "Enter a name to mimic.", "Mimic Voice", null), MAX_NAME_LEN) + if(!mimic_voice) + return + + changeling.mimicing = mimic_voice + + to_chat(src, "We shape our glands to take the voice of [mimic_voice], this will stop us from regenerating chemicals while active.") + to_chat(src, "Use this power again to return to our original voice and reproduce chemicals again.") + + + while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing) + src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0) + sleep(40) + if(src && src.mind && src.mind.changeling) + src.mind.changeling.mimicing = "" diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm new file mode 100644 index 0000000000000..eaf3959e45df9 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -0,0 +1,51 @@ +/datum/power/changeling/panacea + name = "Anatomic Panacea" + desc = "Expels impurifications from our form; curing diseases, removing toxins, chemicals, radiation, and resetting our genetic code completely." + helptext = "Can be used while unconscious. This will also purge any reagents inside ourselves, both harmful and beneficial." + enhancedtext = "We heal more toxins." + ability_icon_state = "ling_anatomic_panacea" + genomecost = 1 + verbpath = /mob/proc/changeling_panacea + +//Heals the things that the other regenerative abilities don't. +/mob/proc/changeling_panacea() + set category = "Changeling" + set name = "Anatomic Panacea (20)" + set desc = "Clense ourselves of impurities." + + var/datum/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS) + if(!changeling) + return FALSE + src.mind.changeling.chem_charges -= 20 + + to_chat(src, "We cleanse impurities from our form.") + + var/mob/living/carbon/human/C = src + + C.radiation = 0 + C.sdisabilities = 0 + C.disabilities = 0 + C.reagents.clear_reagents() + C.empty_stomach() + + + var/heal_amount = 5 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + heal_amount = heal_amount * 2 + to_chat(src, "We will heal much faster.") + + + for(var/obj/item/organ/external/E in C.organs) + var/obj/item/organ/external/G = E + if(G.germ_level) + var/germ_heal = heal_amount * 100 + G.germ_level = min(0, G.germ_level - germ_heal) + + for(var/obj/item/organ/internal/I in C.internal_organs) + var/obj/item/organ/internal/G = I + if(G.germ_level) + var/germ_heal = heal_amount * 100 + G.germ_level = min(0, G.germ_level - germ_heal) + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/rapid_regen.dm b/code/game/gamemodes/changeling/powers/rapid_regen.dm new file mode 100644 index 0000000000000..e9d7fe3b78136 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/rapid_regen.dm @@ -0,0 +1,110 @@ +//had to make this because restore_all_organs just does a full heal, which we don't want. +/obj/item/organ/external/proc/ling_organ_rejuvenate(ignore_prosthetic_prefs) + + // handle internal organs. Heavily debating making this just fix some of the damage since a full heal might be much? idk + for(var/obj/item/organ/current_organ in internal_organs) + current_organ.rejuvenate(ignore_prosthetic_prefs) + + // remove embedded objects and drop them on the floor + for(var/obj/implanted_object in implants) + if(!istype(implanted_object,/obj/item/implant,) || istype(implanted_object,/obj/item/organ/internal/augment)) // We don't want to remove REAL implants. Just shrapnel etc. + implanted_object.forceMove(get_turf(src)) + implants -= implanted_object + + if(owner && !ignore_prosthetic_prefs) + if(owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name) + var/status = owner.client.prefs.organ_data[organ_tag] + if(status == "amputated") + remove_rejuv() + owner.updatehealth() + if(status & ORGAN_ARTERY_CUT) + status &= ~ORGAN_ARTERY_CUT + if(status & ORGAN_TENDON_CUT) + status &= ~ORGAN_TENDON_CUT + if(status & ORGAN_BROKEN) + status &= ~ORGAN_BROKEN + stage = 0 + if(!QDELETED(src) && species) + species.post_organ_rejuvenate(src, owner) +/mob/living/carbon/human/proc/ling_heal_single_organ(ignore_prosthetic_prefs) + for(var/bodypart in BP_BY_DEPTH) + var/obj/item/organ/external/current_organ = organs_by_name[bodypart] + //checks if the limb has been twisted/hurt/affected and if it has, fixes it + if(istype(current_organ) && (current_organ.status != initial(current_organ.status) && !(BP_IS_ROBOTIC(current_organ)))) + current_organ.ling_organ_rejuvenate(ignore_prosthetic_prefs) + return + verbs -= /mob/living/carbon/human/proc/undislocate +/datum/power/changeling/rapid_regen + name = "Rapid Regeneration" + desc = "We quickly heal ourselves, removing most advanced injuries, at a high chemical cost. We cannot regenerate lost limbs however." + helptext = "We will begin to regenerate our flesh, healing ourselves of brute, burn, oxygen and brain damage, as well as mending internal organs, broken bones, \ + and organ damage. The process is fast, but we must remain still and anyone who sees us will know us for what we are." + enhancedtext = "Healing increased to heal up to maximum health." + ability_icon_state = "ling_rapid_regeneration" + genomecost = 2 + verbpath = /mob/proc/changeling_rapid_regen + +//Gives a big heal, removing various injuries that might shut down normal people, like IB or fractures. +/mob/proc/changeling_rapid_regen() + set category = "Changeling" + set name = "Rapid Regeneration (30 per tick)" + set desc = "Heal ourselves of most injuries rapidly." + set waitfor = FALSE + var/finish_regen = TRUE + var/datum/changeling/changeling = changeling_power(30,0,100,UNCONSCIOUS) + if(!changeling) + return + + + if(ishuman(src)) + var/mob/living/carbon/human/C = src + if(C.on_fire) + to_chat(src,SPAN_DANGER("We cannot regenerate while engulfed in flames!")) + return + if(changeling.already_regenerating) + to_chat(src,SPAN_DANGER("We are already regenerating our flesh.")) + return + var/healing_amount = 40 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + healing_amount = 80 + to_chat(src, SPAN_NOTICE("Our regeneration will be stronger.")) + if(src.mind.changeling.chem_charges < 30) + to_chat(src,SPAN_WARNING("We require at least 10 chemicals to regenerate!")) + return + to_chat(src,SPAN_NOTICE("We begin to regenerate our form. We must remain still to complete the process.")) + changeling.already_regenerating = TRUE + while((src.mind.changeling.chem_charges >= 30)) + + finish_regen = do_mob(C,C,5 SECONDS,ignore_movement = FALSE, incapacitation_affected = FALSE) + + if(finish_regen) + C.adjustBruteLoss(-healing_amount) + C.adjustFireLoss(-healing_amount) + C.adjustOxyLoss(-healing_amount) + C.adjustToxLoss(-healing_amount) + C.adjustCloneLoss(-healing_amount) + C.adjustBrainLoss(-healing_amount) + C.regenerate_blood(healing_amount*2) + C.ling_heal_single_organ() + C.blinded = 0 + C.eye_blind = 0 + C.eye_blurry = 0 + C.ear_deaf = 0 + C.ear_damage = 0 + + // make the icons look correct + C.regenerate_icons() + + // now make it obvious that we're not human (or whatever xeno race they are impersonating) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + var/T = get_turf(src) + new /obj/gibspawner/human(T) + + else + to_chat(src,SPAN_WARNING("Our regeneration is brought to a halt by us moving!")) + changeling.already_regenerating = FALSE + return + + src.mind.changeling.chem_charges -= 30 + changeling.already_regenerating = FALSE diff --git a/code/game/gamemodes/changeling/powers/rapid_synthesis.dm b/code/game/gamemodes/changeling/powers/rapid_synthesis.dm new file mode 100644 index 0000000000000..a707d07148cb6 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/rapid_synthesis.dm @@ -0,0 +1,12 @@ +/datum/power/changeling/RapidSynthesis + name = "Rapid Chemical Synthesis" + desc = "We alter our chemical glands to optimize our production of internal chemicals." + helptext = "Allows us to store an extra 30 units of chemicals, and doubles production rate." + genomecost = 1 + isVerb = 0 + verbpath = /mob/proc/changeling_rapid_synthesis + +//Increases macimum chemical storage +/mob/proc/changeling_rapid_synthesis() + src.mind.changeling.chem_recharge_rate *= 2 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/reattach_limb.dm b/code/game/gamemodes/changeling/powers/reattach_limb.dm new file mode 100644 index 0000000000000..bb9116d1591d8 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/reattach_limb.dm @@ -0,0 +1,68 @@ +//Change this to just regrowing it honestly +/datum/power/changeling/reattach_limb + name = "Regrow Limb" + desc = "We reform a lost or damaged appendage." + helptext = "To select the body part to regrow, target the area on the health doll and click the ability." + ability_icon_state = "ling_sting_extract" + genomecost = 1 + power_category = CHANGELING_POWER_INHERENT + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_reattach_limb + +/mob/proc/changeling_reattach_limb() + set category = "Changeling" + set name = "Regrow Limb (25)" + set desc="Regrow a limb that we have lost." + + var/datum/changeling/changeling = changeling_power(25,0,100,CONSCIOUS) + if(src.mind && src.mind.changeling) + changeling = src.mind.changeling + if(!changeling || (src.mind.changeling.chem_charges < 25)) + //to_chat(src,SPAN_WARNING("We require at least 25 chemicals to regenerate a limb!")) + return FALSE + + var/mob/living/carbon/human/C = src + //zone_sel.selecting gets string + var/obj/item/organ/external/target_limb = C.zone_sel.selecting + var/list/organ_data = C.species.has_limbs[target_limb] + var/obj/item/organ/external/limb_path = organ_data["path"] + var/obj/item/organ/external/current_limb = C.get_organ(C.zone_sel.selecting) + if((current_limb.organ_tag == BP_HEAD) || (current_limb.organ_tag == BP_CHEST)) + to_chat(C,SPAN_WARNING("That part of our body is too complex for us to simply regrow. We must enter hibernating stasis to regrow it.")) + return FALSE + if (!current_limb || current_limb.is_stump()) + var/obj/item/organ/external/regrown_limb = new limb_path(C) + var/obj/item/organ/external/parent_limb = (C.get_organ(regrown_limb.parent_organ)) + if(!parent_limb || parent_limb.is_stump()) + to_chat(C,SPAN_WARNING("We cannot regrow this limb without regrowing the limb it attaches to first!")) + qdel(regrown_limb) + return + if(current_limb) + qdel(current_limb) + regrown_limb.replaced(C) + regrown_limb.forceMove(C) + regrown_limb = null + C.update_body() + C.updatehealth() + C.UpdateDamageIcon() + C.visible_message(SPAN_DANGER("\The [C] regenerates the tissue of their limb with a sickening squelch!")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + changeling.chem_charges -= 25 + else + current_limb.rejuvenate(ignore_prosthetic_prefs = FALSE) + C.update_body() + C.updatehealth() + C.UpdateDamageIcon() + C.visible_message(SPAN_DANGER("\The [C] regenerates the tissue of their limb with a sickening squelch!")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + changeling.chem_charges -= 25 + + + + if(!C) + return + + + + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/recursive_enhancement.dm b/code/game/gamemodes/changeling/powers/recursive_enhancement.dm new file mode 100644 index 0000000000000..de7f241a8fd4a --- /dev/null +++ b/code/game/gamemodes/changeling/powers/recursive_enhancement.dm @@ -0,0 +1,24 @@ +/datum/power/changeling/recursive_enhancement + name = "Recursive Enhancement" + desc = "We cause our abilities to have increased or additional effects." + helptext = "To check the effects for each ability, check the blue text underneath the ability in the evolution menu." + ability_icon_state = "ling_recursive_enhancement" + genomecost = 3 + verbpath = /mob/proc/changeling_recursive_enhancement + +//Increases macimum chemical storage +/mob/proc/changeling_recursive_enhancement() + set category = "Changeling" + set name = "Recursive Enhancement" + set desc = "Empowers our abilities." + var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + if(!changeling) + return FALSE + if(src.mind.changeling.recursive_enhancement) + to_chat(src, "We will no longer empower our abilities.") + src.mind.changeling.recursive_enhancement = 0 + return FALSE + to_chat(src, "We empower ourselves. Our next ability will now be extra potent.") + src.mind.changeling.recursive_enhancement = 1 + changeling.chem_charges -= 10 + return TRUE diff --git a/code/game/gamemodes/changeling/powers/reinforced_tendons.dm b/code/game/gamemodes/changeling/powers/reinforced_tendons.dm new file mode 100644 index 0000000000000..b9672c1e2de1c --- /dev/null +++ b/code/game/gamemodes/changeling/powers/reinforced_tendons.dm @@ -0,0 +1,18 @@ +/datum/power/changeling/reinforced_tendons + name = "Reinforced Tendons" + desc = "We reinforce our tendons to lunch" + helptext = "We will be also be able to fall great distances without damage. If we leap at a target with both arms shaped into weapons, we will perform a special pounce attack." + genomecost = 2 + isVerb = 0 + verbpath = /mob/proc/reinforced_tendons +/mob/proc/reinforced_tendons() + + var/mob/living/carbon/human/C = src + //C.available_maneuvers.Remove(/singleton/maneuver/leap) + C.available_maneuvers.Add(/singleton/maneuver/leap/changeling) + src.mind.changeling.tendons_reinforced = TRUE + C.buff_skill(list(SKILL_HAULING = 1), 0, buff_type = /datum/skill_buff) + //C.prepared_maneuver = /singleton/maneuver/leap/grab + + playsound(src, 'sound/effects/corpsecube.ogg',35,1) + to_chat(src, SPAN_NOTICE("Our leg muscles twist and reknit themselves, forming stronger muscles that can both leap further and fall from great heights!")) diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm new file mode 100644 index 0000000000000..d2126021d14bb --- /dev/null +++ b/code/game/gamemodes/changeling/powers/revive.dm @@ -0,0 +1,47 @@ +//Revive from revival stasis +/mob/proc/changeling_revive() + set category = "Changeling" + set name = "Revive" + set desc = "We are ready to revive ourselves on command." + + var/datum/changeling/changeling = changeling_power(0,0,100,DEAD) + if(!changeling) + return 0 + + if(changeling.max_geneticpoints < 0) //Absorbed by another ling + to_chat(src, "You have no genomes, not even your own, and cannot revive.") + return 0 + var/mob/living/carbon/C = src + // restore us to health + C.revive() + // remove our fake death flag + C.status_flags &= ~(FAKEDEATH) + // let us move again + C.UpdateLyingBuckledAndVerbStatus() + // re-add out changeling powers + C.make_changeling() + // sending display messages + to_chat(C, SPAN_NOTICE("We have regenerated.")) + C.verbs -= /mob/proc/changeling_revive + + return TRUE + +//Revive from revival stasis, but one level removed, as the tab refuses to update. Placed in its own tab to avoid hyper-exploding the original tab through the same name being used. + +/obj/changeling_revive_holder + name = "strange object" + desc = "Please report this object's existence to the dev team! You shouldn't see it." + mouse_opacity = FALSE + alpha = 1 + +/obj/changeling_revive_holder/verb/ling_revive() + set src = usr.contents + set category = "Regenerate" + set name = "Revive" + set desc = "We are ready to revive ourselves on command." + + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.changeling_revive() + + qdel(src) diff --git a/code/game/gamemodes/changeling/powers/self_respiration.dm b/code/game/gamemodes/changeling/powers/self_respiration.dm new file mode 100644 index 0000000000000..3a0a2312b5080 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/self_respiration.dm @@ -0,0 +1,34 @@ +/datum/power/changeling/self_respiration + name = "Self Respiration" + desc = "We evolve our body to no longer require drawing oxygen from the atmosphere." + helptext = "We will no longer require internals, and we cannot inhale any gas, including harmful ones." + ability_icon_state = "ling_toggle_breath" + genomecost = 0 + power_category = CHANGELING_POWER_INHERENT + verbpath = /mob/proc/changeling_self_respiration + +//No breathing required +/mob/proc/changeling_self_respiration() + set category = "Changeling" + set name = "Activate Internal Air Sacs" + set desc = "We choose whether or not to breathe." + var/mnoBreath = 100 + + var/datum/changeling/changeling = changeling_power(0,0,100,UNCONSCIOUS) + if(!changeling) + return FALSE + + if(istype(src,/mob/living/carbon)) + var/mob/living/carbon/C = src + if (mnoBreath in C.mutations) + //C.mutations |= mNobreath + C.mutations.Remove(mnoBreath) + to_chat(src,SPAN_NOTICE("We resume breathing, to avoid suspicion.")) + return FALSE + else + C.mutations.Add(mnoBreath) + to_chat(src, "We stop breathing, as we no longer need to.") + return TRUE + //else + //C.does_not_breathe = 0 + //to_chat(src, "We resume breathing, as we now need to again.") diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm new file mode 100644 index 0000000000000..dfa48a2183e81 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/shriek.dm @@ -0,0 +1,152 @@ +/datum/power/changeling/resonant_shriek + name = "Resonant Shriek" + desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded." + helptext = "Lights are blown, organics are disoriented, and synthetics act as if they were flashed." + enhancedtext = "Range is doubled." + ability_icon_state = "ling_resonant_shriek" + genomecost = 2 + verbpath = /mob/proc/changeling_resonant_shriek + +/datum/power/changeling/dissonant_shriek + name = "Dissonant Shriek" + desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby radios" + helptext = "Radios within a radius of us are shut off." + enhancedtext = "Range is doubled." + ability_icon_state = "ling_dissonant_shriek" + genomecost = 2 + verbpath = /mob/proc/changeling_dissonant_shriek + +//A flashy ability, good for crowd control and sewing chaos. +/mob/proc/changeling_resonant_shriek() + set category = "Changeling" + set name = "Resonant Shriek (15)" + set desc = "Emits a high-frequency sound that deafens organics, blows out nearby lights, and overloads synthetics' sensors." + + var/mob/living/carbon/human/H = src + + var/datum/changeling/changeling = changeling_power(15,0,100,CONSCIOUS) + if(!changeling) return 0 + + if(is_muzzled()) + to_chat(src, "Mmmf mrrfff!") + return 0 + + if(ishuman(src)) + if(H.silent) + to_chat(src, "You can't speak!") + return 0 + + if(world.time < (changeling.last_shriek + 10 SECONDS) ) + to_chat(src, "We are still recovering from our last shriek...") + return 0 + + if(!isturf(loc)) + to_chat(src, "Shrieking here would be a bad idea.") + return 0 + + H.remove_cloaking_source(src) //No more invisible shrieking + + changeling.chem_charges -= 15 + var/range = 4 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + range = range * 2 + to_chat(src, "We are extra loud.") + + visible_message("[src] appears to shout.") + playsound(src, 'sound/effects/ling_shriek.ogg',100,1) + var/list/affected = list() + for(var/mob/living/M in range(range, src)) + if(iscarbon(M)) + if(!M.mind || !M.mind.changeling) + if(M.get_sound_volume_multiplier() < 0.2) + continue + to_chat(M, "You hear an extremely loud screeching sound! It \ + [pick("confuses","confounds","perturbs","befuddles","dazes","unsettles","disorients")] you.") + M.adjustEarDamage(0,30) + affected += M + else + if(M != src) + to_chat(M, "You hear a familiar screech from nearby. It has no effect on you.") + + if(issilicon(M)) + to_chat(M, "Auditory input overloaded. Reinitializing...") + M.Weaken(rand(5,10)) + affected += M + + for(var/obj/machinery/light/L in range(range, src)) + L.on = 1 + L.broken() + for(var/obj/item/device/flashlight/F in range(range, src)) + //add code for flashlights that cannot be turned off here + if (F.on) + F.on = FALSE + F.set_light(0) + F.update_icon() + changeling.last_shriek = world.time + + admin_attack_log(src,affected,"Used resonant shriek") + return TRUE + +//EMP version +/mob/proc/changeling_dissonant_shriek() + set category = "Changeling" + set name = "Dissonant Shriek (15)" + set desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby radios." + + var/mob/living/carbon/human/H = src + + var/datum/changeling/changeling = changeling_power(15,0,100,CONSCIOUS) + if(!changeling) + return FALSE + + if(is_muzzled()) + to_chat(src, "Mmmf mrrfff!") + return FALSE + + if(ishuman(src)) + if(H.silent) + to_chat(src, "You can't speak!") + return FALSE + + if(world.time < (changeling.last_shriek + 10 SECONDS) ) + to_chat(src, "We are still recovering from our last shriek...") + return FALSE + + if(!isturf(loc)) + to_chat(src, "Shrieking here would be a bad idea.") + return FALSE + + H.remove_cloaking_source(src) //No more invisible shrieking + + changeling.chem_charges -= 15 + + var/range_heavy = 1 + var/range_med = 2 + var/range_light = 4 + var/range_long = 6 + if(src.mind.changeling.recursive_enhancement) + range_heavy = range_heavy * 2 + range_med = range_med * 2 + range_light = range_light * 2 + range_long = range_long * 2 + to_chat(src, "We are extra loud.") + src.mind.changeling.recursive_enhancement = FALSE + + visible_message("[src] appears to shout.") + playsound(src, 'sound/effects/ling_shriek2.ogg',100,1) + admin_attack_log(src,null,"Use dissonant shriek") + + + for(var/obj/item/device/radio/R in range(range_long,src)) + R.broadcasting = FALSE + R.listening = FALSE + for (var/ch_name in R.channels) + R.channels[ch_name] = FALSE + if(R.cell) + R.cell.emp_act(2) + + + changeling.last_shriek = world.time + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/silence_sting.dm b/code/game/gamemodes/changeling/powers/silence_sting.dm new file mode 100644 index 0000000000000..c570902e5b322 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/silence_sting.dm @@ -0,0 +1,29 @@ +/datum/power/changeling/silence_sting + name = "Silence Sting" + desc = "We silently sting a human, completely silencing them for a short time." + helptext = "Does not provide a warning to a victim that they have been stung, until they try to speak and cannot." + enhancedtext = "Silence duration is extended." + ability_icon_state = "ling_sting_mute" + genomecost = 2 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_silence_sting + sting_effect = /mob/proc/silence + is_sting = TRUE + sting_duration = 30 +/mob/proc/silence(mob/living/carbon/M, duration) + M.silent += duration +/mob/proc/changeling_silence_sting() + set category = "Changeling" + set name = "Silence sting (10)" + set desc="Sting target" + + var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting) + if(!T) return 0 + admin_attack_log(src,T,"Silence sting (changeling)") + var/duration = 30 + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + duration = duration + 10 + to_chat(src, "They will be unable to cry out in fear for a little longer.") + silence(T,duration) + return TRUE diff --git a/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm b/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm new file mode 100644 index 0000000000000..8392e7f397db7 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/synaptizine_overdose.dm @@ -0,0 +1,43 @@ +/datum/power/changeling/epinephrine_overdose + name = "Adrenaline Surge" + desc = "We evolve additional sacs of adrenaline throughout our body." + helptext = "We can instantly recover from stuns and reduce the effect of future stuns, but we will suffer toxicity in the long term. Can be used while unconscious." + enhancedtext = "Immunity from most disabling effects for 30 seconds." + ability_icon_state = "ling_epinephrine_overdose" + genomecost = 2 + verbpath = /mob/proc/changeling_synaptizine_overdose + +//Recover from stuns. +/mob/proc/changeling_synaptizine_overdose() + set category = "Changeling" + set name = "Adrenaline Surge (20)" + set desc = "Removes all stuns instantly, and reduces future stuns." + + var/datum/changeling/changeling = changeling_power(20,0,100,UNCONSCIOUS) + if(!changeling) + return FALSE + changeling.chem_charges -= 20 + + var/mob/living/carbon/human/C = src + to_chat(C, "Energy rushes through us. [C.lying ? "We arise." : ""]") + C.set_stat(CONSCIOUS) + C.SetParalysis(0) + C.SetStunned(0) + C.SetWeakened(0) + C.setHalLoss(0) + C.lying = 0 + C.blinded = 0 + C.eye_blind = 0 + C.eye_blurry = 0 + C.ear_deaf = 0 + C.ear_damage = 0 + C.clear_confused() + C.sleeping = 0 +// C.reagents.add_reagent("toxin", 10) + C.reagents.add_reagent("synaptizine", 10) + + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + + + return TRUE diff --git a/code/game/gamemodes/changeling/powers/tentacle.dm b/code/game/gamemodes/changeling/powers/tentacle.dm new file mode 100644 index 0000000000000..a713ef335ea80 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/tentacle.dm @@ -0,0 +1,103 @@ +/proc/get_steps_with_obstacles(turf/Turf1,turf/Turf2) + var/list/steps = list() + while(Turf1 != Turf2) + var/turf/next_tile = get_step_towards(Turf1,Turf2) + steps.Add(next_tile) + Turf1 = next_tile + return steps +/proc/find_tentacle_hit(list/tentacle_obstacles) + for(var/turf/check_turf in tentacle_obstacles) + if(check_turf.density) + return check_turf + for(var/obj/item in check_turf) + if(!item.throwpass && item.density) + return item +//commented out until sprite gets added +/* +/datum/power/changeling/tentacle + name = "Tentacle" + desc = "We reform our arm into a tentacle capable of grabbing objects or victims at a distance. Victims grabbed by the tentacle will have their stamina drained, and struggle to run away." + helptext = "Items grabbed by the tentacle will be pulled directly into our hand." + enhancedtext = "The range is extended to five tiles." + ability_icon_state = "ling_sting_boost_range" + genomecost = 2 + allowduringlesserform = 1 + verbpath = /mob/proc/changeling_tentacle +*/ +/obj/item/tentacle + name = "tentacle" + desc = "A quivering tentacle of flesh that shudders with anticipation." + icon = 'icons/obj/guns/confuseray.dmi' + icon_state = "confuseray" + canremove = FALSE + throw_speed = 0 + throwforce = 0 + throw_range = 0 + var/default_range = 5 + var/last_tentacle = null + var/cooldown = 4 SECONDS +/obj/item/tentacle/enhanced + default_range = 7 +/obj/ebeam/ling + pass_flags = PASS_FLAG_TABLE + +/obj/item/tentacle/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + if ((last_tentacle + cooldown > world.time)) + to_chat(user, SPAN_WARNING("We are still recovering from our last tentacle attack.")) + return + if(get_dist(user, target) >= default_range) + to_chat(user,SPAN_WARNING("Our tentacle cannot extend to that distance!")) + return + var/list/tentacle_obstacles = get_steps_with_obstacles(get_turf(user),get_turf(target)) + if(find_tentacle_hit(tentacle_obstacles)) + target = find_tentacle_hit(tentacle_obstacles) + user.Beam(BeamTarget = target, icon_state = "r_beam", time = 3, maxdistance = INFINITY, beam_type = /obj/ebeam/ling) + + user.visible_message(SPAN_DANGER("\the [user] lashes at \the [target] with their tentacle!")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + last_tentacle = world.time + if(isobj(target)) + var/obj/grabbed_object = target + if(grabbed_object.anchored) + to_chat(user,SPAN_WARNING("\the [grabbed_object] is too secured in place for our tentacle to get a good grip!")) + return FALSE + user.throw_mode_on() + grabbed_object.throw_at(user, 5,3) + qdel(src) + if(ismob(target)) + var/mob/victim = target + var/turf/tile = get_step_towards(user,victim) + victim.throw_at(tile, 5,1) + if(victim.get_active_hand()) + victim.unequip_item() + if(istype(victim,/mob/living/carbon/human)) + var/mob/living/carbon/human/V = victim + V.stamina = 0 + return TRUE +/mob/proc/changeling_tentacle() + set category = "Changeling" + set name = "Tentacle (20)" + set desc="We transform one of our arms into a tentacle that can pull distant targets towards us." + + var/datum/changeling/changeling = changeling_power(10,0,100) + if(!changeling) + return TRUE + + var/holding = src.get_active_hand() + if (istype(holding, /obj/item/tentacle)) + to_chat(src,SPAN_WARNING("We reform our arm from a tentacle back into its normal form.")) + playsound(src, 'sound/effects/blobattack.ogg', 30, 1) + qdel(holding) + return 0 + + changeling.chem_charges -= 10 + + if(src.mind.changeling.recursive_enhancement) + if(changeling_generic_weapon(/obj/item/tentacle/enhanced, 10)) + src.mind.changeling.recursive_enhancement = FALSE + return 1 + + else + if(changeling_generic_weapon(/obj/item/tentacle, 10)) + return 1 + return 0 diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm new file mode 100644 index 0000000000000..08105e63f6f93 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -0,0 +1,69 @@ +/datum/power/changeling/transform + name = "Transform" + desc = "We take on the appearance and voice of one we have absorbed." + ability_icon_state = "ling_transform" + genomecost = 0 + power_category = CHANGELING_POWER_INHERENT + verbpath = /mob/proc/changeling_transform +/mob/proc/transform_cooldown() + src.verbs += /mob/proc/changeling_transform + src.regenerate_icons() + src.update_hud() +//Change our DNA to that of somebody we've absorbed. +/mob/proc/changeling_transform() + set category = "Changeling" + set name = "Transform (5)" + + var/datum/changeling/changeling = changeling_power(5,1,0) + if(!changeling) return + + if(!isturf(loc) || !(/mob/proc/changeling_transform in src.verbs)) + to_chat(src, "Transforming here would be a bad idea.") + return 0 + + var/list/names = list() + for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna) + names += "[DNA.name]" + + var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names + if(!S) return + + var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S) + if(!chosen_dna) + return + + changeling.chem_charges -= 5 + src.visible_message("[src] transforms!") + changeling.geneticdamage = 5 + + if(ishuman(src)) + var/mob/living/carbon/human/H = src + var/newSpecies = chosen_dna.speciesName + H.set_species(newSpecies,1) + H.ability_master.toggle_open(forced_state = 2) + for(var/obj/item/organ/internal/augment/ling_organ in H.mind.changeling.purchased_organs) + var/obj/item/organ/external/parent = H.get_organ(BP_CHEST) + ling_organ.forceMove(src) + ling_organ.replaced(src, parent) + ling_organ = null + src.dna.b_type = "AB+" //This is needed to avoid blood rejection bugs. The fact that the blood type might not match up w/ records could be a *FEATURE* too. + if(ishuman(src)) + var/mob/living/carbon/human/H = src + H.dna = chosen_dna.dna.Clone() + H.b_type = "AB+" //For some reason we have two blood types on the mob. + H.gender = chosen_dna.gender + H.pronouns = chosen_dna.pronouns + H.flavor_texts = chosen_dna.flavour_texts ? chosen_dna.flavour_texts.Copy() : null + if(H.descriptors) + H.descriptors = chosen_dna.descriptors.Copy() + H.sync_organ_dna() + H.icon_render_keys = chosen_dna.icon_render_details + src.real_name = chosen_dna.name + domutcheck(src, null) + + src.UpdateAppearance() + + changeling_update_languages(changeling.absorbed_languages) + + src.verbs -= /mob/proc/changeling_transform + addtimer(new Callback(src,/mob/.proc/transform_cooldown), 10 SECONDS) diff --git a/code/game/gamemodes/changeling/powers/visible_camouflage.dm b/code/game/gamemodes/changeling/powers/visible_camouflage.dm new file mode 100644 index 0000000000000..e6ed45b873599 --- /dev/null +++ b/code/game/gamemodes/changeling/powers/visible_camouflage.dm @@ -0,0 +1,75 @@ +/datum/power/changeling/visible_camouflage + name = "Camouflage" + desc = "We rapidly shape the color of our skin and secrete easily reversible dye on our clothes, to blend in with our surroundings. \ + We are undetectable, so long as we move slowly.(Toggle)" + helptext = "Running, and performing most acts will reveal us. Our chemical regeneration is halted while we are hidden." + enhancedtext = "Can run while hidden." + ability_icon_state = "ling_camoflage" + genomecost = 3 + verbpath = /mob/proc/changeling_visible_camouflage + +//Hide us from anyone who would do us harm. +/mob/proc/changeling_visible_camouflage() + set category = "Changeling" + set name = "Visible Camouflage (10)" + set desc = "Turns yourself almost invisible, as long as you move slowly." + + + if(istype(src,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + + if(H.mind.changeling.cloaked) + H.mind.changeling.cloaked = 0 + return TRUE + + //We delay the check, so that people can uncloak without needing 10 chemicals to do so. + var/datum/changeling/changeling = changeling_power(10,0,100,CONSCIOUS) + + if(!changeling) + return FALSE + changeling.chem_charges -= 10 + var/old_regen_rate = H.mind.changeling.chem_recharge_rate + + to_chat(H, "We vanish from sight, and will remain hidden, so long as we move carefully.") + H.mind.changeling.cloaked = 1 + H.mind.changeling.chem_recharge_rate = 0 + animate(src,alpha = 255, alpha = 10, time = 10) + + var/must_walk = TRUE + if(src.mind.changeling.recursive_enhancement) + src.mind.changeling.recursive_enhancement = FALSE + must_walk = FALSE + to_chat(src, "We may move at our normal speed while hidden.") + + if(must_walk) + H.set_move_intent(default_walk_intent) + + var/remain_cloaked = TRUE + while(remain_cloaked) //This loop will keep going until the player uncloaks. + sleep(1 SECOND) // Sleep at the start so that if something invalidates a cloak, it will drop immediately after the check and not in one second. + + if(MOVING_QUICKLY(H) && must_walk) // Moving too fast uncloaks you. + remain_cloaked = 0 + if(!H.mind.changeling.cloaked) + remain_cloaked = 0 + if(H.stat) // Dead or unconscious lings can't stay cloaked. + remain_cloaked = 0 + //using or equipping an item will uncloak you + if(H.l_hand || H.r_hand) + remain_cloaked = 0 + if(H.incapacitated(INCAPACITATION_DISABLED)) // Stunned lings also can't stay cloaked. + remain_cloaked = 0 + + if(mind.changeling.chem_recharge_rate != 0) //Without this, there is an exploit that can be done, if one buys engorged chem sacks while cloaked. + old_regen_rate += mind.changeling.chem_recharge_rate //Unfortunately, it has to occupy this part of the proc. This fixes it while at the same time + mind.changeling.chem_recharge_rate = 0 //making sure nobody loses out on their bonus regeneration after they're done hiding. + + + + H.invisibility = initial(invisibility) + visible_message("[src] suddenly fades in, seemingly from nowhere!", + "We revert our camouflage, revealing ourselves.") + H.mind.changeling.cloaked = 0 + H.mind.changeling.chem_recharge_rate = old_regen_rate + + animate(src,alpha = 10, alpha = 255, time = 10) diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index d0663f1930023..079a88e8d5039 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -294,7 +294,13 @@ icon_state = "greenshatter" anchored = TRUE layer = BLOOD_LAYER - +/obj/decal/cleanable/ling_vomit + name = "green goop" + desc = "Green squishy mess." + icon = 'icons/effects/effects.dmi' + icon_state = "greenshatter" + anchored = TRUE + layer = BLOOD_LAYER /obj/spider/cocoon name = "cocoon" desc = "Something wrapped in silky spider web." diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index d9a2528cc320b..c1dff74f87270 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -360,9 +360,13 @@ /obj/structure/railing/hitby(atom/movable/AM, datum/thrownthing/TT) var/mob/living/L = AM + var/chance if (!istype(L)) return - var/chance = TT.thrower.skill_check(SKILL_HAULING, SKILL_EXPERIENCED) ? 100 : 50 + if (istype(TT.thrower, L)) + chance = TT.thrower.skill_check(SKILL_HAULING, SKILL_EXPERIENCED) ? 100 : 50 + else + chance = 50 if (prob(chance)) slam_into(L) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index f2e5b10fddbbb..ff0cae97c75a3 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -134,7 +134,8 @@ var/global/list/admin_verbs_fun = list( /client/proc/cmd_admin_simulate_distant_explosion, /datum/admins/proc/ai_hologram_set, /client/proc/bombard_zlevel, - /client/proc/rename_shuttle + /client/proc/rename_shuttle, + /client/proc/give_disease2 // [/SIERRA-ADD] - CLIENT_VERBS ) var/global/list/admin_verbs_spawn = list( diff --git a/code/modules/augment/passive/armor.dm b/code/modules/augment/passive/armor.dm index d8695936dd7cb..1206d8f873923 100644 --- a/code/modules/augment/passive/armor.dm +++ b/code/modules/augment/passive/armor.dm @@ -6,3 +6,13 @@ augment_flags = AUGMENT_MECHANICAL | AUGMENT_BIOLOGICAL | AUGMENT_SCANNABLE | AUGMENT_INSPECTABLE var/brute_mult = 0.8 var/burn_mult = 1 +/obj/item/organ/internal/augment/armor/changeling + name = "organic armor" + icon_state = "endoarmor" + augment_flags = AUGMENT_BIOLOGICAL + status = ORGAN_CONFIGURE + desc = "A collection of bone, chitin and tissue carefully wrapped around the chest cavity." + brute_mult = 0.75 +/obj/item/organ/internal/augment/armor/changeling/emp_act() + SHOULD_CALL_PARENT(FALSE) + return \ No newline at end of file diff --git a/code/modules/augment/passive/ling_lenses.dm b/code/modules/augment/passive/ling_lenses.dm new file mode 100644 index 0000000000000..1e3ec15e96852 --- /dev/null +++ b/code/modules/augment/passive/ling_lenses.dm @@ -0,0 +1,19 @@ +/obj/item/organ/internal/augment/ling_lenses + name = "organic eyelid lense" + desc = "a thin, almost transparent lense surrounded by nerve fibers" + icon_state = "thermal_eyes" + status = ORGAN_CONFIGURE + augment_slots = AUGMENT_HEAD + augment_flags = AUGMENT_BIOLOGICAL + var/is_active = FALSE +/obj/item/organ/internal/augment/ling_lenses/emp_act() + ..() + return +/obj/item/organ/internal/augment/ling_lenses/Process() + ..() + if(is_active && (owner.mind.changeling.chem_charges > 0)) + owner.set_sight(owner.sight | SEE_MOBS) + owner.mind.changeling.chem_charges -= 3 + if(owner.mind.changeling.chem_charges < 3) + to_chat(owner,SPAN_NOTICE("Our lenses retract, causing us to lose our augmented vision.")) + is_active = FALSE diff --git a/code/modules/augment/passive/lingcore.dm b/code/modules/augment/passive/lingcore.dm new file mode 100644 index 0000000000000..eec814725f3a5 --- /dev/null +++ b/code/modules/augment/passive/lingcore.dm @@ -0,0 +1,57 @@ +/obj/item/organ/internal/augment/lingcore + name = "bizarre mass" + desc = "a twitching, pulsating mass that almost resembles a deformed embryo" + icon_state = "lingcore" + w_class = ITEM_SIZE_TINY + augment_slots = AUGMENT_CHEST + status = ORGAN_CONFIGURE + augment_flags = AUGMENT_BIOLOGICAL + var/ticks_remaining = 0; + var/datum/mind/backup + var/ownerckey + var/default_language + var/list/languages = list() + +/obj/item/organ/internal/augment/lingcore/emp_act() + ..() + return +/obj/item/organ/internal/augment/lingcore/getToxLoss() + return 0 +/obj/item/organ/internal/augment/lingcore/Process() + ..() + if(istype(owner,/mob/living/carbon/human)) + var/mob/living/carbon/human/C = owner + var/obj/item/organ/internal/brain/B = C.internal_organs_by_name[BP_BRAIN] + if(B.germ_level != 0) + B.germ_level = 0 +/obj/item/organ/internal/augment/lingcore/onInstall() + ..() + if(istype(owner,/mob/living/carbon) && (owner.mind) && (!owner.mind.changeling)) + overwrite() + owner.make_changeling() + if(owner.mind && owner.mind.changeling) + do_backup() +/obj/item/organ/internal/augment/lingcore/onRemove() + ..() + if(istype(owner,/mob/living/carbon) ) + owner.death(0) + owner.Drain() +/obj/item/organ/internal/augment/lingcore/proc/do_backup() + if(owner && owner.stat != DEAD && !is_broken() && owner.mind) + languages = owner.languages.Copy() + backup = owner.mind + default_language = owner.default_language + if(owner.ckey) + ownerckey = owner.ckey +/obj/item/organ/internal/augment/lingcore/proc/overwrite() + if(owner.mind && owner.ckey) //Someone is already in this body! + if(owner.mind == backup) // Oh, it's the same mind in the backup. Someone must've spammed the 'Start Procedure' button in a panic. + return + owner.visible_message(SPAN_DANGER("\The [owner] spasms and seizes!")) + owner.ghostize() + backup.active = 1 + backup.transfer_to(owner) + if (default_language) + owner.default_language = default_language + owner.languages = languages.Copy() + to_chat(owner, SPAN_NOTICE("Our tendrils extend throughout our new form, wrapping around nerve filaments. We awaken.")) diff --git a/code/modules/augment/passive/ragecore.dm b/code/modules/augment/passive/ragecore.dm new file mode 100644 index 0000000000000..333bf1d4d33f4 --- /dev/null +++ b/code/modules/augment/passive/ragecore.dm @@ -0,0 +1,29 @@ +/obj/item/organ/internal/augment/changeling/ragecore + name = "strange mass" + desc = "a throbbing, writhing sack of meat that almost seems to beat like a living heart" + icon_state = "berserk_sac" + augment_slots = AUGMENT_GROIN + status = ORGAN_CONFIGURE + augment_flags = AUGMENT_BIOLOGICAL + var/ticks_remaining = 0; +/obj/item/organ/internal/augment/changeling/ragecore/emp_act() + ..() + return +/obj/item/organ/internal/augment/changeling/ragecore/Process() + ..() + if (ticks_remaining > 0) + owner.add_chemical_effect(CE_PAINKILLER, 160) // About twice as strong as tramadol at full strength + owner.add_chemical_effect(CE_SPEEDBOOST, 1) + owner.add_chemical_effect(CE_STIMULANT, 4) + ticks_remaining -= 1 + if (ticks_remaining == 15) + to_chat(owner, SPAN_WARNING("We feel our adrenal sacs beginning to exhaust themselves. If we do not retreat soon, we will be left vulnerable.")) + if (!ticks_remaining) // ...but comes at a price. Brief short term benefit for a long-term comedown + to_chat(owner, SPAN_WARNING("We feel our body grow weak and sluggish, our adrenal sacs depleted.")) + owner.drowsyness = max(owner.drowsyness, 15) + owner.set_confused(15) + owner.slurring = max(owner.slurring, 30) + owner.chem_effects[CE_PAINKILLER] = 0 + owner.stamina = 0 + if(MOVING_QUICKLY(owner)) + owner.set_moving_slowly() diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 474811ab4261b..600fa4c9db4bc 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -7,6 +7,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/under/chameleon/Initialize() + . = ..() + set_extension(src,/datum/extension/chameleon/clothing/under, /obj/item/clothing/under) + /obj/item/clothing/head/chameleon name = "cap" icon_state = "greysoft" @@ -15,6 +19,11 @@ body_parts_covered = 0 item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/head/chameleon/Initialize() + . = ..() + + set_extension(src, /datum/extension/chameleon/clothing/head,/obj/item/clothing/head) + /obj/item/clothing/suit/chameleon name = "armor" icon_state = "armor" @@ -23,6 +32,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/suit/chameleon/Initialize() + . = ..() + set_extension(src, /datum/extension/chameleon/clothing/suit,/obj/item/clothing/suit) + /obj/item/clothing/shoes/chameleon name = "shoes" icon_state = "black" @@ -31,6 +44,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/shoes/chameleon/Initialize() + . = ..() + set_extension(src, /datum/extension/chameleon/clothing/shoes, /obj/item/clothing/shoes) + /obj/item/storage/backpack/chameleon name = "backpack" icon_state = "backpack" @@ -39,6 +56,7 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM + /obj/item/clothing/gloves/chameleon name = "gloves" icon_state = "black" @@ -47,6 +65,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/gloves/chameleon/Initialize() + . = ..() + set_extension(src, /datum/extension/chameleon/clothing/gloves, /obj/item/clothing/gloves) + /obj/item/clothing/mask/chameleon name = "mask" icon_state = "fullgas" @@ -55,6 +77,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/mask/chameleon/Initialize() + . = ..() + set_extension(src, /datum/extension/chameleon/clothing/mask, /obj/item/clothing/mask) + /obj/item/clothing/glasses/chameleon name = "goggles" icon_state = "meson" @@ -63,6 +89,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/glasses/chameleon/Initialize() + . = ..() + set_extension(src, /datum/extension/chameleon/clothing/glasses, /obj/item/clothing/glasses) + /obj/item/device/radio/headset/chameleon name = "radio headset" icon_state = "headset" @@ -79,6 +109,10 @@ origin_tech = list(TECH_ESOTERIC = 3) item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM +/obj/item/clothing/accessory/chameleon/Initialize() + . = ..() + set_extension(src,/datum/extension/chameleon/clothing/accessory, /obj/item/clothing/accessory) + /obj/item/gun/energy/chameleon name = "chameleon gun" desc = "A hologram projector in the shape of a gun. There is a dial on the side to change the gun's disguise." diff --git a/code/modules/clothing/under/accessories/_accessory.dm b/code/modules/clothing/under/accessories/_accessory.dm index 628320f5f7ef1..2c98c7c7d559d 100644 --- a/code/modules/clothing/under/accessories/_accessory.dm +++ b/code/modules/clothing/under/accessories/_accessory.dm @@ -84,6 +84,9 @@ //when user attached an accessory to S /obj/item/clothing/accessory/proc/on_attached(obj/item/clothing/S, mob/user) + if(istype(src,/obj/item/clothing/accessory/chameleon/changeling)) + if(is_type_in_list(S,changeling_fabricated_clothing)) + return if(!istype(S)) return parent = S diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index 29134484954ce..315e5896f1b7e 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -23,11 +23,15 @@ /mob/living/carbon/human/proc/change_gender(gender) if(src.gender == gender) return - src.gender = gender + reset_hair() + force_update_limbs() update_body() update_dna() + src.sync_organ_dna() + if(src.gender == MALE) + src.UpdateAppearance() return 1 /mob/living/carbon/human/proc/randomize_gender() diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 9349e5bcdff45..a6d6fe3b79745 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -87,6 +87,11 @@ update_body(1) return +/mob/living/carbon/human/proc/Drain() + ChangeToHusk() + mutations |= MUTATION_HUSK + return + /mob/living/carbon/human/proc/ChangeToSkeleton() if(MUTATION_SKELETON in src.mutations) return diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 35317c9bab2ef..356e64e6b906c 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -348,6 +348,11 @@ var/show_descs = show_descriptors_to(user) if(show_descs) msg += SPAN_NOTICE("[jointext(show_descs, "
")]") + + var/age_diff = species.get_age_comparison_string(src, user) + if (age_diff) + msg += "
[SPAN_NOTICE(age_diff)]" + to_chat(user, SPAN_INFO(jointext(msg, null))) //Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 604313ccf91e6..c32aeef4f2a36 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -119,6 +119,11 @@ If you have any questions/constructive-comments/bugs-to-report/or have a massivl Please contact me on #coderbus IRC. ~Carn x */ +//Add an entry to overlays, assuming it exists +/mob/living/carbon/human/proc/apply_layer(cache_index) + if((. = overlays_standing[cache_index])) + overlays.Add(.) + //Human Overlays Indexes///////// #define HO_BODY_LAYER 1 #define HO_MUTATIONS_LAYER 2 @@ -147,7 +152,8 @@ Please contact me on #coderbus IRC. ~Carn x #define HO_L_HAND_LAYER 25 #define HO_R_HAND_LAYER 26 #define HO_FIRE_LAYER 27 //If you're on fire -#define TOTAL_LAYERS 28 +#define HO_EFFECTS_LAYER 28 +#define TOTAL_LAYERS 29 ////////////////////////////////// /mob/living/carbon/human @@ -328,6 +334,7 @@ var/global/list/damage_icon_parts = list() var/list/icon_render_keys = list() /mob/living/carbon/human/proc/update_body(update_icons=1) + //Update all limbs and visible organs one by one var/list/needs_update = list() var/limb_count_update = FALSE @@ -766,7 +773,6 @@ var/global/list/damage_icon_parts = list() if(update_icons) queue_icon_update() - /mob/living/carbon/human/update_fire(update_icons=1) overlays_standing[HO_FIRE_LAYER] = null if(on_fire) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 95787852e45e7..844bcc992e4ac 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -11,7 +11,6 @@ if(machine && !CanMouseDrop(machine, src)) machine = null - //Handle temperature/pressure differences between body and environment var/datum/gas_mixture/environment = loc.return_air() if(environment) @@ -194,12 +193,21 @@ /mob/living/proc/update_sight() if(stat == DEAD || eyeobj) update_dead_sight() - else - update_living_sight() + if (seedarkness) + set_sight(0) + set_see_in_dark(0) + if(stat == DEAD || eyeobj) + update_dead_sight() + else + update_living_sight() - var/list/vision = get_accumulated_vision_handlers() - set_sight(sight | vision[1]) - set_see_invisible(max(vision[2], see_invisible)) + var/list/vision = get_accumulated_vision_handlers() + set_sight(sight | vision[1]) + set_see_invisible(max(vision[2], see_invisible)) + + else + set_see_in_dark(8) + set_see_invisible(SEE_INVISIBLE_NOLIGHTING) /mob/living/proc/update_living_sight() var/set_sight_flags = sight & ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) diff --git a/code/modules/mob/living/maneuvers/maneuver_leap.dm b/code/modules/mob/living/maneuvers/maneuver_leap.dm index 8f785dde54418..815e377cb4c39 100644 --- a/code/modules/mob/living/maneuvers/maneuver_leap.dm +++ b/code/modules/mob/living/maneuvers/maneuver_leap.dm @@ -81,3 +81,19 @@ animate(pixel_z = user.default_pixel_z, time = 3, easing = SINE_EASING | EASE_OUT) user.throw_at(get_turf(target), strength, 1, user, FALSE, new Callback(src, TYPE_PROC_REF(/singleton/maneuver/leap, end_leap), user, target)) addtimer(new Callback(user, TYPE_PROC_REF(/mob/living, jump_layer_shift_end)), 4.5) + +/singleton/maneuver/leap/changeling + name = "vigorous leap" + stamina_cost = 20 + reflexive_modifier = 0.5 + cooldown = 5 SECONDS + delay = 0 SECONDS + +/singleton/maneuver/leap/changeling/end_leap(mob/living/user, atom/target) + . = ..() + if(ishuman(user) && !user.lying && ismob(target) && user.Adjacent(target)) + if(istype(user.get_active_hand(),/obj/item/melee/changeling)) + target.use_weapon(user.get_active_hand(),user) + if(istype(user.get_inactive_hand(), /obj/item/melee/changeling)) + user.swap_hand() + target.use_weapon(user.get_active_hand(),user) \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 11549b6583b87..4b99ee3c9e197 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -153,6 +153,9 @@ var/voice_name = "unidentifiable voice" + // To check if mobs can see shadows or not. By default they can. + var/seedarkness = 1 + var/faction = MOB_FACTION_NEUTRAL //Used for checking whether hostile simple animals will attack you, possibly more stuff later var/last_faction = MOB_FACTION_NEUTRAL var/blinded = null diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index 89e89882922a6..325c6d2ab01e2 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -30,7 +30,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images var/admin_ghosted = 0 var/anonsay = 0 var/ghostvision = 1 //is the ghost able to see things humans can't? - var/seedarkness = 1 + seedarkness = 1 var/obj/item/device/multitool/ghost_multitool var/list/hud_images // A list of hud images diff --git a/code/modules/mob/skills/skill.dm b/code/modules/mob/skills/skill.dm index 8ed4c35cc6b7b..807d8a18aba6f 100644 --- a/code/modules/mob/skills/skill.dm +++ b/code/modules/mob/skills/skill.dm @@ -343,6 +343,6 @@ GLOBAL_LIST_EMPTY(skills) desc = "Virology requires a specialist to have not only a deep knowledge of viruses, but also the ability to work with them, similar to how a chemist works with chemicals. This means not only understanding chemical reactions and their effects, but also knowing how these reactions will affect the human body. Thus, a virologist must have not only knowledge of virology, but also medical skills. An analogy can be drawn with a medical chemist, who must have experience working with chemicals and understand what effect they will have in order to use them safely in medicine." levels = list( "Unskilled" = "You know that virologist work with viruses; you know that they can be very dangerous. You probably know basic defence against viruses..", "Trained" = "You can engeener viruses. You have some training in safety and you won't infect yourself while work... probably. You can almost safely use the virologist equipment.") - prerequisites = list(SKILL_CHEMISTRY = SKILL_TRAINED) + prerequisites = list(SKILL_CHEMISTRY = SKILL_BASIC) default_max = SKILL_BASIC - difficulty = SKILL_AVERAGE + difficulty = SKILL_EXPERIENCED diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index d5876e2fbf631..88e22de9f3668 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -242,6 +242,7 @@ SPAN_NOTICE("\The [src]'s suit whirrs loudly as \the [rig] absorbs the fall!"), SPAN_NOTICE("You hear an electric *whirr* right after the slam!") ) + if (fall_damage()) for (var/mob/living/M in landing.contents) if (M == src) @@ -263,7 +264,8 @@ /mob/living/carbon/human/handle_fall_effect(turf/landing) if(species && species.handle_fall_special(src, landing)) return - + if(src.mind && src.mind.changeling && src.mind.changeling.tendons_reinforced) + return var/obj/item/rig/rig = get_rig() if (istype(rig)) for (var/obj/item/rig_module/actuators/A in rig.installed_modules) diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index e5c33df72149b..fc69b33afa8d5 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -429,7 +429,7 @@ This function completely restores a damaged organ to perfect condition. // remove embedded objects and drop them on the floor for(var/obj/implanted_object in implants) - if(!istype(implanted_object,/obj/item/implant)) // We don't want to remove REAL implants. Just shrapnel etc. + if(!istype(implanted_object,/obj/item/implant,) || istype(implanted_object,/obj/item/organ/internal/augment)) // We don't want to remove REAL implants. Just shrapnel etc. implanted_object.forceMove(get_turf(src)) implants -= implanted_object diff --git a/code/modules/organs/external/_external_damage.dm b/code/modules/organs/external/_external_damage.dm index b8a0be3f1831b..86cf857db14b1 100644 --- a/code/modules/organs/external/_external_damage.dm +++ b/code/modules/organs/external/_external_damage.dm @@ -10,6 +10,7 @@ take_external_damage(amount) /obj/item/organ/external/proc/take_external_damage(brute, burn, damage_flags, used_weapon = null) + brute = round(brute * get_brute_mod(damage_flags), 0.1) burn = round(burn * get_burn_mod(damage_flags), 0.1) diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm index 54757fc8ecc89..07e27811e8c4f 100644 --- a/code/modules/organs/internal/lungs.dm +++ b/code/modules/organs/internal/lungs.dm @@ -29,7 +29,7 @@ var/breathing = 0 var/last_successful_breath var/breath_fail_ratio // How badly they failed a breath. Higher is worse. - + var/mnoBreath = 100 /obj/item/organ/internal/lungs/proc/can_drown() return (is_broken() || !has_gills) @@ -80,7 +80,7 @@ if(prob(5)) owner.emote("cough") //respitory tract infection - if(is_bruised() && !owner.is_asystole()) + if(is_bruised() && !owner.is_asystole() && !(mnoBreath in owner.mutations)) if(prob(2)) if(active_breathing) owner.visible_message( diff --git a/code/modules/overmap/ships/beacon.dm b/code/modules/overmap/ships/beacon.dm index c23d6b79580e9..40940f7635063 100644 --- a/code/modules/overmap/ships/beacon.dm +++ b/code/modules/overmap/ships/beacon.dm @@ -11,6 +11,7 @@ construct_state = /singleton/machine_construction/default/panel_closed var/obj/overmap/radio/signal var/obj/overmap/radio/distress/emergency_signal + var/obj/item/device/gps/gps /// Integer. The `world.time` value of the last distress broadcast. var/last_message_time = 0 /// Integer. The `world.time` of the last activation toggle. @@ -37,6 +38,10 @@ ) +/obj/machinery/radio_beacon/Initialize() + . = ..() + gps = new(src) + /obj/machinery/radio_beacon/interface_interact(mob/user, skip_time_check = FALSE) if (!CanInteract(user, DefaultTopicState())) return @@ -84,6 +89,12 @@ if(!message) return + var/gps_name = sanitize(input("What should its GCS signal be named?", "[gps.gps_tag] - GCS Tag", gps.gps_tag) as text|null) + if (gps_name && gps_name != gps.gps_tag) + gps.gps_tag = uppertext(copytext(gps_name, 1, 11)) + if (!gps.tracking) + gps.toggle_tracking(silent=TRUE) + visible_message(SPAN_NOTICE("\The [src] whirrs to life, starting its radio broadcast.")) playsound(src, 'sound/machines/sensors/newcontact.ogg', 50, 3, 3) @@ -113,6 +124,10 @@ emergency_signal.set_origin(O) + gps?.gps_tag = "SOS" + if (!gps?.tracking) + gps.toggle_tracking(silent=TRUE) + update_use_power(POWER_USE_ACTIVE) update_icon() @@ -127,6 +142,9 @@ last_activation_time = world.time + if (gps?.tracking) + gps.toggle_tracking(silent=TRUE) + update_use_power(POWER_USE_OFF) update_icon() @@ -154,6 +172,7 @@ /obj/machinery/radio_beacon/Destroy() QDEL_NULL(signal) QDEL_NULL(emergency_signal) + QDEL_NULL(gps) . = ..() /obj/overmap/radio diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 8f978646c3938..900360b01ba61 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -119,6 +119,10 @@ #define TURBGENG 0.8 /obj/machinery/power/turbine/Process() +//[SIERRA-ADD] + if(!compressor) + return +//[SIERRA-ADD] Мод делать ради двух строк не хочу if(!compressor.starter) return ClearOverlays() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index fe797006d15e2..7ce951d91ed88 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -164,7 +164,11 @@ return 0 if(!user.IsAdvancedToolUser()) return 0 - + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit,/obj/item/clothing/suit/space/changeling/armored) && !istype(src,/obj/item/gun/projectile/changeling)) + to_chat(user,SPAN_WARNING("This form is too bulky to make use of the trigger guard!")) + return FALSE var/mob/living/M = user if(!safety() && world.time > last_safety_check + 5 MINUTES && !user.skill_check(SKILL_WEAPONS, SKILL_BASIC)) if(prob(30)) diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 2321283af05ce..89130ca6e809e 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -36,7 +36,7 @@ var/mag_insert_sound = 'sound/weapons/guns/interaction/pistol_magin.ogg' var/mag_remove_sound = 'sound/weapons/guns/interaction/pistol_magout.ogg' var/can_special_reload = TRUE //Whether or not we can tactical/speed reload - + var/can_reload = TRUE var/is_jammed = 0 //Whether this gun is jammed var/jam_chance = 0 //Chance it jams on fire //TODO generalize ammo icon states for guns @@ -134,6 +134,8 @@ //Attempts to load A into src, depending on the type of thing being loaded and the load_method //Maybe this should be broken up into separate procs for each load method? /obj/item/gun/projectile/proc/load_ammo(obj/item/A, mob/user) + if(!can_reload) + return if(istype(A, /obj/item/ammo_magazine)) . = TRUE var/obj/item/ammo_magazine/AM = A @@ -255,6 +257,8 @@ //attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled /obj/item/gun/projectile/proc/unload_ammo(mob/user, allow_dump=1) + if(!can_reload) + return if(is_jammed) user.visible_message("\The [user] begins to unjam [src].", "You clear the jam and unload [src]") if(!do_after(user, 0.4 SECONDS, src, DO_DEFAULT | DO_BOTH_UNIQUE_ACT)) diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index dc9ab272f5e55..d9aa5435a9c36 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -419,7 +419,6 @@ The slots that you can use are found in items_clothing.dm and are the inventory warning("[O.type] has a default organ tag \"[O.organ_tag]\" that differs from the species' organ tag \"[organ_tag]\". Updating organ_tag to match.") O.organ_tag = organ_tag H.internal_organs_by_name[organ_tag] = O - for(var/name in H.organs_by_name) H.organs |= H.organs_by_name[name] @@ -429,7 +428,6 @@ The slots that you can use are found in items_clothing.dm and are the inventory for(var/obj/item/organ/O in (H.organs|H.internal_organs)) O.owner = H post_organ_rejuvenate(O, H) - H.sync_organ_dna() /* ------------------------> code\modules\emotes\definitions\_species.dm (У нас всё в моде в mods\emote_panel\code\species.dm) /datum/species/proc/hug(mob/living/carbon/human/H, mob/living/target) diff --git a/code/modules/species/species_age_comparison.dm b/code/modules/species/species_age_comparison.dm new file mode 100644 index 0000000000000..02bd5bce7529c --- /dev/null +++ b/code/modules/species/species_age_comparison.dm @@ -0,0 +1,63 @@ +/// List (string => integer). Map of descriptive words to the age threshhold to display these words. Age threshhold is the _highest_ age that can use this descriptor. Descriptors should make grammatical sense when used in the following sentence: "They are of [X] age for a [species]." +/datum/species/var/list/age_descriptors = list( + "very young" = 20, + "young" = 40, + "moderate" = 60, + "old" = 80, + "very old" = 100, + "ancient" = INFINITY +) + + +/// List (string => integer). Map of descriptive words to the age different threshhold to display these words. Age difference threshhold is the _highest_ number that can use this descriptor. Descriptors should make grammatical sence when used in the following sentence: "They are [X] you." +/datum/species/var/list/age_diff_descriptors = list( + "much younger than" = -15, + "younger than" = -10, + "slightly younger than" = -5, + "around the same age as" = 5, + "slightly older than" = 10, + "older than" = 15, + "much older than" = INFINITY +) + + +/// Boolean. If set, allows other species to see age descriptions. +/datum/species/var/show_age_to_other_species = FALSE + + +/datum/species/proc/get_age_comparison_string(mob/living/carbon/human/observed, mob/living/carbon/human/observer) + if (!age_descriptors || !age_diff_descriptors || !istype(observed) || observed.isSynthetic()) + return + if (!show_age_to_other_species && name != observer.get_species()) + return + + var/observed_real_age = observed.age + observed.changed_age + var/observer_real_age = observer.age + observer.changed_age + var/age_diff + var/age_percentile = round((observed_real_age / max_age) * 100) + var/age_descriptor + var/age_diff_descriptor + + for (var/descriptor in age_descriptors) + if (age_percentile > age_descriptors[descriptor]) + continue + age_descriptor = descriptor + break + if (!age_descriptor) + age_descriptor = age_descriptors[length(age_descriptors)] + + if (istype(observer) && observed.get_species() == observer.get_species() && !observer.isSynthetic()) + age_diff = observed_real_age - observer_real_age + for (var/descriptor in age_diff_descriptors) + if (age_diff > age_diff_descriptors[descriptor]) + continue + age_diff_descriptor = descriptor + break + if (!age_diff_descriptor) + age_diff_descriptor = age_diff_descriptors[length(age_diff_descriptors)] + + var/datum/pronouns/pronouns = observed.choose_from_pronouns() + if (age_diff_descriptor) + return "[pronouns.He] [pronouns.is] of [age_descriptor] age for a [name], [age_diff_descriptor] you." + else if (age_descriptor) + return "[pronouns.He] [pronouns.is] of [age_descriptor] age for a [name]." diff --git a/code/modules/species/station/lizard.dm b/code/modules/species/station/lizard.dm index 09e0c98b5bb66..1a80f22c9f552 100644 --- a/code/modules/species/station/lizard.dm +++ b/code/modules/species/station/lizard.dm @@ -170,6 +170,8 @@ /obj/item/clothing = /obj/decal/cleanable/blood/tracks/claw // Needs to apply to both shoes and space suits. ) + show_age_to_other_species = TRUE + /datum/species/unathi/equip_survival_gear(mob/living/carbon/human/H) ..() H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H),slot_shoes) diff --git a/code/modules/species/station/monkey.dm b/code/modules/species/station/monkey.dm index 126cfdded4b2b..c74f5635b1d15 100644 --- a/code/modules/species/station/monkey.dm +++ b/code/modules/species/station/monkey.dm @@ -57,6 +57,8 @@ ingest_amount = 6 + show_age_to_other_species = TRUE + /datum/species/monkey/New() equip_adjust = list( slot_l_hand_str = list("[NORTH]" = list("x" = 1, "y" = 3), "[EAST]" = list("x" = -3, "y" = 2), "[SOUTH]" = list("x" = -1, "y" = 3), "[WEST]" = list("x" = 3, "y" = 2)), diff --git a/code/modules/species/station/station.dm b/code/modules/species/station/station.dm index c76d964a1e3fe..a1b28e47afac3 100644 --- a/code/modules/species/station/station.dm +++ b/code/modules/species/station/station.dm @@ -81,6 +81,8 @@ /singleton/emote/exertion/synthetic/creak ) + show_age_to_other_species = TRUE + /datum/species/human/get_bodytype(mob/living/carbon/human/H) return SPECIES_HUMAN diff --git a/html/changelog.html b/html/changelog.html index cf48de4b84bd5..b62714f1a63cd 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -28,6 +28,83 @@

Sierra SS13

-->
+

20.12 - 2024

+

Обновления Builder13:

+
+
Добавлены изменения генокрадов с оффов
+
Фикс подключения телепада к консоли через мультитул
+
+

Обновления DieSiege:

+
+
Новая окрашиваемая кепка в лоадаут
+
+

Обновления Lexanx:

+
+
Различные фиксы вирусологии
+
Скиллпоинты ИПС, Адхерантов и Дион теперь не зависят от возраста
+
+

Обновления SierraKomodo:

+
+
Examining other characters can now give you a rough approximation of their age, and the relation of that age to you. Cross-species examinations are less precise, don't include a comparison to your own age, and are only visible for certain species.
+
+ +

17.12 - 2024

+

Обновления Baneuus:

+
+
Теперь у пальто ГСБ новая текстурка.
+
+

Обновления Builder13:

+
+
Звук окончательного оповещения пси сигнала теперь также слышен на всех уровнях, как и звук стартового оповещения.
+
+

Обновления Lexanx:

+
+
Пофиксил баг
+
+

Обновления Teteshnik:

+
+
Пофиксил вендор сигарет в либерии.
+
Добавил новый спрайт для MA21.
+
Вернул возможность стать ППТ воксом.
+
Добавил новые значки в лодаут.
+
+ +

15.12 - 2024

+

Обновления Shegar:

+
+
Временное отключение всех аномалий
+
+ +

12.12 - 2024

+

Обновления DieSiege:

+
+
Цюрих необязателен для планет с инфинити.
+
+ +

10.12 - 2024

+

Обновления Baneuus:

+
+
Жители Кьюига теперь знают упрощённый скреллианский вместо обычного.
+
Теперь Синта и Йоза унати понимают друг друга с шансом в 80 процентов вместо 20.
+
Добавил упрощённые языки Унати, Скреллов и Таяран.
+
+ +

09.12 - 2024

+

Обновления Builder13:

+
+
Портированы префы на музыку: Play jukeboxes and boomboxes и Play music players
+
+

Обновления RocheHendson:

+
+
Заменил outlet на chute в карго почте
+
+ +

07.12 - 2024

+

Обновления Sbotkin:

+
+
Removed the cyborgification contract from the report editor. It's illegal, btw.
+
+

05.12 - 2024

Обновления Banditoz:

@@ -701,43 +778,6 @@

Обновления Builder13:

Возвращены OOC заметки персонажа. Их можно настроить через кнопку Set OOC notes рядом с кнопками флавора. Заполненные OOC заметки можно смотреть через examine персонажа
- -

14.10 - 2024

-

Обновления Lexanx:

-
-
Добавлена возможность сохранять сканы с бодисканнера.
-
Добавлен новый тип файлов. BSC - bodyscan
-
-

Обновления Neonvolt:

-
-
Исправлен доступ дрона Восхождения
-
Множественные исправления пропавших спрайтов Восхождения
-
Добавлены сканеры аномалий для различных ИКСов Восхождения
-
Возвращены авей-корабли Восхождения
-
- -

06.10 - 2024

-

Обновления Builder13:

-
-
Фикс вставки кристаллов в консоль телесайнса
-
Возврат старой скорости перемещения для Адхерантов
-
Паник кнопка добавлена на корабли
-
Возвращено дыхание под водой для Тритонцев
-
Возвращена вкладка size converter для принтера интегральных плат
-
-

Обновления Lexanx:

-
-
Пофиксил баг аутопсии и фикс некорректного описания и пропавшей подкожной брони из робо
-
Добавил в игру бирку для переименования симлмобов
-
-

Обновления awkardlyconfusedneuralnetwork:

-
-
fixed this instant headgib exploit that dumb as hell
-
-

Обновления awkardlyconfusedneuralnetwork, out-of-phaze:

-
-
Makes observ cleanup tracking use datum vars instead of a list
-
Icons by Icons8
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 9b7c1b6340513..a3e6c6d259ae1 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -26549,3 +26549,95 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY sierra_genchangelog.p 2024-12-05: Banditoz: - rscadd: Borg module activations are now announced to their department. +2024-12-07: + Sbotkin: + - rscdel: Removed the cyborgification contract from the report editor. It's illegal, + btw. +2024-12-09: + Builder13: + - rscadd: "\u041F\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u044B \u043F\ + \u0440\u0435\u0444\u044B \u043D\u0430 \u043C\u0443\u0437\u044B\u043A\u0443:\ + \ Play jukeboxes and boomboxes \u0438 Play music players" + RocheHendson: + - bugfix: "\u0417\u0430\u043C\u0435\u043D\u0438\u043B outlet \u043D\u0430 chute\ + \ \u0432 \u043A\u0430\u0440\u0433\u043E \u043F\u043E\u0447\u0442\u0435" +2024-12-10: + Baneuus: + - tweak: "\u0416\u0438\u0442\u0435\u043B\u0438 \u041A\u044C\u044E\u0438\u0433\u0430\ + \ \u0442\u0435\u043F\u0435\u0440\u044C \u0437\u043D\u0430\u044E\u0442 \u0443\ + \u043F\u0440\u043E\u0449\u0451\u043D\u043D\u044B\u0439 \u0441\u043A\u0440\u0435\ + \u043B\u043B\u0438\u0430\u043D\u0441\u043A\u0438\u0439 \u0432\u043C\u0435\u0441\ + \u0442\u043E \u043E\u0431\u044B\u0447\u043D\u043E\u0433\u043E." + - tweak: "\u0422\u0435\u043F\u0435\u0440\u044C \u0421\u0438\u043D\u0442\u0430 \u0438\ + \ \u0419\u043E\u0437\u0430 \u0443\u043D\u0430\u0442\u0438 \u043F\u043E\u043D\ + \u0438\u043C\u0430\u044E\u0442 \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\ + \u0430 \u0441 \u0448\u0430\u043D\u0441\u043E\u043C \u0432 80 \u043F\u0440\u043E\ + \u0446\u0435\u043D\u0442\u043E\u0432 \u0432\u043C\u0435\u0441\u0442\u043E 20." + - rscadd: "\u0414\u043E\u0431\u0430\u0432\u0438\u043B \u0443\u043F\u0440\u043E\u0449\ + \u0451\u043D\u043D\u044B\u0435 \u044F\u0437\u044B\u043A\u0438 \u0423\u043D\u0430\ + \u0442\u0438, \u0421\u043A\u0440\u0435\u043B\u043B\u043E\u0432 \u0438 \u0422\ + \u0430\u044F\u0440\u0430\u043D." +2024-12-12: + DieSiege: + - tweak: "\u0426\u044E\u0440\u0438\u0445 \u043D\u0435\u043E\u0431\u044F\u0437\u0430\ + \u0442\u0435\u043B\u0435\u043D \u0434\u043B\u044F \u043F\u043B\u0430\u043D\u0435\ + \u0442 \u0441 \u0438\u043D\u0444\u0438\u043D\u0438\u0442\u0438." +2024-12-15: + Shegar: + - experiment: "\u0412\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0435 \u043E\u0442\ + \u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u0432\u0441\u0435\u0445 \u0430\ + \u043D\u043E\u043C\u0430\u043B\u0438\u0439" +2024-12-17: + Baneuus: + - imageadd: "\u0422\u0435\u043F\u0435\u0440\u044C \u0443 \u043F\u0430\u043B\u044C\ + \u0442\u043E \u0413\u0421\u0411 \u043D\u043E\u0432\u0430\u044F \u0442\u0435\u043A\ + \u0441\u0442\u0443\u0440\u043A\u0430." + Builder13: + - tweak: "\u0417\u0432\u0443\u043A \u043E\u043A\u043E\u043D\u0447\u0430\u0442\u0435\ + \u043B\u044C\u043D\u043E\u0433\u043E \u043E\u043F\u043E\u0432\u0435\u0449\u0435\ + \u043D\u0438\u044F \u043F\u0441\u0438 \u0441\u0438\u0433\u043D\u0430\u043B\u0430\ + \ \u0442\u0435\u043F\u0435\u0440\u044C \u0442\u0430\u043A\u0436\u0435 \u0441\ + \u043B\u044B\u0448\u0435\u043D \u043D\u0430 \u0432\u0441\u0435\u0445 \u0443\u0440\ + \u043E\u0432\u043D\u044F\u0445, \u043A\u0430\u043A \u0438 \u0437\u0432\u0443\ + \u043A \u0441\u0442\u0430\u0440\u0442\u043E\u0432\u043E\u0433\u043E \u043E\u043F\ + \u043E\u0432\u0435\u0449\u0435\u043D\u0438\u044F." + Lexanx: + - bugfix: "\u041F\u043E\u0444\u0438\u043A\u0441\u0438\u043B \u0431\u0430\u0433" + Teteshnik: + - bugfix: "\u041F\u043E\u0444\u0438\u043A\u0441\u0438\u043B \u0432\u0435\u043D\u0434\ + \u043E\u0440 \u0441\u0438\u0433\u0430\u0440\u0435\u0442 \u0432 \u043B\u0438\u0431\ + \u0435\u0440\u0438\u0438." + - imageadd: "\u0414\u043E\u0431\u0430\u0432\u0438\u043B \u043D\u043E\u0432\u044B\ + \u0439 \u0441\u043F\u0440\u0430\u0439\u0442 \u0434\u043B\u044F MA21." + - tweak: "\u0412\u0435\u0440\u043D\u0443\u043B \u0432\u043E\u0437\u043C\u043E\u0436\ + \u043D\u043E\u0441\u0442\u044C \u0441\u0442\u0430\u0442\u044C \u041F\u041F\u0422\ + \ \u0432\u043E\u043A\u0441\u043E\u043C." + - rscadd: "\u0414\u043E\u0431\u0430\u0432\u0438\u043B \u043D\u043E\u0432\u044B\u0435\ + \ \u0437\u043D\u0430\u0447\u043A\u0438 \u0432 \u043B\u043E\u0434\u0430\u0443\ + \u0442." +2024-12-20: + Builder13: + - rscadd: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0438\u0437\u043C\ + \u0435\u043D\u0435\u043D\u0438\u044F \u0433\u0435\u043D\u043E\u043A\u0440\u0430\ + \u0434\u043E\u0432 \u0441 \u043E\u0444\u0444\u043E\u0432" + - bugfix: "\u0424\u0438\u043A\u0441 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\ + \u043D\u0438\u044F \u0442\u0435\u043B\u0435\u043F\u0430\u0434\u0430 \u043A \u043A\ + \u043E\u043D\u0441\u043E\u043B\u0438 \u0447\u0435\u0440\u0435\u0437 \u043C\u0443\ + \u043B\u044C\u0442\u0438\u0442\u0443\u043B" + DieSiege: + - imageadd: "\u041D\u043E\u0432\u0430\u044F \u043E\u043A\u0440\u0430\u0448\u0438\ + \u0432\u0430\u0435\u043C\u0430\u044F \u043A\u0435\u043F\u043A\u0430 \u0432 \u043B\ + \u043E\u0430\u0434\u0430\u0443\u0442" + Lexanx: + - bugfix: "\u0420\u0430\u0437\u043B\u0438\u0447\u043D\u044B\u0435 \u0444\u0438\u043A\ + \u0441\u044B \u0432\u0438\u0440\u0443\u0441\u043E\u043B\u043E\u0433\u0438\u0438" + - tweak: "\u0421\u043A\u0438\u043B\u043B\u043F\u043E\u0438\u043D\u0442\u044B \u0418\ + \u041F\u0421, \u0410\u0434\u0445\u0435\u0440\u0430\u043D\u0442\u043E\u0432 \u0438\ + \ \u0414\u0438\u043E\u043D \u0442\u0435\u043F\u0435\u0440\u044C \u043D\u0435\ + \ \u0437\u0430\u0432\u0438\u0441\u044F\u0442 \u043E\u0442 \u0432\u043E\u0437\ + \u0440\u0430\u0441\u0442\u0430" + SierraKomodo: + - rscadd: Examining other characters can now give you a rough approximation of their + age, and the relation of that age to you. Cross-species examinations are less + precise, don't include a comparison to your own age, and are only visible for + certain species. diff --git a/html/changelogs/AutoChangeLog-sierra-pr-2931.yml b/html/changelogs/AutoChangeLog-sierra-pr-2931.yml new file mode 100644 index 0000000000000..5f6d946a2f95e --- /dev/null +++ b/html/changelogs/AutoChangeLog-sierra-pr-2931.yml @@ -0,0 +1,5 @@ +author: Merlin1230 +changes: + - {rscadd: Transmission beacons now allow you to set a tag for a GCS signal for + its transmissions. Using the distress mode will just set the tag to SOS.} +delete-after: true diff --git a/icons/mob/modifier_effects.dmi b/icons/mob/modifier_effects.dmi new file mode 100644 index 0000000000000..99a4f6e4ed0d4 Binary files /dev/null and b/icons/mob/modifier_effects.dmi differ diff --git a/icons/mob/onmob/items/lefthand.dmi b/icons/mob/onmob/items/lefthand.dmi index 52eb1962b7f75..363f44c1f6015 100644 Binary files a/icons/mob/onmob/items/lefthand.dmi and b/icons/mob/onmob/items/lefthand.dmi differ diff --git a/icons/mob/onmob/items/lefthand_spacesuits.dmi b/icons/mob/onmob/items/lefthand_spacesuits.dmi index a2a812e1ed62b..2411f534473f9 100644 Binary files a/icons/mob/onmob/items/lefthand_spacesuits.dmi and b/icons/mob/onmob/items/lefthand_spacesuits.dmi differ diff --git a/icons/mob/onmob/items/righthand.dmi b/icons/mob/onmob/items/righthand.dmi index b2d9e57420577..ef5c0ffc0ab61 100644 Binary files a/icons/mob/onmob/items/righthand.dmi and b/icons/mob/onmob/items/righthand.dmi differ diff --git a/icons/mob/onmob/items/righthand_spacesuits.dmi b/icons/mob/onmob/items/righthand_spacesuits.dmi index e0a3f2cd06ae1..9640ec709b3df 100644 Binary files a/icons/mob/onmob/items/righthand_spacesuits.dmi and b/icons/mob/onmob/items/righthand_spacesuits.dmi differ diff --git a/icons/mob/onmob/onmob_belt.dmi b/icons/mob/onmob/onmob_belt.dmi index 9e7941b095e4d..4ab840928e9f3 100644 Binary files a/icons/mob/onmob/onmob_belt.dmi and b/icons/mob/onmob/onmob_belt.dmi differ diff --git a/icons/mob/onmob/onmob_eyes.dmi b/icons/mob/onmob/onmob_eyes.dmi index 47c97701f868a..d49a12a5e0fa2 100644 Binary files a/icons/mob/onmob/onmob_eyes.dmi and b/icons/mob/onmob/onmob_eyes.dmi differ diff --git a/icons/mob/onmob/onmob_feet.dmi b/icons/mob/onmob/onmob_feet.dmi index 954ce5656840e..2754ebb2c31fd 100644 Binary files a/icons/mob/onmob/onmob_feet.dmi and b/icons/mob/onmob/onmob_feet.dmi differ diff --git a/icons/mob/onmob/onmob_hands.dmi b/icons/mob/onmob/onmob_hands.dmi index 43964e3ce8f4a..8adb2586a3f02 100644 Binary files a/icons/mob/onmob/onmob_hands.dmi and b/icons/mob/onmob/onmob_hands.dmi differ diff --git a/icons/mob/onmob/onmob_head.dmi b/icons/mob/onmob/onmob_head.dmi index ab89699f89b6c..d9ccd6d0c834a 100644 Binary files a/icons/mob/onmob/onmob_head.dmi and b/icons/mob/onmob/onmob_head.dmi differ diff --git a/icons/mob/onmob/onmob_mask.dmi b/icons/mob/onmob/onmob_mask.dmi index 26b7ec6ddeda4..44a3b562407ff 100644 Binary files a/icons/mob/onmob/onmob_mask.dmi and b/icons/mob/onmob/onmob_mask.dmi differ diff --git a/icons/mob/onmob/onmob_suit.dmi b/icons/mob/onmob/onmob_suit.dmi index cdd965312a55c..320ee8cf7a664 100644 Binary files a/icons/mob/onmob/onmob_suit.dmi and b/icons/mob/onmob/onmob_suit.dmi differ diff --git a/icons/mob/onmob/onmob_under.dmi b/icons/mob/onmob/onmob_under.dmi index 2cb6b7ddc48de..f39d63695a3c8 100644 Binary files a/icons/mob/onmob/onmob_under.dmi and b/icons/mob/onmob/onmob_under.dmi differ diff --git a/icons/mob/screen_spells.dmi b/icons/mob/screen_spells.dmi index 50be07d787b8c..541f09908c282 100644 Binary files a/icons/mob/screen_spells.dmi and b/icons/mob/screen_spells.dmi differ diff --git a/icons/obj/augment.dmi b/icons/obj/augment.dmi index 48e29f6e018c5..7416d992f9b9d 100644 Binary files a/icons/obj/augment.dmi and b/icons/obj/augment.dmi differ diff --git a/icons/obj/clothing/obj_accessories.dmi b/icons/obj/clothing/obj_accessories.dmi index 9948e18d39640..a1f1b10ed5130 100644 Binary files a/icons/obj/clothing/obj_accessories.dmi and b/icons/obj/clothing/obj_accessories.dmi differ diff --git a/icons/obj/clothing/obj_backpacks.dmi b/icons/obj/clothing/obj_backpacks.dmi index 121b8260e5a3a..0b397695adae9 100644 Binary files a/icons/obj/clothing/obj_backpacks.dmi and b/icons/obj/clothing/obj_backpacks.dmi differ diff --git a/icons/obj/clothing/obj_belt.dmi b/icons/obj/clothing/obj_belt.dmi index cb6487083229e..e47e6491a2af2 100644 Binary files a/icons/obj/clothing/obj_belt.dmi and b/icons/obj/clothing/obj_belt.dmi differ diff --git a/icons/obj/clothing/obj_eyes.dmi b/icons/obj/clothing/obj_eyes.dmi index c746f3a1a661b..5db018dc41f7e 100644 Binary files a/icons/obj/clothing/obj_eyes.dmi and b/icons/obj/clothing/obj_eyes.dmi differ diff --git a/icons/obj/clothing/obj_feet.dmi b/icons/obj/clothing/obj_feet.dmi index 8d29e712d9cd1..e5086830b489d 100644 Binary files a/icons/obj/clothing/obj_feet.dmi and b/icons/obj/clothing/obj_feet.dmi differ diff --git a/icons/obj/clothing/obj_hands.dmi b/icons/obj/clothing/obj_hands.dmi index 09b807a538398..0bdb00ca1c2cd 100644 Binary files a/icons/obj/clothing/obj_hands.dmi and b/icons/obj/clothing/obj_hands.dmi differ diff --git a/icons/obj/clothing/obj_head.dmi b/icons/obj/clothing/obj_head.dmi index 22964cf7c9af4..b6e612c8feaf4 100644 Binary files a/icons/obj/clothing/obj_head.dmi and b/icons/obj/clothing/obj_head.dmi differ diff --git a/icons/obj/clothing/obj_mask.dmi b/icons/obj/clothing/obj_mask.dmi index 168436c1a2538..0bcb6f8cc641f 100644 Binary files a/icons/obj/clothing/obj_mask.dmi and b/icons/obj/clothing/obj_mask.dmi differ diff --git a/icons/obj/clothing/obj_suit.dmi b/icons/obj/clothing/obj_suit.dmi index 95fdc5db3973f..8475fc1a2213e 100644 Binary files a/icons/obj/clothing/obj_suit.dmi and b/icons/obj/clothing/obj_suit.dmi differ diff --git a/icons/obj/clothing/obj_under.dmi b/icons/obj/clothing/obj_under.dmi index f9968bcbaf687..76b6f671b826d 100644 Binary files a/icons/obj/clothing/obj_under.dmi and b/icons/obj/clothing/obj_under.dmi differ diff --git a/icons/obj/guns/dartgun.dmi b/icons/obj/guns/dartgun.dmi index 628a1464c798d..65413149c669b 100644 Binary files a/icons/obj/guns/dartgun.dmi and b/icons/obj/guns/dartgun.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 13ab8d2ac82bd..8fd3c7d33364b 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/icons/obj/weapons/melee_energy.dmi b/icons/obj/weapons/melee_energy.dmi index 4d7e0b4c5e91a..9cd3b3c83022e 100644 Binary files a/icons/obj/weapons/melee_energy.dmi and b/icons/obj/weapons/melee_energy.dmi differ diff --git a/icons/obj/weapons/melee_physical.dmi b/icons/obj/weapons/melee_physical.dmi index bd59c7d83eea6..ccda4b64e103d 100644 Binary files a/icons/obj/weapons/melee_physical.dmi and b/icons/obj/weapons/melee_physical.dmi differ diff --git a/maps/sierra/loadout/loadout_accessories.dm b/maps/sierra/loadout/loadout_accessories.dm index 55d242e41cc8a..f7834a3ba0f1f 100644 --- a/maps/sierra/loadout/loadout_accessories.dm +++ b/maps/sierra/loadout/loadout_accessories.dm @@ -142,3 +142,32 @@ flags = GEAR_HAS_NO_CUSTOMIZATION cost = 0 +/datum/gear/accessory/cross_blue + display_name = "Cross blue" + path = /obj/item/clothing/accessory/cross_blue + flags = GEAR_HAS_NO_CUSTOMIZATION + cost = 2 + +/datum/gear/accessory/cross_red + display_name = "Cross red" + path = /obj/item/clothing/accessory/cross_red + flags = GEAR_HAS_NO_CUSTOMIZATION + cost = 2 + +/datum/gear/accessory/flower_gold + display_name = "Flower gold" + path = /obj/item/clothing/accessory/flower_gold + flags = GEAR_HAS_NO_CUSTOMIZATION + cost = 2 + +/datum/gear/accessory/flower_silver + display_name = "Flower silver" + path = /obj/item/clothing/accessory/flower_silver + flags = GEAR_HAS_NO_CUSTOMIZATION + cost = 2 + +/datum/gear/accessory/flower_bronze + display_name = "Flower bronze" + path = /obj/item/clothing/accessory/flower_bronze + flags = GEAR_HAS_NO_CUSTOMIZATION + cost = 2 diff --git a/maps/sierra/sierra_unit_testing.dm b/maps/sierra/sierra_unit_testing.dm index 16c21059e9c3f..747ed5141f126 100644 --- a/maps/sierra/sierra_unit_testing.dm +++ b/maps/sierra/sierra_unit_testing.dm @@ -106,11 +106,11 @@ name = "ZAS: Supply Shuttle (CentComm)" area_path = /area/supply/dock -/* + /datum/unit_test/zas_area_test/virology name = "ZAS: Virology" area_path = /area/medical/virology -*/ + /datum/unit_test/zas_area_test/xenobio name = "ZAS: Xenobiology" diff --git a/maps/sierra/z1-z5_sierra.dmm b/maps/sierra/z1-z5_sierra.dmm index 76595dccb8fff..e1153a66d8080 100644 --- a/maps/sierra/z1-z5_sierra.dmm +++ b/maps/sierra/z1-z5_sierra.dmm @@ -1619,35 +1619,6 @@ /obj/floor_decal/industrial/hatch/yellow, /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/firstdeck/centralstarboard) -"amo" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/cable/green{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/floor_decal/borderfloor{ - dir = 4 - }, -/obj/floor_decal/industrial/danger{ - dir = 4 - }, -/obj/floor_decal/borderfloor/corner{ - dir = 8 - }, -/obj/floor_decal/corner/lime/bordercorner{ - dir = 8 - }, -/turf/simulated/floor/tiled, -/area/hallway/primary/seconddeck/center) "ams" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 1 @@ -4048,8 +4019,24 @@ /turf/simulated/floor/tiled/white/monotile, /area/medical/sleeper) "aCR" = ( -/turf/simulated/wall/r_wall/hull, -/area/medical/virology) +/obj/machinery/button/blast_door{ + id_tag = "vir_blast_window"; + name = "Virology windows"; + pixel_y = -24 + }, +/obj/floor_decal/borderfloorblack{ + dir = 1 + }, +/obj/floor_decal/corner/green/border{ + dir = 1 + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/turf/simulated/floor/tiled/white, +/area/medical/virology/lab) "aCX" = ( /obj/machinery/flasher{ pixel_x = 32; @@ -5330,18 +5317,29 @@ /turf/simulated/wall/ocp_wall, /area/maintenance/incinerator) "aNR" = ( -/obj/floor_decal/corner/green/diagonal, -/obj/structure/table/glass, /obj/structure/cable/green{ d1 = 1; d2 = 4; icon_state = "1-4" }, -/obj/machinery/light/spot{ - dir = 8 +/obj/floor_decal/borderfloorblack{ + dir = 1 + }, +/obj/floor_decal/corner/green/border{ + dir = 1 + }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" }, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "aNW" = ( /obj/machinery/portable_atmospherics/hydroponics, /obj/machinery/atmospherics/portables_connector{ @@ -16552,10 +16550,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/crew_quarters/sleep/bunk) "cxR" = ( -/obj/structure/disposalpipe/segment{ - dir = 1; - icon_state = "pipe-c" - }, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 1 }, @@ -20084,6 +20078,11 @@ /obj/floor_decal/corner/green/border{ dir = 1 }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 4; + icon_state = "2-4" + }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "cXt" = ( @@ -23232,17 +23231,8 @@ /turf/simulated/floor/plating, /area/hallway/primary/seconddeck/center) "duu" = ( -/obj/structure/disposalpipe/segment{ - dir = 8; - icon_state = "pipe-c" - }, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold4w/hidden/supply, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" - }, /obj/structure/cable/green{ d1 = 2; d2 = 8; @@ -25677,21 +25667,11 @@ /turf/simulated/floor/wood/walnut, /area/crew_quarters/lounge) "dLm" = ( -/obj/machinery/door/firedoor, -/obj/structure/disposalpipe/segment, /obj/floor_decal/corner/green/mono, -/obj/floor_decal/industrial/hatch/yellow, -/obj/machinery/door/airlock/glass/virology, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/item/taperoll/engineering/applied, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" - }, -/turf/simulated/floor/tiled/white/monotile, -/area/medical/virology) +/obj/floor_decal/industrial/outline/yellow, +/obj/machinery/computer/centrifuge, +/turf/simulated/floor/tiled/white, +/area/medical/virology/lab) "dLu" = ( /obj/structure/ladder, /obj/structure/lattice, @@ -25809,9 +25789,6 @@ dir = 8; pixel_x = -24 }, -/obj/machinery/computer/diseasesplicer{ - dir = 4 - }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "dMC" = ( @@ -26708,14 +26685,15 @@ /turf/simulated/floor/wood/mahogany, /area/crew_quarters/sleep/bunk) "dSS" = ( -/obj/structure/disposalpipe/junction{ - dir = 1 - }, /obj/structure/cable/green{ d1 = 1; d2 = 2; icon_state = "1-2" }, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "dSU" = ( @@ -35236,22 +35214,14 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/waterstore) "ffc" = ( -/obj/floor_decal/borderfloorblack{ - dir = 4 - }, -/obj/floor_decal/corner/green/border{ - dir = 4 - }, -/obj/floor_decal/borderfloorblack/corner2{ - dir = 5 +/obj/machinery/disposal, +/obj/floor_decal/corner/green{ + dir = 6 }, +/obj/floor_decal/industrial/hatch/yellow, /obj/structure/disposalpipe/trunk{ dir = 8 }, -/obj/machinery/disposal, -/obj/floor_decal/corner/green/bordercorner2{ - dir = 5 - }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "ffd" = ( @@ -42585,6 +42555,15 @@ /obj/floor_decal/corner/green/bordercorner2{ dir = 8 }, +/obj/machinery/power/apc{ + dir = 8; + name = "west bump"; + pixel_x = -24 + }, +/obj/structure/cable/green{ + d2 = 4; + icon_state = "0-4" + }, /turf/simulated/floor/tiled/white, /area/medical/virology) "gjE" = ( @@ -44431,8 +44410,11 @@ d2 = 8; icon_state = "4-8" }, -/obj/floor_decal/borderfloor{ - dir = 8 +/obj/floor_decal/corner/green/bordercorner{ + dir = 1 + }, +/obj/floor_decal/borderfloor/corner{ + dir = 1 }, /obj/floor_decal/corner/green/bordercorner{ dir = 1 @@ -44870,7 +44852,7 @@ id_tag = "vir_blast_window" }, /turf/simulated/floor/plating, -/area/medical/virology) +/area/medical/virology/lab) "gDw" = ( /obj/structure/table/rack, /obj/machinery/power/apc/super/critical{ @@ -46853,6 +46835,11 @@ d2 = 4; icon_state = "2-4" }, +/obj/structure/cable/green{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, /turf/simulated/floor/tiled/white, /area/medical/virology) "gRR" = ( @@ -47043,25 +47030,22 @@ /turf/simulated/floor/tiled/white/monotile, /area/maintenance/fourthdeck/aft) "gTc" = ( -/obj/floor_decal/corner/green/diagonal, /obj/structure/bed/chair/office/light{ dir = 1 }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 6 +/obj/floor_decal/borderfloorblack{ + dir = 1 }, -/obj/structure/cable/green{ - d1 = 2; - d2 = 4; - icon_state = "2-4" +/obj/floor_decal/corner/green/border{ + dir = 1 }, /obj/structure/cable/green{ - d1 = 2; + d1 = 4; d2 = 8; - icon_state = "2-8" + icon_state = "4-8" }, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "gTl" = ( /turf/simulated/wall/r_wall/hull, /area/maintenance/abandoned_hydroponics) @@ -53934,22 +53918,11 @@ /turf/simulated/floor/tiled, /area/assembly/office) "hVT" = ( -/obj/floor_decal/corner/green/diagonal, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 10 - }, -/obj/structure/disposalpipe/segment{ - dir = 4; - icon_state = "pipe-c" - }, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" - }, +/obj/floor_decal/borderfloorblack, +/obj/floor_decal/corner/green/border, +/obj/machinery/light/spot, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "hWb" = ( /obj/floor_decal/chapel{ dir = 1 @@ -59667,26 +59640,6 @@ }, /turf/simulated/floor/tiled/techfloor, /area/engineering/bluespace) -"iOx" = ( -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/floor_decal/borderfloor{ - dir = 4 - }, -/obj/floor_decal/industrial/danger{ - dir = 4 - }, -/obj/floor_decal/borderfloor/corner{ - dir = 1 - }, -/obj/floor_decal/corner/green/bordercorner{ - dir = 1 - }, -/turf/simulated/floor/tiled, -/area/hallway/primary/seconddeck/center) "iOy" = ( /obj/structure/table/standard, /obj/item/book/manual/ripley_build_and_repair, @@ -60600,10 +60553,6 @@ /turf/simulated/floor/tiled/techfloor, /area/security/sierra/armory/lobby) "iUN" = ( -/obj/floor_decal/corner/green/diagonal, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 9 - }, /obj/structure/hygiene/sink{ dir = 4; pixel_x = 11 @@ -60613,8 +60562,14 @@ d2 = 8; icon_state = "1-8" }, +/obj/floor_decal/borderfloorblack{ + dir = 5 + }, +/obj/floor_decal/corner/green/border{ + dir = 5 + }, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "iUO" = ( /obj/machinery/status_display{ pixel_y = 32 @@ -65782,12 +65737,8 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/multi_tile/maintenance{ - dir = 8; - autoset_access = 0 - }, -/obj/floor_decal/industrial/hatch/yellow, +/obj/floor_decal/borderfloor, +/obj/floor_decal/corner/green/border, /turf/simulated/floor/tiled, /area/hallway/primary/seconddeck/center) "jMp" = ( @@ -79420,6 +79371,7 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 }, +/obj/structure/closet/crate/secure/biohazard, /turf/simulated/floor/tiled/white, /area/medical/virology) "lIc" = ( @@ -82743,9 +82695,6 @@ d2 = 4; icon_state = "2-4" }, -/obj/floor_decal/borderfloor{ - dir = 8 - }, /obj/floor_decal/corner/green/bordercorner, /obj/floor_decal/corner/green/bordercorner{ dir = 8 @@ -83925,13 +83874,6 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/plating, /area/storage/eva) -"msF" = ( -/obj/wallframe_spawn/reinforced/hull, -/obj/machinery/door/blast/regular{ - id_tag = "vir_blast_window" - }, -/turf/simulated/floor/plating, -/area/medical/virology) "msI" = ( /obj/floor_decal/industrial/hatch/yellow, /obj/machinery/self_destruct, @@ -90545,13 +90487,6 @@ /turf/simulated/floor/plating, /area/maintenance/seconddeck/foreport) "nrb" = ( -/obj/floor_decal/corner/green/diagonal, -/obj/structure/table/glass, -/obj/machinery/microwave{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/machinery/atmospherics/unary/vent_scrubber/on, /obj/item/device/radio/intercom{ dir = 8; pixel_x = 24 @@ -90561,8 +90496,10 @@ d2 = 2; icon_state = "1-2" }, +/obj/floor_decal/corner/green/mono, +/obj/structure/table/glass, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "nrg" = ( /obj/structure/lattice, /obj/floor_decal/corner/red/border{ @@ -92901,9 +92838,6 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/hallway/primary/seconddeck/aft) "nJm" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -94481,6 +94415,11 @@ d2 = 4; icon_state = "2-4" }, +/obj/structure/cable/green{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "nYD" = ( @@ -107740,9 +107679,6 @@ /turf/simulated/floor/tiled/steel_grid, /area/hallway/primary/thirddeck/fore) "pSP" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -113333,7 +113269,8 @@ dir = 8; pixel_x = 24 }, -/obj/machinery/computer/centrifuge, +/obj/structure/table/glass, +/obj/item/device/scanner/antibody_scanner, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "qLh" = ( @@ -116404,20 +116341,18 @@ /turf/simulated/floor/tiled/monotile, /area/rnd/misc_lab) "rgQ" = ( -/obj/floor_decal/corner/green/diagonal, -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/obj/structure/sign/warning/deathsposal{ - pixel_x = 32 - }, /obj/machinery/alarm{ dir = 8; pixel_x = 24 }, +/obj/floor_decal/borderfloorblack{ + dir = 6 + }, +/obj/floor_decal/corner/green/border{ + dir = 6 + }, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "rgS" = ( /obj/structure/railing/mapped{ dir = 1 @@ -116869,16 +116804,6 @@ }, /turf/simulated/floor/airless, /area/solar/port) -"rjG" = ( -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/machinery/door/firedoor, -/obj/floor_decal/industrial/hatch/yellow, -/turf/simulated/floor/tiled, -/area/hallway/primary/seconddeck/center) "rjN" = ( /obj/floor_decal/industrial/warning/corner{ dir = 4 @@ -117743,22 +117668,17 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/maintenance/thirddeck/port) "rsf" = ( -/obj/floor_decal/corner/green/diagonal, /obj/structure/table/glass, /obj/structure/cable/green{ d2 = 2; icon_state = "0-2" }, -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -24 - }, /obj/item/diseasedisk, /obj/item/diseasedisk, /obj/item/diseasedisk, +/obj/floor_decal/corner/green/mono, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "rsi" = ( /obj/floor_decal/spline/fancy/wood{ dir = 1 @@ -123703,20 +123623,22 @@ /turf/simulated/floor/tiled, /area/hallway/primary/seconddeck/fore) "skh" = ( -/obj/floor_decal/corner/green/diagonal, /obj/structure/table/glass, /obj/item/modular_computer/laptop/preset/records{ dir = 4 }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 - }, /obj/machinery/camera/network/medbay{ c_tag = "Virology - Entrance"; dir = 4 }, +/obj/floor_decal/borderfloorblack{ + dir = 10 + }, +/obj/floor_decal/corner/green/border{ + dir = 10 + }, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "skl" = ( /obj/structure/catwalk, /turf/simulated/floor/plating, @@ -127144,7 +127066,9 @@ dir = 8 }, /obj/machinery/atmospherics/unary/vent_pump/on, -/obj/structure/table/glass, +/obj/machinery/computer/diseasesplicer{ + dir = 4 + }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) "sIr" = ( @@ -127155,16 +127079,15 @@ /area/thruster/d1starboard) "sIs" = ( /obj/floor_decal/borderfloorblack{ - dir = 5 + dir = 1 }, /obj/floor_decal/corner/green/border{ - dir = 5 + dir = 1 }, -/obj/machinery/button/blast_door{ - id_tag = "vir_blast_window"; - name = "Virology windows"; - dir = 8; - pixel_x = 24 +/obj/structure/cable/green{ + d1 = 4; + d2 = 8; + icon_state = "4-8" }, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) @@ -128671,9 +128594,6 @@ /area/maintenance/seconddeck/forestarboard) "sSp" = ( /obj/machinery/door/firedoor, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/floor_decal/corner/green/mono, /obj/floor_decal/industrial/hatch/yellow, /obj/machinery/door/airlock/glass/virology, @@ -136266,7 +136186,6 @@ }, /obj/structure/table/glass, /obj/item/device/scanner/health, -/obj/item/device/scanner/antibody_scanner, /obj/item/storage/fancy/vials, /turf/simulated/floor/tiled/white, /area/medical/virology/lab) @@ -142024,15 +141943,13 @@ /turf/simulated/floor/tiled/white, /area/medical/infirmreception) "uUh" = ( -/obj/floor_decal/corner/green/diagonal, /obj/structure/table/glass, /obj/structure/noticeboard{ pixel_y = 32 }, -/obj/machinery/recharger, -/obj/item/device/scanner/antibody_scanner, +/obj/floor_decal/corner/green/mono, /turf/simulated/floor/tiled/white, -/area/medical/virology) +/area/medical/virology/lab) "uUl" = ( /obj/floor_decal/borderfloorblack{ dir = 8 @@ -160500,23 +160417,17 @@ }, /area/engineering/engine_room) "xIZ" = ( -/obj/structure/disposalpipe/segment, /obj/floor_decal/steeldecal/steel_decals4{ dir = 4 }, /obj/floor_decal/steeldecal/steel_decals4{ dir = 9 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 }, -/obj/structure/cable/green{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 6 }, /turf/simulated/floor/tiled/white, /area/medical/virology) @@ -161702,6 +161613,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/structure/closet/crate/secure/biohazard, /turf/simulated/floor/tiled/techfloor/grid, /area/medical/virology/atmos) "xRB" = ( @@ -225290,8 +225202,8 @@ rtT rtT rtT rtT -idA -tXe +rtT +rtT egk opM kJW @@ -264282,17 +264194,17 @@ idA tXe xxf utn -utn -utn +iqR +iqR qWx qWx -utn +iqR qWx qWx -utn -utn -utn -utn +iqR +iqR +iqR +iqR dBU dBU dBU @@ -264484,11 +264396,11 @@ idA tXe mKZ utn -utn +iqR nlx bFk oie -utn +iqR jHG jRK kkL @@ -264686,7 +264598,7 @@ rtT ibG eME utn -utn +iqR huY xAG eGe @@ -264888,11 +264800,11 @@ aTh tXe hLJ utn -utn -utn +iqR +iqR atf atf -utn +iqR qMl kvF ejr @@ -265094,7 +265006,7 @@ kCD ukG bFk oie -utn +iqR vCG gvQ oFf @@ -265493,12 +265405,12 @@ rtT tXe jDS utn -utn -utn -utn +iqR +iqR +iqR atf atf -utn +iqR vWF oaO oFf @@ -266319,8 +266231,8 @@ xFc ano kDh dBU -iOx -amo +eIl +jMh egj dFc dFc @@ -266502,15 +266414,15 @@ rtT idA tXe hLJ +utn +iqR +dLm aCR -aCR -mHT -mHT -mHT -mHT +iqR +iqR hBO sSp -hBO +mHT mHT mHT mHT @@ -266521,7 +266433,7 @@ dBU dBU dBU dBU -rjG +eIl jMh fhm lNl @@ -266705,11 +266617,11 @@ idA tXe hLJ uaW -msF +kCD rsf aNR skh -hBO +iqR lHV pSP gjj @@ -266906,12 +266818,12 @@ rtT idA tXe fdX -aCR -aCR +utn +iqR uUh gTc hVT -dLm +iqR xIZ duu gRO @@ -267113,7 +267025,7 @@ gDr nrb iUN rgQ -hBO +iqR pbh mtQ qeH @@ -267311,7 +267223,7 @@ idA tXe xxf fNW -fNW +ttc ttc ttc ttc diff --git a/maps/torch/datums/reports/robotics.dm b/maps/torch/datums/reports/robotics.dm deleted file mode 100644 index 681c50827f5cc..0000000000000 --- a/maps/torch/datums/reports/robotics.dm +++ /dev/null @@ -1,20 +0,0 @@ -/datum/computer_file/report/recipient/borging - form_name = "CC-SGF-09" - title = "Cyborgification Contract" - logo = "\[solcrest\]" - available_on_ntnet = TRUE - -/datum/computer_file/report/recipient/borging/generate_fields() - ..() - var/list/xo_fields = list() - add_field(/datum/report_field/text_label/header, "SEV Torch - Office of the Executive Officer") - add_field(/datum/report_field/people/from_manifest, "Name (XO)") - add_field(/datum/report_field/people/from_manifest, "Name (subject)", required = 1) - add_field(/datum/report_field/date, "Date filed") - add_field(/datum/report_field/time, "Time filed") - add_field(/datum/report_field/text_label/instruction, "I, undersigned, hereby agree to willingly undergo a Regulation Lobotimization with intention of cyborgification or AI assimilation, and I am aware of all the consequences of such act. I also understand that this operation may be irreversible, and that my employment contract will be terminated.") - add_field(/datum/report_field/signature, "Subject's signature") - xo_fields += add_field(/datum/report_field/signature, "Executive Officer's signature") - xo_fields += add_field(/datum/report_field/options/yes_no, "Approved") - for(var/datum/report_field/field in xo_fields) - field.set_access(access_edit = access_hop) \ No newline at end of file diff --git a/maps/torch/torch.dm b/maps/torch/torch.dm index a1b5e3e9237ec..593a4dfdeccda 100644 --- a/maps/torch/torch.dm +++ b/maps/torch/torch.dm @@ -44,7 +44,6 @@ #include "datums/reports/corporate.dm" #include "datums/reports/exploration.dm" #include "datums/reports/medical.dm" - #include "datums/reports/robotics.dm" #include "datums/reports/science.dm" #include "datums/reports/security.dm" #include "datums/reports/solgov.dm" diff --git a/mods/_maps/liberia/maps/liberia.dmm b/mods/_maps/liberia/maps/liberia.dmm index b4b8c50d1375b..5eb558e832e66 100644 --- a/mods/_maps/liberia/maps/liberia.dmm +++ b/mods/_maps/liberia/maps/liberia.dmm @@ -4430,7 +4430,7 @@ /obj/machinery/vending/cigarette{ name = "hacked cigarette machine"; prices = list(); - products = list(/obj/item/storage/fancy/cigarettes=10,/obj/item/storage/box/matches=10,/obj/item/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) + products = list(/obj/item/storage/fancy/smokable/luckystars=5,/obj/item/storage/fancy/smokable/professionals=5,/obj/item/flame/lighter/zippo=4,/obj/item/clothing/mask/smokable/cigarette/cigar/havana=2) }, /turf/simulated/floor/wood/walnut, /area/liberia/bar) diff --git a/mods/_master_files/code/modules/psionics/events/mini_spasm.dm b/mods/_master_files/code/modules/psionics/events/mini_spasm.dm index 3ba16a65658a4..5f29a33e6112d 100644 --- a/mods/_master_files/code/modules/psionics/events/mini_spasm.dm +++ b/mods/_master_files/code/modules/psionics/events/mini_spasm.dm @@ -26,8 +26,9 @@ /datum/event/minispasm/end() priority_announcement.Announce( \ "PRIORITY ALERT: SIGNAL BROADCAST HAS CEASED. Personnel are cleared to resume use of non-hardened radio transmission equipment. Have a nice day.", \ - "Cuchulain Sensor Array Automated Message", \ - new_sound = 'packs/infinity/sound/misc/foundation_restore.ogg' ) + "Cuchulain Sensor Array Automated Message") + var/new_sound = 'packs/infinity/sound/misc/foundation_restore.ogg' + sound_to(world, sound(new_sound)) /datum/event/minispasm/do_spasm(mob/living/victim, obj/item/device/radio/source) set waitfor = 0 diff --git a/mods/adherent_discharge/code/adherent.dm b/mods/adherent_discharge/code/adherent.dm index cdeea81675e41..0cadeea6d174a 100644 --- a/mods/adherent_discharge/code/adherent.dm +++ b/mods/adherent_discharge/code/adherent.dm @@ -4,6 +4,9 @@ /obj/item/organ/internal/cell/adherent var/ready_to_charge +/datum/species/adherent/skills_from_age(age) + if(age) + . = 8 /mob/living/carbon/human/proc/toggle_emergency_discharge() set category = "Abilities" diff --git a/mods/anomaly/code/spawn_anomalies_protocol/spawn_on_planet.dm b/mods/anomaly/code/spawn_anomalies_protocol/spawn_on_planet.dm index 57650beba3211..8311a9d05c1c3 100644 --- a/mods/anomaly/code/spawn_anomalies_protocol/spawn_on_planet.dm +++ b/mods/anomaly/code/spawn_anomalies_protocol/spawn_on_planet.dm @@ -1,12 +1,11 @@ /datum/map/build_exoplanets() //Игра заспавнит 1 обычную планету и 1 аномальную var/list/anomaly_planets_list = list( - /obj/overmap/visitable/sector/exoplanet/ice, - /obj/overmap/visitable/sector/exoplanet/volcanic, - /obj/overmap/visitable/sector/exoplanet/flying + // /obj/overmap/visitable/sector/exoplanet/ice, + // /obj/overmap/visitable/sector/exoplanet/volcanic, + // /obj/overmap/visitable/sector/exoplanet/flying ) var/list/all_planets_list = subtypesof(/obj/overmap/visitable/sector/exoplanet) - LAZYREMOVE(all_planets_list, anomaly_planets_list) //Я не придумал как обьяснять игре какая планета обычная, а какая аномальная без //заранее подготовленных списков. Увы. if(!use_overmap) @@ -16,10 +15,11 @@ var/normal_planet_type = pick(all_planets_list) var/obj/overmap/visitable/sector/exoplanet/new_planet = new normal_planet_type(null, world.maxx, world.maxy) new_planet.build_level() - - var/anomaly_planet_type = pick(anomaly_planets_list) - var/obj/overmap/visitable/sector/exoplanet/anomaly_new_planet = new anomaly_planet_type(null, world.maxx, world.maxy) - anomaly_new_planet.build_level() + if(LAZYLEN(anomaly_planets_list)) + LAZYREMOVE(all_planets_list, anomaly_planets_list) + var/anomaly_planet_type = pick(anomaly_planets_list) + var/obj/overmap/visitable/sector/exoplanet/anomaly_new_planet = new anomaly_planet_type(null, world.maxx, world.maxy) + anomaly_new_planet.build_level() //Данный код отвечает за размещение аномалий по всей планете. /obj/overmap/visitable/sector/exoplanet diff --git a/mods/diona/code/station.dm b/mods/diona/code/station.dm index ef4e1aac863f3..7d40874b9fb26 100644 --- a/mods/diona/code/station.dm +++ b/mods/diona/code/station.dm @@ -16,3 +16,7 @@ slowdown = 2 thirst_factor = 0.06 additional_languages = 2 + +/datum/species/diona/skills_from_age(age) + if(age) + . = 16 diff --git a/mods/expanded_culture_descriptor/code/locations/locations_human.dm b/mods/expanded_culture_descriptor/code/locations/locations_human.dm index ca2edf29e05a5..0079a7db3f364 100644 --- a/mods/expanded_culture_descriptor/code/locations/locations_human.dm +++ b/mods/expanded_culture_descriptor/code/locations/locations_human.dm @@ -35,6 +35,7 @@ тёплыми отношениями между Трестеном и ЦПСС, первый имеет несколько отрядов во вооружённых силах ЦПСС." capital = "Терстен-сити" economic_power = 1.0 + secondary_langs = list(LANGUAGE_SIMPUNATHI) /singleton/cultural_info/location/avalon name = HOME_SYSTEM_AVALON @@ -57,7 +58,6 @@ Миранианские отношения с Терранской Конфедерацией сегодня довольно дружелюбные, так как Мирания стремится наладить контакт с независимой от ЦПСС силой, чтобы закрепить свой дипломатический суверенитет. \ Мирания практикует ксенофобскую политику, и недавние экономические трудности привели к тому, что многие из оставшихся обездоленных и безработных обратились к националистическим партиям, которые обещают довести Миранию на первые места и восстановить прежнюю сильную экономику. \ Сегодня Мирания является концентратором науки и технологий на фронтире, особенно в сфере вооружений и кораблестроения." - language = LANGUAGE_HUMAN_EURO capital = "Мирания III" ruling_body = "Федеративная Республика Мирания" distance = "22 световых года" @@ -70,7 +70,6 @@ description = "Бринкбёрн - марсоподобная планета и бывшая корпоративная колония, ныне находящаяся под властью многочисленных банд и преступников. \ Эти банды вымогают деньги местного населения, в качестве формы грубого налогообложения для дальнейшей своей деятельности, и взамен они обеспечивают «защиту» от других банд, наряду с использованием некоторых государственных услуг, которыми они управляют. \ Банда Lumoco Arms and Supplies контролирует большую часть территории планеты и почти всю экономику. Этот беззаконный статус сохранился до настоящего времени и вряд ли можно еще говорить о создании устойчивого правительства." - language = LANGUAGE_HUMAN_EURO capital = "Остакра-Таун" ruling_body = "Никс" distance = "30 световых лет" @@ -85,7 +84,6 @@ Это привлекает людей сомнительного происхождения или обыкновенных граждан, которые не хотят работать ни на одну корпорацию. \ Калдарк получает свою долю от горнодобывающих операций в регионе, по сути являясь одним из крупнейших независимых горнодобывающих и торговых центров не только вокруг Эребуса, но и во всей системе Никс. \ Несмотря на то, что это, технически, республика, она не признается суверенным государством. ЦПСС не может позаботиться о Калдарке, и от некоторых людей можно услышать, что Калдарк является “первой независимой нацией системы Никс“" - language = LANGUAGE_HUMAN_EURO capital = "Калдарк" ruling_body = "Никс" distance = "30 световых лет" @@ -98,7 +96,6 @@ description = "Роанок - больших размеров спутник Эребуса со смертельной атмосферой. NanoTrasen заявила свои права на это место, богатое фороном, и основало множество предприятий. \ Когда из 8 изначальных сооружений осталось всего 3, NanoTrasen, узнав об истинных опасностях спутника, решила местную проблему благополучия и безопасности, утвердив строгие правила работы и техники безопасности. \ Сегодня Роанок является одним из основных поставщиков форона NanoTrasen в системе Никс, а работники имеют относительно роскошные жилищные апартаменты и хорошие условия работы, особенно в сравнении с другими местами в этой системе." - language = LANGUAGE_HUMAN_EURO capital = "ТБН Аркоп V" ruling_body = "Никс" distance = "30 световых лет" @@ -112,7 +109,6 @@ Бывший археологический лагерь Гриндиир сегодня является полноценным купольным городом и фактической столицей планеты. \ Gilthari Exports, в первую очередь, ориентируется на сельскохозяйственные, горнодобывающие и ксеноархеологические работы, в то время как Aether Atmospherics использует свои ресурсы для поддержания атмосферы и гравитационных генераторов. Йуклит, обычно, считается фактической столицей Никс, так как колония имеет наибольшую численность в системе, однако стоимость проживания здесь довольно высока из-за законов, которые утвердили корпорации, такие как высокие арендная плата и налоги на коммунальные услуги, транспорт и водоснабжение. \ Независимо от относительного отсутствия больших возможностей, это по-прежнему одно из самых предпочтительных мест в системе Никс для лиц среднего класса или богатых, которые могут позволить себе такие расходы, причем многие корпорации и предприятия из других систем базируются именно здесь." - language = LANGUAGE_HUMAN_EURO capital = "Гриндиир" ruling_body = "Никс" distance = "30 световых лет" @@ -124,7 +120,6 @@ nickname = "Кассер" description = "Кассер - средний по размеру спутник Эребуса. Grayson Manufactories, используя свой опыт как в шахтёрстве, так и в промышленности, создала небольшой, но эффективный горнодобывающий комплекс, который быстро стал одним из основных экспортеров и производителей всего оборудования, используемого сегодня на местных спутниках. \ Небольшая шахтерская платформа превратилась в огромную индустрию с топливоперерабатывающими заводами и фабриками, покрывающими большую часть поверхности Кассера. Здесь шахтеры зарабатывают достаточно, чтобы прожить, но едва. Рабочие фабрик получают немного больше, а корпоративные представители зарабатывают добротно каждый день." - language = LANGUAGE_HUMAN_EURO capital = "Центральный Космопорт Кассера" ruling_body = "Никс" distance = "30 световых лет" @@ -240,4 +235,4 @@ ruling_body = "Центральное Правительство Солнечной Системы" distance = "4 световых года" economic_power = 1.2 - secondary_langs = list(LANGUAGE_RESOMI, LANGUAGE_SKRELLIAN, LANGUAGE_HUMAN_CHINESE, LANGUAGE_HUMAN_ARABIC, LANGUAGE_HUMAN_INDIAN, LANGUAGE_HUMAN_IBERIAN, LANGUAGE_HUMAN_RUSSIAN) + secondary_langs = list(LANGUAGE_RESOMI, LANGUAGE_SIMPSKRELLIAN, LANGUAGE_HUMAN_CHINESE, LANGUAGE_HUMAN_ARABIC, LANGUAGE_HUMAN_INDIAN, LANGUAGE_HUMAN_IBERIAN, LANGUAGE_HUMAN_RUSSIAN) diff --git a/mods/failu_skrell_clothes/_failu_skrell_clothes.dm b/mods/failu_skrell_clothes/_failu_skrell_clothes.dm index 2429145207f6c..5415f13cbe6c8 100644 --- a/mods/failu_skrell_clothes/_failu_skrell_clothes.dm +++ b/mods/failu_skrell_clothes/_failu_skrell_clothes.dm @@ -1,4 +1,4 @@ -/singleton/modpack/failu_skrell_clothes - name = "Skrell Clothes by Failu" - desc = "Мод добавляет одежду для скреллов всех каст." - author = "KOJIECO" +/singleton/modpack/failu_skrell_clothes + name = "Skrell Clothes by Failu" + desc = "Мод добавляет одежду для скреллов всех каст." + author = "KOJIECO" diff --git a/mods/failu_skrell_clothes/code/lists/xenowear.dm b/mods/failu_skrell_clothes/code/lists/xenowear.dm index 264f192ed0942..b2a7005c8326d 100644 --- a/mods/failu_skrell_clothes/code/lists/xenowear.dm +++ b/mods/failu_skrell_clothes/code/lists/xenowear.dm @@ -1,36 +1,36 @@ -// Alien clothing - -// Skrell clothing - -// Коммент от Колеса: если по какой-то причине у вас не отображается вещь в нужной вкладке лодаута, почекайте -// её в других вкладках. Если отображается не там, где нужно, поищите в коркоде определенные флаги сортировки. -// У меня например пришлось брать из коркода sort_category = "Xenowear" -/datum/gear/suit/skrell_robe - display_name = "(Skrell) Skrellian robe" - path = /obj/item/clothing/suit/skrell_robe - sort_category = "Xenowear" - cost = 1 - slot = slot_wear_suit - -/datum/gear/suit/skrell_clothes - display_name = "(Skrell) Skrell clothes selection" - path = /obj/item/clothing/under/ - sort_category = "Xenowear" - -/datum/gear/suit/skrell_clothes/New() - ..() - var/skrell_clothes = list() - skrell_clothes["Talum-Katish blue dress"] = /obj/item/clothing/under/uniform/skrell_talum_bluedress - skrell_clothes["Talum-Katish green waist cloak"] = /obj/item/clothing/under/uniform/skrell_talum_greenwaistcloak - skrell_clothes["Raskinta-Katish navy clothes"] = /obj/item/clothing/under/uniform/skrell_raskinta_navyclothes - skrell_clothes["Malish-Katish blue office suit"] = /obj/item/clothing/under/uniform/skrell_malish_officesuit - skrell_clothes["Talum-Katish white and red dress"] = /obj/item/clothing/under/uniform/skrell_talum_whitereddress - skrell_clothes["Malish-Katish office suit with tie"] = /obj/item/clothing/under/uniform/skrell_malish_greentiedsuit - skrell_clothes["Kanin-Katish sand-blue turtleneck"] = /obj/item/clothing/under/uniform/skrell_kanin_sandblueturtleneck - skrell_clothes["Kanin-Katish orange-cyan uniform"] = /obj/item/clothing/under/uniform/skrell_kanin_orangecyanuniform - skrell_clothes["Kanin-Katish yellow-black costume"] = /obj/item/clothing/under/uniform/skrell_kanin_yellowblackcostume - skrell_clothes["Malish-Katish green office suit with suspenders"] = /obj/item/clothing/under/uniform/skrell_malish_greenofficesuit - skrell_clothes["Raskinta-Katish red clothes"] = /obj/item/clothing/under/uniform/skrell_raskinta_redclothes - skrell_clothes["Skrellian diving suit"] = /obj/item/clothing/under/uniform/skrell_skrellian_divingsuit - gear_tweaks += new/datum/gear_tweak/path(skrell_clothes) - +// Alien clothing + +// Skrell clothing + +// Коммент от Колеса: если по какой-то причине у вас не отображается вещь в нужной вкладке лодаута, почекайте +// её в других вкладках. Если отображается не там, где нужно, поищите в коркоде определенные флаги сортировки. +// У меня например пришлось брать из коркода sort_category = "Xenowear" +/datum/gear/suit/skrell_robe + display_name = "(Skrell) Skrellian robe" + path = /obj/item/clothing/suit/skrell_robe + sort_category = "Xenowear" + cost = 1 + slot = slot_wear_suit + +/datum/gear/suit/skrell_clothes + display_name = "(Skrell) Skrell clothes selection" + path = /obj/item/clothing/under/ + sort_category = "Xenowear" + +/datum/gear/suit/skrell_clothes/New() + ..() + var/skrell_clothes = list() + skrell_clothes["Talum-Katish blue dress"] = /obj/item/clothing/under/uniform/skrell_talum_bluedress + skrell_clothes["Talum-Katish green waist cloak"] = /obj/item/clothing/under/uniform/skrell_talum_greenwaistcloak + skrell_clothes["Raskinta-Katish navy clothes"] = /obj/item/clothing/under/uniform/skrell_raskinta_navyclothes + skrell_clothes["Malish-Katish blue office suit"] = /obj/item/clothing/under/uniform/skrell_malish_officesuit + skrell_clothes["Talum-Katish white and red dress"] = /obj/item/clothing/under/uniform/skrell_talum_whitereddress + skrell_clothes["Malish-Katish office suit with tie"] = /obj/item/clothing/under/uniform/skrell_malish_greentiedsuit + skrell_clothes["Kanin-Katish sand-blue turtleneck"] = /obj/item/clothing/under/uniform/skrell_kanin_sandblueturtleneck + skrell_clothes["Kanin-Katish orange-cyan uniform"] = /obj/item/clothing/under/uniform/skrell_kanin_orangecyanuniform + skrell_clothes["Kanin-Katish yellow-black costume"] = /obj/item/clothing/under/uniform/skrell_kanin_yellowblackcostume + skrell_clothes["Malish-Katish green office suit with suspenders"] = /obj/item/clothing/under/uniform/skrell_malish_greenofficesuit + skrell_clothes["Raskinta-Katish red clothes"] = /obj/item/clothing/under/uniform/skrell_raskinta_redclothes + skrell_clothes["Skrellian diving suit"] = /obj/item/clothing/under/uniform/skrell_skrellian_divingsuit + gear_tweaks += new/datum/gear_tweak/path(skrell_clothes) + diff --git a/mods/failu_skrell_clothes/code/uniforms.dm b/mods/failu_skrell_clothes/code/uniforms.dm index 3103d8579bf09..7ae3d750a8b76 100644 --- a/mods/failu_skrell_clothes/code/uniforms.dm +++ b/mods/failu_skrell_clothes/code/uniforms.dm @@ -1,122 +1,122 @@ -// Skrell outfits - - -/obj/item/clothing/under/uniform/skrell_talum_bluedress - name = "Talum-Katish blue dress" - desc = "Talum's blue dress, seems luminous, it's light and pleasant to the touch. The dress has a purple cape on the shoulders." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "talum_bluedress_s" - item_state = "talum_bluedress_s" - worn_state = "talum_bluedress" - - -/obj/item/clothing/under/uniform/skrell_talum_greenwaistcloak - name = "Talum-Katish green waist cloak" - desc = "Talum's white-green clothes with the translucent veil at the waist." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "talum_greenwaistcloak_s" - item_state = "talum_greenwaistcloak_s" - worn_state = "talum_greenwaistcloak" - - -/obj/item/clothing/under/uniform/skrell_raskinta_navyclothes - name = "Raskinta-Katish navy clothes" - desc = "Raskinta's navy clothes. Has no Qarr-Morr'Kon markings, and pretty flexible!" - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "raskinta_navyclothes_s" - item_state = "raskinta_navyclothes_s" - worn_state = "raskinta_navyclothes" - - - -/obj/item/clothing/under/uniform/skrell_malish_officesuit - name = "Malish-Katish blue office suit" - desc = "Malish's office suit, light top, dark bottom, and some glowing purple-teal jewelry." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "malish_officesuit_s" - item_state = "malish_officesuit_s" - worn_state = "malish_officesuit" - - -/obj/item/clothing/under/uniform/skrell_talum_whitereddress - name = "Talum-Katish white and red dress" - desc = "Talum's white-red dress. The sparkling red skirt is attached to one of the legs. It looks unusual." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "talum_whitereddress_s" - item_state = "talum_whitereddress_s" - worn_state = "talum_whitereddress" - - - -/obj/item/clothing/under/uniform/skrell_malish_greentiedsuit - name = "Malish-Katish office suit with tie" - desc = "Malish's office suit, light top, green bottom, and a nice-looking green tie that barely touches the neck, made of soft material." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "malish_greentiedsuit_s" - item_state = "malish_greentiedsuit_s" - worn_state = "malish_greentiedsuit" - - -/obj/item/clothing/under/uniform/skrell_kanin_sandblueturtleneck - name = "Kanin-Katish sand-blue turtleneck" - desc = "Comfortable Kanin's clothing that has padded inserts and cleverly stitched fabric, allowing the wearer's skin to breathe." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "kanin_sandblueturtleneck_s" - item_state = "kanin_sandblueturtleneck_s" - worn_state = "kanin_sandblueturtleneck" - -/obj/item/clothing/under/uniform/skrell_kanin_orangecyanuniform - name = "Kanin-Katish orange-cyan uniform" - desc = "Lightweight, breathable uniform designed for work in non-traumatic conditions, providing its owner with ease of movement." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "kanin_orangecyanuniform_s" - item_state = "kanin_orangecyanuniform_s" - worn_state = "kanin_orangecyanuniform" - - -/obj/item/clothing/under/uniform/skrell_kanin_yellowblackcostume - name = "Kanin-Katish yellow-black costume" - desc = "Kanin's black and yellow suit is of high quality, the contrast of colors makes a highly qualified employee stand out. White helmet is not included!" - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "kanin_yellowblackcostume_s" - item_state = "kanin_yellowblackcostume_s" - worn_state = "kanin_yellowblackcostume" - -/obj/item/clothing/under/uniform/skrell_malish_greenofficesuit - name = "Malish-Katish green office suit with suspenders" - desc = "Malish's office suit, light top, black bottom, and a fancy lime-green thin veil above the belt." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "malish_greenofficesuit_s" - item_state = "malish_greenofficesuit_s" - worn_state = "malish_greenofficesuit" - - -/obj/item/clothing/under/uniform/skrell_raskinta_redclothes - name = "Raskinta-Katish red clothes" - desc = "Raskinta's red clothes. Has no Qarr-Morr'Kon markings, and pretty flexible!" - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "raskinta_redclothes_s" - item_state = "raskinta_redclothes_s" - worn_state = "raskinta_redclothes" - - -/obj/item/clothing/under/uniform/skrell_skrellian_divingsuit - name = "Skrellian diving suit" - desc = "A highly detailed Skrellian diving suit. Found mainly among the inhabitants of the planets of the Empire." - icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' - item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') - icon_state = "skrellian_divingsuit_s" - item_state = "skrellian_divingsuit_s" - worn_state = "skrellian_divingsuit" - +// Skrell outfits + + +/obj/item/clothing/under/uniform/skrell_talum_bluedress + name = "Talum-Katish blue dress" + desc = "Talum's blue dress, seems luminous, it's light and pleasant to the touch. The dress has a purple cape on the shoulders." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "talum_bluedress_s" + item_state = "talum_bluedress_s" + worn_state = "talum_bluedress" + + +/obj/item/clothing/under/uniform/skrell_talum_greenwaistcloak + name = "Talum-Katish green waist cloak" + desc = "Talum's white-green clothes with the translucent veil at the waist." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "talum_greenwaistcloak_s" + item_state = "talum_greenwaistcloak_s" + worn_state = "talum_greenwaistcloak" + + +/obj/item/clothing/under/uniform/skrell_raskinta_navyclothes + name = "Raskinta-Katish navy clothes" + desc = "Raskinta's navy clothes. Has no Qarr-Morr'Kon markings, and pretty flexible!" + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "raskinta_navyclothes_s" + item_state = "raskinta_navyclothes_s" + worn_state = "raskinta_navyclothes" + + + +/obj/item/clothing/under/uniform/skrell_malish_officesuit + name = "Malish-Katish blue office suit" + desc = "Malish's office suit, light top, dark bottom, and some glowing purple-teal jewelry." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "malish_officesuit_s" + item_state = "malish_officesuit_s" + worn_state = "malish_officesuit" + + +/obj/item/clothing/under/uniform/skrell_talum_whitereddress + name = "Talum-Katish white and red dress" + desc = "Talum's white-red dress. The sparkling red skirt is attached to one of the legs. It looks unusual." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "talum_whitereddress_s" + item_state = "talum_whitereddress_s" + worn_state = "talum_whitereddress" + + + +/obj/item/clothing/under/uniform/skrell_malish_greentiedsuit + name = "Malish-Katish office suit with tie" + desc = "Malish's office suit, light top, green bottom, and a nice-looking green tie that barely touches the neck, made of soft material." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "malish_greentiedsuit_s" + item_state = "malish_greentiedsuit_s" + worn_state = "malish_greentiedsuit" + + +/obj/item/clothing/under/uniform/skrell_kanin_sandblueturtleneck + name = "Kanin-Katish sand-blue turtleneck" + desc = "Comfortable Kanin's clothing that has padded inserts and cleverly stitched fabric, allowing the wearer's skin to breathe." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "kanin_sandblueturtleneck_s" + item_state = "kanin_sandblueturtleneck_s" + worn_state = "kanin_sandblueturtleneck" + +/obj/item/clothing/under/uniform/skrell_kanin_orangecyanuniform + name = "Kanin-Katish orange-cyan uniform" + desc = "Lightweight, breathable uniform designed for work in non-traumatic conditions, providing its owner with ease of movement." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "kanin_orangecyanuniform_s" + item_state = "kanin_orangecyanuniform_s" + worn_state = "kanin_orangecyanuniform" + + +/obj/item/clothing/under/uniform/skrell_kanin_yellowblackcostume + name = "Kanin-Katish yellow-black costume" + desc = "Kanin's black and yellow suit is of high quality, the contrast of colors makes a highly qualified employee stand out. White helmet is not included!" + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "kanin_yellowblackcostume_s" + item_state = "kanin_yellowblackcostume_s" + worn_state = "kanin_yellowblackcostume" + +/obj/item/clothing/under/uniform/skrell_malish_greenofficesuit + name = "Malish-Katish green office suit with suspenders" + desc = "Malish's office suit, light top, black bottom, and a fancy lime-green thin veil above the belt." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "malish_greenofficesuit_s" + item_state = "malish_greenofficesuit_s" + worn_state = "malish_greenofficesuit" + + +/obj/item/clothing/under/uniform/skrell_raskinta_redclothes + name = "Raskinta-Katish red clothes" + desc = "Raskinta's red clothes. Has no Qarr-Morr'Kon markings, and pretty flexible!" + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "raskinta_redclothes_s" + item_state = "raskinta_redclothes_s" + worn_state = "raskinta_redclothes" + + +/obj/item/clothing/under/uniform/skrell_skrellian_divingsuit + name = "Skrellian diving suit" + desc = "A highly detailed Skrellian diving suit. Found mainly among the inhabitants of the planets of the Empire." + icon = 'mods/failu_skrell_clothes/icons/skrell/obj_under_skrell.dmi' + item_icons = list(slot_w_uniform_str = 'mods/failu_skrell_clothes/icons/skrell/onmob_under_skrell.dmi') + icon_state = "skrellian_divingsuit_s" + item_state = "skrellian_divingsuit_s" + worn_state = "skrellian_divingsuit" + diff --git a/mods/global_modpacks.dm b/mods/global_modpacks.dm index 26cdd288e1a35..c489044b7a97c 100644 --- a/mods/global_modpacks.dm +++ b/mods/global_modpacks.dm @@ -44,5 +44,6 @@ #include "failu_skrell_clothes/_failu_skrell_clothes.dme" #include "chemtweaks/_chemtweaks.dme" #include "diona/_diona.dme" +#include "simplangs/simplangs.dme" #include "../packs/sierra-tweaks/_pack.dm" diff --git a/mods/ipc_mods/code/machine.dm b/mods/ipc_mods/code/machine.dm index 3de8fc23e7a86..1026456608a92 100644 --- a/mods/ipc_mods/code/machine.dm +++ b/mods/ipc_mods/code/machine.dm @@ -5,6 +5,10 @@ passive_temp_gain = 0 // This should cause IPCs to stabilize at ~80 C in a 20 C environment.(5 is default without organ) additional_languages = 1 +/datum/species/machine/skills_from_age(age) + if(age) + . = 8 + /obj/machinery/organ_printer/robot/New() LAZYINITLIST(products) products[BP_COOLING] = list(/obj/item/organ/internal/cooling_system, 35) diff --git a/mods/loadout_items/_loadout_items.dme b/mods/loadout_items/_loadout_items.dme index b93bb82ae70cf..c0716ccb09387 100644 --- a/mods/loadout_items/_loadout_items.dme +++ b/mods/loadout_items/_loadout_items.dme @@ -17,6 +17,7 @@ #include "code/lists/uniforms.dm" #include "code/lists/suits.dm" #include "code/lists/xenowear.dm" +#include "code/lists/headwear.dm" #include "code/loadout/loadout.dm" #include "code/loadout/donations.dm" #include "code/loadout/donator.dm" diff --git a/mods/loadout_items/code/head.dm b/mods/loadout_items/code/head.dm index fdad5c3540768..289555f60fff3 100644 --- a/mods/loadout_items/code/head.dm +++ b/mods/loadout_items/code/head.dm @@ -37,3 +37,19 @@ icon_state = initial(icon_state) item_state = initial(icon_state) to_chat(user, "You lower the ear flaps on the Suncap.") + +// Human headgarments + +/obj/item/clothing/head/soft/colorable + name = "Soft cap" + desc = "A simple baseball soft cap without any special qualities" + icon = 'mods/loadout_items/icons/obj_head.dmi' + item_icons = list( + slot_head_str = 'mods/loadout_items/icons/onmob_head.dmi' + ) + icon_state = "cprescap" + item_state = "cprescap" + +/obj/item/clothing/head/soft/on_update_icon() + . = ..() + item_state = initial(item_state) + (flipped ? "_flipped" : "") \ No newline at end of file diff --git a/mods/loadout_items/code/lists/headwear.dm b/mods/loadout_items/code/lists/headwear.dm new file mode 100644 index 0000000000000..2389436ad5e95 --- /dev/null +++ b/mods/loadout_items/code/lists/headwear.dm @@ -0,0 +1,6 @@ +/datum/gear/head/ballcap + display_name = "ballcap, colour select" + path = /obj/item/clothing/head/soft/colorable + flags = GEAR_HAS_COLOR_SELECTION + slot = slot_head + cost = 1 \ No newline at end of file diff --git a/mods/loadout_items/icons/obj_head.dmi b/mods/loadout_items/icons/obj_head.dmi index 59a4a33835632..224ac27344244 100644 Binary files a/mods/loadout_items/icons/obj_head.dmi and b/mods/loadout_items/icons/obj_head.dmi differ diff --git a/mods/loadout_items/icons/onmob_head.dmi b/mods/loadout_items/icons/onmob_head.dmi index 2c649ac83722e..8a13047241bfc 100644 Binary files a/mods/loadout_items/icons/onmob_head.dmi and b/mods/loadout_items/icons/onmob_head.dmi differ diff --git a/mods/machinery/code/telesci_by_TT/tele_pads.dm b/mods/machinery/code/telesci_by_TT/tele_pads.dm index ffda9771c076f..64de6db5ffaf0 100644 --- a/mods/machinery/code/telesci_by_TT/tele_pads.dm +++ b/mods/machinery/code/telesci_by_TT/tele_pads.dm @@ -21,7 +21,7 @@ return panel_open /obj/machinery/telepad/use_tool(obj/item/tool, mob/living/user, list/click_params) - if(component_attackby(tool, user)) return TRUE + //if(component_attackby(tool, user)) return TRUE if(panel_open) if(istype(tool, /obj/item/device/multitool)) var/obj/item/device/multitool/M = tool @@ -42,7 +42,8 @@ if(istype(tool, /obj/item/device/multitool)) to_chat(user, "You should open [src]'s maintenance panel first.") return - .=..() + //.=..() + return ..() /obj/machinery/telepad/on_update_icon() switch (panel_open) diff --git a/mods/music_player/_music_player.dme b/mods/music_player/_music_player.dme index 2cdcb59f18488..8b11351845d6c 100644 --- a/mods/music_player/_music_player.dme +++ b/mods/music_player/_music_player.dme @@ -3,6 +3,7 @@ #include "_music_player.dm" +#include "code/music_prefs.dm" #include "code/music_player.dm" #include "code/music_tape.dm" #include "code/music_writer.dm" diff --git a/mods/music_player/code/music_player.dm b/mods/music_player/code/music_player.dm index 6bb1e75bc65b7..4a6f7bc45061f 100644 --- a/mods/music_player/code/music_player.dm +++ b/mods/music_player/code/music_player.dm @@ -474,7 +474,7 @@ GLOBAL_LIST_INIT(switch_small_sound, list( sound_token.Unpause() else QDEL_NULL(sound_token) - sound_token = GLOB.sound_player.PlayLoopingSound(src, sound_id, tape.track.source, volume = volume, frequency = frequency, range = 9, falloff = 2, prefer_mute = TRUE) + sound_token = GLOB.sound_player.PlayLoopingSound(src, sound_id, tape.track.source, volume = volume, frequency = frequency, range = 9, falloff = 2, prefer_mute = TRUE, preference = /datum/client_preference/play_pmps) mode = PLAYER_STATE_PLAY START_PROCESSING(SSobj, src) diff --git a/mods/music_player/code/music_prefs.dm b/mods/music_player/code/music_prefs.dm new file mode 100644 index 0000000000000..58829c39cd722 --- /dev/null +++ b/mods/music_player/code/music_prefs.dm @@ -0,0 +1,60 @@ +/datum/client_preference/play_jukeboxes + description ="Play jukeboxes and boomboxes" + key = "SOUND_JUKEBOXES" + +/datum/client_preference/play_pmps + description ="Play music players" + key = "SOUND_PMPS" + +/datum/sound_token + var/datum/client_preference/preference + +/datum/sound_token/proc/check_preference(atom/listener) + if(preference) + var/mob/M = listener + if(istype(M)) + if((M.get_preference_value(preference) != GLOB.PREF_YES)) + return FALSE + return TRUE + +/singleton/sound_player/PlaySoundDatum(atom/source, sound_id, sound/sound, range, prefer_mute, datum/client_preference/preference) + var/token_type = isnum(sound.environment) ? /datum/sound_token : /datum/sound_token/static_environment + return new token_type(source, sound_id, sound, range, prefer_mute, preference) + +/singleton/sound_player/PlayLoopingSound(atom/source, sound_id, sound, volume, range, falloff = 1, echo, frequency, prefer_mute, datum/client_preference/preference) + var/sound/S = istype(sound, /sound) ? sound : new(sound) + S.environment = 0 // Ensures a 3D effect even if x/y offset happens to be 0 the first time it's played + S.volume = volume + S.falloff = falloff + S.echo = echo + S.frequency = frequency + S.repeat = TRUE + + return PlaySoundDatum(source, sound_id, S, range, prefer_mute, preference) + +/datum/sound_token/New(atom/source, sound_id, sound/sound, range = 4, prefer_mute = FALSE, datum/client_preference/preference) + src.preference = preference + ..() + +/datum/sound_token/PrivAddListener(atom/listener) + if(!check_preference(listener)) + return + ..() + +/datum/sound_token/PrivUpdateListener(listener, update_sound = TRUE) + if(!check_preference(listener)) + PrivRemoveListener(listener) + return + ..() + + +/datum/jukebox/Play() + if (playing) + return + var/datum/jukebox_track/track = tracks[index] + if (!track.source) + return + playing = TRUE + token = GLOB.sound_player.PlayLoopingSound(owner, sound_id, track.source, + volume, range, falloff, frequency = frequency, prefer_mute = TRUE, preference = /datum/client_preference/play_jukeboxes) + owner.queue_icon_update() \ No newline at end of file diff --git a/mods/simplangs/README.md b/mods/simplangs/README.md new file mode 100644 index 0000000000000..7f2bc62a6e270 --- /dev/null +++ b/mods/simplangs/README.md @@ -0,0 +1,76 @@ + +#### Список PRов: + +- https://github.com/SierraBay/SierraBay12/pull/##### + + + + +ID мода: SIMPLANGS + + +### Описание мода + +Мод, добавляющий упрощённые версии языков ксеноты для людей. + + +### Изменения *кор кода* + +Отсутствуют + + +### Оверрайды + +- `mods/primlangs/code/simplangs.dm` + + +### Дефайны + +- `code/__defines/~mods/~master_defines.dm`: `LANGUAGE_SIMPTAJARAN`, `LANGUAGE_SIMPSKRELLIAN`, `LANGUAGE_SIMPUNATHI` + + +### Используемые файлы, не содержащиеся в модпаке + +Отсутствуют + + +### Авторы: + +Baneuus + diff --git a/mods/simplangs/code/simplangs.dm b/mods/simplangs/code/simplangs.dm new file mode 100644 index 0000000000000..8a3419143bb82 --- /dev/null +++ b/mods/simplangs/code/simplangs.dm @@ -0,0 +1,79 @@ +/datum/language/simpskrell + name = LANGUAGE_SIMPSKRELLIAN + desc = "A simplified interpretation of skrellian language, designed specifically to be spoken by humans." + speech_verb = "warbles" + ask_verb = "warbles" + exclaim_verb = "warbles" + colour = "skrell" + key = "&" + syllables = list("qr","krr","xuq","qil","kvuum","ksum","vol","xrim","zaoo","ku-uu","kvix","qoo","zix","kzh") + shorthand = "sSK" + has_written_form = TRUE + partial_understanding = list(LANGUAGE_SKRELLIAN = 80) + +/datum/language/skrell/New() + . = ..() + partial_understanding += list(LANGUAGE_SIMPSKRELLIAN = 90) + +/datum/language/primtajaran + name = LANGUAGE_SIMPTAJARAN + desc = "A crude and simplified interpretation of tajaran language spoken by humans." + speech_verb = "мурчит" + ask_verb = "мурчит" + exclaim_verb = "воет" + colour = "tajaran" + key = "?" + syllables = list("mrr","rr","tajr","kir","raj","kii","mir","kra","ahk","nal","vah","khaz","jri","ran","darr", + "mi","jri","dynh","manq","rhe","zar","rrhaz","kal","chur","eech","taa","dra","ju-rl","mah","sanu","dra","ii'r", + "ka","aasi","far","wa","baq","ara","qara","zir","sam","mak","hrar","nga","rir","khan","gun","dar","rik","kah", + "hal","ket","jurl","mah","tul","cresh","azu","ragh","mro","mra","mrro","mrra") + shorthand = "sTJ" + has_written_form = TRUE + partial_understanding = list(LANGUAGE_SIIK_MAAS = 80) + +/datum/language/tajaran/New() + . = ..() + partial_understanding += list(LANGUAGE_SIMPTAJARAN = 90) + +/datum/language/simpunathi + name = LANGUAGE_SIMPUNATHI + desc = "A derivative of Sinta'Unathi, this language has been created specifically to be spoken by humans. Vocal sounds and limb gestures that cannot be properly replicated by humans have been replaced with Iberian vowels. Iber'Unathi is primarily used by denizens of the Tersten Republic." + speech_verb = "hisses" + ask_verb = "hisses" + exclaim_verb = "roars" + colour = "soghun" + key = "!" + space_chance = 40 + syllables = list( + "za", "az", "ze", "ez", "zi", "iz", "zo", "oz", "zu", "uz", "zs", "sz", + "ha", "ah", "he", "eh", "hi", "ih", "ho", "oh", "hu", "uh", "hs", "sh", + "la", "al", "le", "el", "li", "il", "lo", "ol", "lu", "ul", "ls", "sl", + "ka", "ak", "ke", "ek", "ki", "ik", "ko", "ok", "ku", "uk", "ks", "sk", + "sa", "as", "se", "es", "si", "is", "so", "os", "su", "us", "ss", "ss", + "ra", "ar", "re", "er", "ri", "ir", "ro", "or", "ru", "ur", "rs", "sr", + "a", "a", "e", "e", "i", "i", "o", "o", "u", "u", "s", "s" + ) + shorthand = "iUT" + has_written_form = TRUE + partial_understanding = list(LANGUAGE_UNATHI_SINTA = 80, LANGUAGE_UNATHI_YEOSA = 40) + +/datum/language/unathi/New() + . = ..() + partial_understanding += list(LANGUAGE_SIMPUNATHI = 90, LANGUAGE_UNATHI_YEOSA = 80) + +/datum/language/yeosa/New() + . = ..() + partial_understanding += list(LANGUAGE_SIMPUNATHI = 60, LANGUAGE_UNATHI_SINTA = 80) + +/singleton/cultural_info/culture/human + secondary_langs = list( + LANGUAGE_HUMAN_EURO, + LANGUAGE_HUMAN_CHINESE, + LANGUAGE_HUMAN_ARABIC, + LANGUAGE_HUMAN_INDIAN, + LANGUAGE_HUMAN_IBERIAN, + LANGUAGE_HUMAN_RUSSIAN, + LANGUAGE_SPACER, + LANGUAGE_SIGN, + LANGUAGE_SIMPTAJARAN + ) diff --git a/mods/simplangs/simplangs.dm b/mods/simplangs/simplangs.dm new file mode 100644 index 0000000000000..187025f44b559 --- /dev/null +++ b/mods/simplangs/simplangs.dm @@ -0,0 +1,4 @@ +/singleton/modpack/simplangs + name = "Примитивные языки" + desc = "Мод, чтоб говорить на упрощённых языках ксеноты" + author = "Baneuus" diff --git a/mods/simplangs/simplangs.dme b/mods/simplangs/simplangs.dme new file mode 100644 index 0000000000000..d52a4f94da206 --- /dev/null +++ b/mods/simplangs/simplangs.dme @@ -0,0 +1,4 @@ +#define MODPACK_SIMPLANGS + +#include "code/simplangs.dm" +#include "simplangs.dm" diff --git a/mods/utility_items/_utility_items.dme b/mods/utility_items/_utility_items.dme index a2709e68e731f..07564d1dd5a00 100644 --- a/mods/utility_items/_utility_items.dme +++ b/mods/utility_items/_utility_items.dme @@ -26,8 +26,12 @@ #include "code/advanced_landing.dm" #include "code/chemistry.dm" #include "code/jobs.dm" +#include "code/ma21.dm" +#include "code/modnye_patchi.dm" #include "code/bottom.dm" #include "code/top.dm" -#include "code\skrell-ship.dm" +#include "code/skrell-ship.dm" +#include "code/security.dm" +#include "code/FBP_vox.dm" -#endif +#endif \ No newline at end of file diff --git a/mods/utility_items/code/FBP_vox.dm b/mods/utility_items/code/FBP_vox.dm new file mode 100644 index 0000000000000..1abe6a78ef244 --- /dev/null +++ b/mods/utility_items/code/FBP_vox.dm @@ -0,0 +1,2 @@ +/datum/species/vox + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_NO_FBP_CONSTRUCTION diff --git a/mods/utility_items/code/ma21.dm b/mods/utility_items/code/ma21.dm new file mode 100644 index 0000000000000..f03f0d50b0e36 --- /dev/null +++ b/mods/utility_items/code/ma21.dm @@ -0,0 +1,5 @@ +/obj/item/gun/energy/plasmastun + item_icons = list( + slot_l_hand_str = 'mods/utility_items/icons/lefthand_guns.dmi', + slot_r_hand_str = 'mods/utility_items/icons/righthand_guns.dmi' + ) diff --git a/mods/utility_items/code/modnye_patchi.dm b/mods/utility_items/code/modnye_patchi.dm new file mode 100644 index 0000000000000..654774163d4d0 --- /dev/null +++ b/mods/utility_items/code/modnye_patchi.dm @@ -0,0 +1,39 @@ +/obj/item/clothing/accessory/cross_blue + name = "Cross blue" + desc = "A beautiful metal cross. It looks like a medal, but does not have any distinctive signs." + icon = 'mods/utility_items/icons/pins.dmi' + accessory_icons = list(slot_w_uniform_str = 'mods/utility_items/icons/pins.dmi', slot_wear_suit_str = 'mods/utility_items/icons/pins.dmi') + icon_state = "small_cross(blue)" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/cross_red + name = "Cross red" + desc = "A beautiful metal cross. It looks like a medal, but does not have any distinctive signs." + icon = 'mods/utility_items/icons/pins.dmi' + accessory_icons = list(slot_w_uniform_str = 'mods/utility_items/icons/pins.dmi', slot_wear_suit_str = 'mods/utility_items/icons/pins.dmi') + icon_state = "small_cross(red)" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/flower_gold + name = "Flower gold" + desc = "Metal brooch, looks like a flower. This one is golden. It looks very stylish!" + icon = 'mods/utility_items/icons/pins.dmi' + accessory_icons = list(slot_w_uniform_str = 'mods/utility_items/icons/pins.dmi', slot_wear_suit_str = 'mods/utility_items/icons/pins.dmi') + icon_state = "brooch flower(gold)" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/flower_silver + name = "Flower silver" + desc = "Metal brooch, looks like a flower. This one is silver. It looks very stylish!" + icon = 'mods/utility_items/icons/pins.dmi' + accessory_icons = list(slot_w_uniform_str = 'mods/utility_items/icons/pins.dmi', slot_wear_suit_str = 'mods/utility_items/icons/pins.dmi') + icon_state = "brooch flower(silver)" + slot = ACCESSORY_SLOT_INSIGNIA + +/obj/item/clothing/accessory/flower_bronze + name = "Flower bronze" + desc = "Metal brooch, looks like a flower. This one is bronze. It looks very stylish!" + icon = 'mods/utility_items/icons/pins.dmi' + accessory_icons = list(slot_w_uniform_str = 'mods/utility_items/icons/pins.dmi', slot_wear_suit_str = 'mods/utility_items/icons/pins.dmi') + icon_state = "brooch flower(bronze)" + slot = ACCESSORY_SLOT_INSIGNIA diff --git a/mods/utility_items/code/security.dm b/mods/utility_items/code/security.dm new file mode 100644 index 0000000000000..7fa6363e5dec5 --- /dev/null +++ b/mods/utility_items/code/security.dm @@ -0,0 +1,23 @@ +/obj/item/clothing/suit/armor/hos/Initialize() + . = ..() + name = "armored coat" + desc = "A greatcoat enhanced with a special alloy for some protection and style." + icon = 'maps/sierra/icons/obj/clothing/obj_suit.dmi' + item_icons = list(slot_wear_suit_str = 'maps/sierra/icons/mob/onmob/onmob_suit.dmi') + icon_state = "hos" + sprite_sheets = list( + SPECIES_UNATHI = 'maps/sierra/icons/mob/onmob/onmob_suit.dmi', + SPECIES_TAJARA = 'maps/sierra/icons/mob/onmob/onmob_suit.dmi', + SPECIES_SKRELL = 'maps/sierra/icons/mob/onmob/onmob_suit.dmi', + SPECIES_VOX = 'maps/sierra/icons/mob/onmob/onmob_suit.dmi', + SPECIES_RESOMI = 'mods/resomi/icons/clothing/onmob_suit_resomi.dmi') + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list( + melee = ARMOR_MELEE_MAJOR, + bullet = ARMOR_BALLISTIC_PISTOL, + laser = ARMOR_LASER_HANDGUNS, + energy = ARMOR_ENERGY_MINOR, + bomb = ARMOR_BOMB_PADDED + ) + siemens_coefficient = 0.6 + flags_inv = null diff --git a/mods/utility_items/icons/lefthand_guns.dmi b/mods/utility_items/icons/lefthand_guns.dmi new file mode 100644 index 0000000000000..8ad41db4d3e9c Binary files /dev/null and b/mods/utility_items/icons/lefthand_guns.dmi differ diff --git a/mods/utility_items/icons/pins.dmi b/mods/utility_items/icons/pins.dmi new file mode 100644 index 0000000000000..27a0547739da7 Binary files /dev/null and b/mods/utility_items/icons/pins.dmi differ diff --git a/mods/utility_items/icons/righthand_guns.dmi b/mods/utility_items/icons/righthand_guns.dmi new file mode 100644 index 0000000000000..af4a3ea1301b7 Binary files /dev/null and b/mods/utility_items/icons/righthand_guns.dmi differ diff --git a/mods/virusology/code/admin.dm b/mods/virusology/code/admin.dm index 81eabebb1904a..e83b8b3a38e15 100644 --- a/mods/virusology/code/admin.dm +++ b/mods/virusology/code/admin.dm @@ -113,7 +113,10 @@ RELEASE "} + show_browser(usr, H, "window=admin2;size=540x600") + /datum/virus2_editor/Topic(href, href_list) + ..() switch(href_list["what"]) if("effect") var/stage = text2num(href_list["stage"]) @@ -161,7 +164,7 @@ if("antigen") if(href_list["toggle"]) var/T = href_list["toggle"] - if(length(T) != 1) return + if(length(T) != 2) return if(T in antigens) antigens -= T else @@ -214,8 +217,8 @@ /client/proc/give_disease2(mob/T as mob in SSmobs.mob_list) // -- Giacom + set name = "Gives Disease" set category = "Fun" - set name = "Give Disease" set desc = "Gives a Disease to a mob." var/datum/disease2/disease/D = new /datum/disease2/disease() diff --git a/mods/virusology/code/antibodies.dm b/mods/virusology/code/antibodies.dm index e48de876d8209..07e49685d5360 100644 --- a/mods/virusology/code/antibodies.dm +++ b/mods/virusology/code/antibodies.dm @@ -1,7 +1,32 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 var/global/list/ALL_ANTIGENS = list( - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", + "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", + "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", + "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", + "G0", "G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "G9", + "H0", "H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", + "I0", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", + "J0", "J1", "J2", "J3", "J4", "J5", "J6", "J7", "J8", "J9", + "K0", "K1", "K2", "K3", "K4", "K5", "K6", "K7", "K8", "K9", + "L0", "L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8", "L9", + "M0", "M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9", + "N0", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", + "O0", "O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", + "P0", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", + "Q0", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "Q7", "Q8", "Q9", + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", + "S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", + "T0", "T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", + "U0", "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8", "U9", + "V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", + "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7", "W8", "W9", + "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "X9", + "Y0", "Y1", "Y2", "Y3", "Y4", "Y5", "Y6", "Y7", "Y8", "Y9", + "Z0", "Z1", "Z2", "Z3", "Z4", "Z5", "Z6", "Z7", "Z8", "Z9" ) /hook/startup/proc/randomise_antigens_order() diff --git a/mods/virusology/code/antibodyanalyser.dm b/mods/virusology/code/antibodyanalyser.dm index 90cd903e96836..a4e41fe405524 100644 --- a/mods/virusology/code/antibodyanalyser.dm +++ b/mods/virusology/code/antibodyanalyser.dm @@ -48,6 +48,8 @@ known_antibodies |= unknown_antibodies //Add the new antibodies to list else src.state("\The [src] buzzes, \"Failed to identify any new antibodies.\"") + if(!LAZYLEN(given_antibodies)) //return if no antibodies + return 0 container.dropInto(loc) container = null diff --git a/mods/virusology/code/curer.dm b/mods/virusology/code/curer.dm index 522d77564e47a..6d09d0afc51c4 100644 --- a/mods/virusology/code/curer.dm +++ b/mods/virusology/code/curer.dm @@ -1,3 +1,4 @@ +// НЕРАБОЧИЙ АППАРАТ, ДЕЛАТЬ ЕГО СЕЙЧАС НЕ БУДУ, кто захочет, делайте /obj/machinery/computer/curer name = "cure research machine" icon = 'icons/obj/machines/computer.dmi' @@ -9,14 +10,28 @@ var/obj/item/reagent_containers/container = null -/obj/machinery/computer/curer/use_tool(obj/item/I, mob/living/user, list/click_params) - . = ..() +/obj/machinery/computer/curer/use_tool(obj/I as obj, mob/user as mob) if(istype(I,/obj/item/reagent_containers)) if(!container) if(!user.unEquip(I, src)) return container = I return + if(istype(I,/obj/item/virusdish)) + if(virusing) + to_chat(user, "The pathogen materializer is still recharging..") + return + var/obj/item/reagent_containers/glass/beaker/product = new(src.loc) + + var/list/data = list("donor" = null, "blood_DNA" = null, "blood_type" = null, "trace_chem" = null, "virus2" = list(), "antibodies" = list()) + data["virus2"] |= I:virus2 + product.reagents.add_reagent(/datum/reagent/blood,30,data) + + virusing = 1 + + + state("The [src.name] Buzzes", "blue") + return ..() return @@ -29,11 +44,15 @@ var/dat if(curing) dat = "Antibody production in progress" + else if(virusing) + dat = "Virus production in progress" else if(container) - // check to see if we have the required reagents - if(container.reagents.get_reagent_amount(/datum/reagent/blood) >= 5 && container.reagents.get_reagent_amount(/datum/reagent/radium) >= 15 && container.reagents.get_reagent_amount(/datum/reagent/spaceacillin) >= 10) + // see if there's any blood in the container + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in container.reagents.reagent_list + if(B) dat = "Blood sample inserted." + dat += "
Antibodies: [antigens2string(B.data["antibodies"])]" dat += "
Begin antibody production" else dat += "
Please check container contents." @@ -41,7 +60,7 @@ else dat = "Please insert a container." - show_browser(user, "[dat]", "window=computer;size=400x500") + show_browser(user, dat, "window=computer") onclose(user, "computer") return @@ -69,22 +88,12 @@ attack_hand(user) /obj/machinery/computer/curer/proc/createcure(obj/item/reagent_containers/container) - var/obj/item/reagent_containers/C = container - var/antibodies - C.dropInto(loc) - var/mob/living/carbon/M = new /mob/living/carbon + var/obj/item/reagent_containers/glass/beaker/product = new(src.loc) + var/datum/reagent/blood/B = locate() in container.reagents.reagent_list - var/data - if(B.data && B.data["virus2"]) - var/list/vlist = B.data["virus2"] - if(LAZYLEN(vlist)) - for(var/ID in vlist) - var/datum/disease2/disease/V = vlist[ID] - data = V.getcopy() - - for(var/ID in data) - var/datum/disease2/disease/V = data[ID] - antibodies |= V.antigen - C.reagents.clear_reagents() - C.reagents.add_reagent(/datum/reagent/antibodies, 10, antibodies) - qdel(M) + + var/list/data = list() + data["antibodies"] = B.data["antibodies"] + product.reagents.add_reagent(/datum/reagent/antibodies,30,data) + + state("\The [src.name] buzzes", "blue") diff --git a/mods/virusology/code/dishincubator.dm b/mods/virusology/code/dishincubator.dm index 48c5a4aa46da2..bcf548f318afa 100644 --- a/mods/virusology/code/dishincubator.dm +++ b/mods/virusology/code/dishincubator.dm @@ -95,7 +95,7 @@ var/threshold_mod = 0 - if(foodsupply) + if(foodsupply > 0) if(dish.growth + 3 >= 100 && dish.growth < 100) ping("\The [src] pings, \"Sufficient viral growth density achieved.\"") @@ -103,7 +103,7 @@ dish.growth += 3 SSnano.update_uis(src) - if(radiation) + if(radiation > 0) threshold_mod++ if(radiation > 50 & prob(5)) dish.virus2.majormutate() @@ -119,7 +119,7 @@ if(toxins && prob(5)) dish.virus2.infectionchance -= 1 SSnano.update_uis(src) - if(toxins > 50) + if(toxins > 70) dish.growth = 0 dish.virus2 = null SSnano.update_uis(src) diff --git a/mods/virusology/code/effect.dm b/mods/virusology/code/effect.dm index 461d6584e6f32..f2e5208419ad8 100644 --- a/mods/virusology/code/effect.dm +++ b/mods/virusology/code/effect.dm @@ -23,7 +23,7 @@ var/chance //probality to fire every tick var/chance_max = 50 var/multiplier = 1 //effect magnitude multiplier - var/multiplier_max = 1 + var/multiplier_max = 5 var/stage = 4 //minimal stage var/badness = VIRUS_MILD //Used in random generation to limit how bad result should come out. var/data = null //For semi-procedural effects; this should be generated in generate() if used @@ -70,6 +70,31 @@ name = "Waiting Syndrome" stage = 1 +/datum/disease2/effect/gibbingtons + name = "Gibbingtons Syndrome" + stage = 4 + badness = VIRUS_EXOTIC + +/datum/disease2/effect/gibbingtons/activate(mob/living/carbon/human/mob, multiplier) + mob.adjustBruteLoss(10*multiplier) + var/obj/item/organ/external/O = pick(mob.organs) + if(prob(25)) + to_chat(mob, "Your [O.name] feels as if it might burst!") + if(prob(10)) + if(O) + O.droplimb(0,DROPLIMB_BLUNT) + + +/datum/disease2/effect/radian + name = "Radian's Syndrome" + stage = 4 + multiplier_max = 3 + badness = VIRUS_COMMON + +/datum/disease2/effect/radian/activate(mob/living/carbon/human/mob,multiplier) + mob.apply_damage(2*multiplier, DAMAGE_RADIATION, armor_pen = 100) + + /datum/disease2/effect/killertoxins name = "Toxification Syndrome" stage = 4 @@ -146,6 +171,18 @@ for (var/obj/item/organ/external/E in mob.organs) E.min_broken_damage = initial(E.min_broken_damage) +/datum/disease2/effect/spiderfication + name = "Hatching Syndrome" + stage = 4 + badness = VIRUS_ENGINEERED + chance_max = 30 + delay = 60 SECONDS + +/datum/disease2/effect/spiderfication/activate(mob/living/carbon/human/mob, multiplier) + var/obj/spider/spiderling/S = new /obj/spider/spiderling(get_turf(mob)) + mob.emote("cough") + to_chat(mob, "You cough up the [S]!") + ////////////////////////STAGE 3///////////////////////////////// /datum/disease2/effect/toxins @@ -277,6 +314,17 @@ if (prob(30)) mob.jitteriness = min(mob.jitteriness + 10, 500) +/datum/disease2/effect/hair + name = "Hair Loss" + stage = 2 + badness = VIRUS_COMMON + +/datum/disease2/effect/hair/activate(mob/living/carbon/human/mob, multiplier) + if(mob.species.name == SPECIES_HUMAN && !(mob.head_hair_style == "Bald") && !(mob.head_hair_style == "Balding Hair")) + to_chat(mob, "Your hair starts to fall out in clumps...") + mob.head_hair_style = "Balding Hair" + mob.update_hair() + ////////////////////////STAGE 1///////////////////////////////// /datum/disease2/effect/sneeze @@ -339,3 +387,10 @@ delay = 25 SECONDS /datum/disease2/effect/stomach/activate(mob/living/carbon/human/mob, multiplier) to_chat(mob, "Your stomach feels heavy.") + +/datum/disease2/effect/stealth + name = "Silent Death Syndrome" + stage = 1 + badness = VIRUS_EXOTIC + chance_max = 0 + allow_multiple = 1 diff --git a/mods/virusology/code/general.dm b/mods/virusology/code/general.dm index 18d2b5f4dd779..f3a798b420598 100644 --- a/mods/virusology/code/general.dm +++ b/mods/virusology/code/general.dm @@ -7,6 +7,19 @@ /datum/species/machine virus_immune = 1 +/datum/species/vox + virus_immune = 1 + +/datum/species/starlight + virus_immune = 1 + +/datum/species/alium + virus_immune = 1 + +/datum/species/mantid + virus_immune = 1 + + /datum/species/shapeshifter/promethean virus_immune = 1 diff --git a/mods/virusology/code/helpers.dm b/mods/virusology/code/helpers.dm index fd670b03c9374..74bc3efbf3293 100644 --- a/mods/virusology/code/helpers.dm +++ b/mods/virusology/code/helpers.dm @@ -94,7 +94,7 @@ return if ("[disease.uniqueID]" in M.virus2) return - if(LAZYLEN(M.virus2) >= 3) // cap the number of viruses a mob can have to 3 + if(LAZYLEN(M.virus2) >= 2) // cap the number of viruses a mob can have to 2 return // if one of the antibodies in the mob's body matches one of the disease's antigens, don't infect var/list/antibodies_in_common = M.antibodies & disease.antigen @@ -116,8 +116,6 @@ var/mob_infection_prob = infection_chance(M, disease.spreadtype) * M.immunity_weakness() if(forced || (prob(disease.infectionchance) && prob(mob_infection_prob))) var/datum/disease2/disease/D = disease.getcopy() - if(rand(1, 50) == 1) - D.minormutate() // log_debug("Adding virus") M.virus2["[D.uniqueID]"] = D SET_BIT(M.hud_updateflag, STATUS_HUD) diff --git a/mods/virusology/code/items_devices.dm b/mods/virusology/code/items_devices.dm index 0383a57b050d7..b572283f57b9a 100644 --- a/mods/virusology/code/items_devices.dm +++ b/mods/virusology/code/items_devices.dm @@ -3,7 +3,7 @@ /obj/item/device/scanner/antibody_scanner name = "antibody scanner" desc = "Scans living beings for antibodies in their blood." - icon = 'icons/obj/tools/health_analyzer.dmi' + icon = 'mods/virusology/icons/virology.dmi' icon_state = "health" item_state = "analyzer" w_class = ITEM_SIZE_SMALL @@ -59,7 +59,7 @@ /obj/item/virusdish/use_tool(obj/item/W, mob/living/user, list/click_params) . = ..() - if(istype(W, /obj/item/hand_labeler) || istype(W, /obj/item/reagent_containers/syringe)) + if(istype(W, /obj/item/hand_labeler) || istype(W, /obj/item/reagent_containers)) return ..() if(prob(50)) @@ -88,7 +88,7 @@ /obj/item/ruinedvirusdish/use_tool(obj/item/W, mob/living/user, list/click_params) . = ..() - if(istype(W,/obj/item/hand_labeler) || istype(W,/obj/item/reagent_containers/syringe)) + if(istype(W,/obj/item/hand_labeler) || istype(W,/obj/item/reagent_containers)) return ..() if(prob(50)) diff --git a/mods/virusology/code/lar_maria.dm b/mods/virusology/code/lar_maria.dm index f9d081f6c5b97..ec7fa057d4c63 100644 --- a/mods/virusology/code/lar_maria.dm +++ b/mods/virusology/code/lar_maria.dm @@ -46,6 +46,16 @@ to_chat(mob, "You feel uncontrollable rage filling you! You want to hurt and destroy!") if (mob.reagents.get_reagent_amount(/datum/reagent/hyperzine) < 10) mob.reagents.add_reagent(/datum/reagent/hyperzine, 4) + var/list/mobs_to_beat = list() + for(var/mob/living/L in range(1)) + if (L == mob) + continue + mobs_to_beat += L + if (LAZYLEN(mobs_to_beat) < 1)//nobody to beat + return + var/mob/living/Target = pick(mobs_to_beat) + mob.a_intent = I_HURT + mob.attack_hand(Target) if(prob(50) && mob.check_has_mouth())//go crazy and bite someone var/list/mouth_status = mob.can_eat_status() if (mouth_status[1] == 1)//if no mouth HUMAN_EATING_NBP_MOUTH @@ -55,13 +65,13 @@ to_chat(mob, "You angrily chew \the [mouth_status[2]] covering your mouth!") return var/list/mobs_to_bite = list() - for (var/mob/living/carbon/human/L in range(1)) + for (var/mob/living/carbon/L in range(1)) if (L == mob) continue mobs_to_bite += L if (LAZYLEN(mobs_to_bite) < 1)//nobody to bite return - var/mob/living/carbon/human/Target = pick(mobs_to_bite) + var/mob/living/Target = pick(mobs_to_bite) mob.visible_message("[mob] violently bites [Target]!") Target.adjustBruteLoss(5) if (prob(50)) diff --git a/mods/virusology/code/pre_made_viruses.dm b/mods/virusology/code/pre_made_viruses.dm index 3a38498e293d6..0ce45ba7c9d95 100644 --- a/mods/virusology/code/pre_made_viruses.dm +++ b/mods/virusology/code/pre_made_viruses.dm @@ -50,6 +50,12 @@ E3.chance = 2 effects += E3 E3.multiplier = rand(1,E3.multiplier_max) + var/datum/disease2/effect/spiderfication/E4 = new() + E4.stage = 4 + E4.chance = 2 + effects += E4 + E4.multiplier = rand(1,E4.multiplier_max) + /mob/living/simple_animal/hostile/giant_spider @@ -100,11 +106,16 @@ E1.multiplier = rand(1,E1.multiplier_max) var/datum/disease2/effect/hungry/E2 = new() E2.stage = 2 - E1.chance = 2 + E2.chance = 2 effects += E2 E2.multiplier = rand(1,E2.multiplier_max) var/datum/disease2/effect/mutation/E3 = new() E3.stage = 3 - E1.chance = 2 + E3.chance = 2 effects += E3 E3.multiplier = rand(1,E3.multiplier_max) + var/datum/disease2/effect/gibbingtons/E4 = new() + E4.stage = 4 + E4.chance = 2 + effects += E4 + E4.multiplier = rand(1,E4.multiplier_max) diff --git a/mods/virusology/icons/virology.dmi b/mods/virusology/icons/virology.dmi index 93369d83f42a5..5a2abb2120850 100644 Binary files a/mods/virusology/icons/virology.dmi and b/mods/virusology/icons/virology.dmi differ diff --git a/packs/legion/legion_beacon.dm b/packs/legion/legion_beacon.dm index 66eb852b6a822..4132aab15b71e 100644 --- a/packs/legion/legion_beacon.dm +++ b/packs/legion/legion_beacon.dm @@ -24,7 +24,7 @@ var/sensor_range = 8 /// Integer. Time between mob spawns. - var/spawn_rate = 5 SECONDS + var/spawn_rate = 10 SECONDS /// Integer. `world.time` of the last mob spawn. var/last_spawn_time = 0 @@ -88,6 +88,7 @@ if (BEACON_STATE_ON) if (world.time < last_spawn_time + spawn_rate) + last_spawn_time = world.time return if (length(linked_mobs) >= max_active_bots) return diff --git a/sound/effects/ling_horror.ogg b/sound/effects/ling_horror.ogg new file mode 100644 index 0000000000000..3dc6450bab89d Binary files /dev/null and b/sound/effects/ling_horror.ogg differ diff --git a/sound/effects/ling_shriek.ogg b/sound/effects/ling_shriek.ogg new file mode 100644 index 0000000000000..5dd92382720fa Binary files /dev/null and b/sound/effects/ling_shriek.ogg differ diff --git a/sound/effects/ling_shriek2.ogg b/sound/effects/ling_shriek2.ogg new file mode 100644 index 0000000000000..3292fff8e7db7 Binary files /dev/null and b/sound/effects/ling_shriek2.ogg differ diff --git a/test/check-paths.sh b/test/check-paths.sh index 9f3300ab701d6..60dc47576b5e4 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -48,7 +48,7 @@ exactly 27 "text2path uses" 'text2path' exactly 5 "update_icon() override" '/update_icon\((.*)\)' -P exactly 4 "goto use" 'goto ' exactly 1 "NOOP match" 'NOOP' -exactly 332 "spawn uses" '^\s*spawn\s*\(\s*(-\s*)?\d*\s*\)' -P +exactly 323 "spawn uses" '^\s*spawn\s*\(\s*(-\s*)?\d*\s*\)' -P exactly 0 "tag uses" '\stag = ' -P '**/*.dmm' exactly 0 "anchored = 0/1" 'anchored\s*=\s*\d' -P exactly 2 "density = 0/1" 'density\s*=\s*\d' -P