diff --git a/.github/workflows/turdis.yml b/.github/workflows/turdis.yml index dbde0b434455..b09e3401c797 100644 --- a/.github/workflows/turdis.yml +++ b/.github/workflows/turdis.yml @@ -39,13 +39,17 @@ jobs: - name: Misc Checks run: | - python tools/travis/check_dme_alphabetical.py yogstation.dme tools/travis/check_filedirs.sh yogstation.dme tools/travis/check_changelogs.sh find . -name "*.json" -not -path "./tgui/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py tools/build/build --ci lint tgui-test tools/travis/check_grep.sh + - name: Ticked File Enforcement + run: | + python tools/travis/ticked_file_enforcement/ticked_file_enforcement.py < tools/travis/ticked_file_enforcement/schemas/yogstation_dme.json + python tools/travis/ticked_file_enforcement/ticked_file_enforcement.py < tools/travis/ticked_file_enforcement/schemas/unit_tests.json + - name: Run Linter id: linter run: | diff --git a/code/__DEFINES/_readme.dm b/code/__DEFINES/_readme.dm deleted file mode 100644 index 42ad52286fee..000000000000 --- a/code/__DEFINES/_readme.dm +++ /dev/null @@ -1,14 +0,0 @@ -/* - This folder is full of #define statements. They are similar to constants, - but must come before any code that references them, and they do not take up - memory the way constants do. - - The values in this folder are NOT options. They are not for hosts to play with. - Some of the values are arbitrary and only need to be different from similar constants; - for example, the genetic mutation numbers in genetics.dm mean nothing, but MUST be distinct. - - It is wise not to touch them unless you understand what they do, where they're used, - and most importantly, - how to undo your changes if you screw it up. - - Sayu -*/ \ No newline at end of file diff --git a/code/__HELPERS/unused.dm b/code/__HELPERS/unused.dm deleted file mode 100644 index 8280bb73b7f2..000000000000 --- a/code/__HELPERS/unused.dm +++ /dev/null @@ -1,38 +0,0 @@ - - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(src_x, src_y, rotation, angle, power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) diff --git a/code/controllers/subsystem/sound.dm b/code/controllers/subsystem/sound.dm deleted file mode 100644 index 376f1faa746f..000000000000 --- a/code/controllers/subsystem/sound.dm +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Sound subsystem: - * Used for things that need constant updating (sound fading in / out) -*/ - -SUBSYSTEM_DEF(sound_effects) - name = "Sound" - wait = 1 - priority = FIRE_PRIORITY_AMBIENCE - flags = SS_NO_INIT - //Note: Make sure you update this if you use sound fading pre-game - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - - var/list/acting_effects = list() //key = sound, value = datum - var/list/currentrun = list() - -/datum/controller/subsystem/sound_effects/fire(resumed = 0) - if (!resumed) - src.currentrun = acting_effects.Copy() - - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - - while(LAZYLEN(currentrun)) - var/datum/sound_effect/sound_effect = currentrun[currentrun[currentrun.len]] - currentrun.len-- - - sound_effect.update_effect() - - if(world.time > sound_effect.end_tick) - sound_effect.end_effect() - acting_effects -= sound_effect.effect_id - - if (MC_TICK_CHECK) - return - -// ===== Sound effect procs ===== - -/proc/sound_fade(sound/S, start_volume = 100, end_volume = 0, time = 10, var/listeners) - //Check basics - if(!S) - CRASH("sound_fade called without a sound file.") - if(!listeners) - return - //Check in list format - var/listeners_list = listeners - if(!islist(listeners_list)) - listeners_list = list(listeners) - //Create datum - new /datum/sound_effect/fade(S, listeners_list, time, start_volume, end_volume) - -// ===== Sound effect datum ===== - -/datum/sound_effect - var/name = "null" - var/sound/sound - var/list/listeners - var/start_tick - var/end_tick - var/effect_id - -/datum/sound_effect/New(S, list/_listeners, time) - . = ..() - sound = S - listeners = _listeners - start_tick = world.time - end_tick = world.time + time - effect_id = generate_id() - start_sound() - -/datum/sound_effect/proc/generate_id() - var/id = "[name][sound.file]" - for(var/A in listeners) - id = "[id][REF(A)]" - return id - -/datum/sound_effect/proc/send_sound() - for(var/receiver in listeners) - SEND_SOUND(receiver, sound) - -/datum/sound_effect/proc/update_effect() - return //Not implemented - -/datum/sound_effect/proc/end_effect() - return //Not implemented - -// Send the sound to the person it's affecting and add it to the sound subsystem. -// Should be overridden to account for if an effect is already playing for that sound. -/datum/sound_effect/proc/start_sound() - send_sound() - SSsound_effects.acting_effects[effect_id] = src - -//============== Fade ============= - -/datum/sound_effect/fade - name = "fade" - var/in_vol - var/out_vol - //Calculated - var/current_vol - -/datum/sound_effect/fade/New(S, list/_listeners, time, start_vol, end_vol) - in_vol = start_vol - out_vol = end_vol - . = ..(S, _listeners, time) - -/datum/sound_effect/fade/start_sound() - //If the sound is already playing, make it fade from the current point - if(SSsound_effects.acting_effects[effect_id]) - var/datum/sound_effect/fade/old_sound = SSsound_effects.acting_effects[effect_id] - in_vol = old_sound.current_vol - else - send_sound() - SSsound_effects.acting_effects[effect_id] = src - -/datum/sound_effect/fade/update_effect() - var/time_multiplier = clamp((world.time - start_tick) / (end_tick - start_tick), 0, 1) - current_vol = (time_multiplier * out_vol) + ((1-time_multiplier) * in_vol) - sound.status = SOUND_UPDATE - sound.volume = current_vol - - for(var/receiver in listeners) - SEND_SOUND(receiver, sound) - -/datum/sound_effect/fade/end_effect() - if(!out_vol) - sound.repeat = FALSE diff --git a/code/datums/components/acid.dm b/code/datums/components/acid.dm deleted file mode 100644 index e870ae19faf0..000000000000 --- a/code/datums/components/acid.dm +++ /dev/null @@ -1,192 +0,0 @@ -/** Component representing acid applied to an object. - * - * Must be attached to an atom. - * Processes, repeatedly damaging whatever it is attached to. - * If the parent atom is a turf it applies acid to the contents of the turf. - */ -/datum/component/acid - dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS - /// The strength of the acid on the parent [/atom]. - var/acid_power - /// The volume of acid on the parent [/atom]. - var/acid_volume - /// The maximum volume of acid on the parent [/atom]. - var/max_volume = INFINITY - /// The ambiant sound of acid eating away at the parent [/atom]. - var/datum/looping_sound/acid/sizzle - /// Used exclusively for melting turfs. TODO: Move integrity to the atom level so that this can be dealt with there. - var/parent_integrity = 30 - /// How far the acid melting of turfs has progressed - var/stage = 0 - /// The proc used to handle the parent [/atom] when processing. TODO: Unify damage and resistance flags so that this doesn't need to exist! - var/datum/callback/process_effect - -/datum/component/acid/Initialize(_acid_power, _acid_volume, _max_volume=null) - if((_acid_power) <= 0 || (_acid_volume <= 0)) - stack_trace("Acid component added with insufficient acid power ([_acid_power]) or acid volume ([_acid_power]).") - return COMPONENT_INCOMPATIBLE // Not enough acid or the acid's too weak, either one. - if(!isatom(parent)) - stack_trace("Acid component added to [parent] ([parent?.type]) which is not a /atom subtype.") - return COMPONENT_INCOMPATIBLE // Incompatible type. TODO: Rework take_damage to the atom level and move this there. - if(isobj(parent)) - var/obj/parent_object = parent - if(parent_object.resistance_flags & UNACIDABLE) // The parent object cannot have acid. Should never happen, will happen. - stack_trace("Acid component added to unacidable object [parent].") - return COMPONENT_INCOMPATIBLE - max_volume = OBJ_ACID_VOLUME_MAX - process_effect = CALLBACK(src, PROC_REF(process_obj), parent) - else if(isliving(parent)) - max_volume = MOB_ACID_VOLUME_MAX - process_effect = CALLBACK(src, PROC_REF(process_mob), parent) - else if(isturf(parent)) - max_volume = TURF_ACID_VOLUME_MAX - process_effect = CALLBACK(src, PROC_REF(process_turf), parent) - acid_power = _acid_power - set_volume(_acid_volume) - var/atom/parent_atom = parent - RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) - parent_atom.update_appearance(UPDATE_ICON) - sizzle = new(list(parent), TRUE) - START_PROCESSING(SSacid, src) -/datum/component/acid/Destroy(force, silent) - STOP_PROCESSING(SSacid, src) - if(sizzle) - QDEL_NULL(sizzle) - if(process_effect) - QDEL_NULL(process_effect) - UnregisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS) - if(parent && !QDELING(parent)) - var/atom/parent_atom = parent - parent_atom.update_appearance(UPDATE_ICON) - return ..() -/datum/component/acid/RegisterWithParent() - RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean)) - RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand)) - RegisterSignal(parent, COMSIG_ATOM_EXPOSE_REAGENT, PROC_REF(on_expose_reagent)) - if(isturf(parent)) - RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, PROC_REF(on_crossed)) -/datum/component/acid/UnregisterFromParent() - UnregisterSignal(parent, list( - COMSIG_PARENT_EXAMINE, - COMSIG_COMPONENT_CLEAN_ACT, - COMSIG_ATOM_ATTACK_HAND, - COMSIG_ATOM_EXPOSE_REAGENT)) - if(isturf(parent)) - UnregisterSignal(parent, COMSIG_MOVABLE_CROSSED) -/// Averages corrosive power and sums volume. -/datum/component/acid/InheritComponent(datum/component/C, i_am_original, _acid_power, _acid_volume) - acid_power = ((acid_power * acid_volume) + (_acid_power * _acid_volume)) / (acid_volume + _acid_volume) - set_volume(acid_volume + _acid_volume) -/// Sets the acid volume to a new value. Limits the acid volume by the amount allowed to exist on the parent atom. -/datum/component/acid/proc/set_volume(new_volume) - acid_volume = clamp(new_volume, 0, max_volume) - if(!acid_volume) - qdel(src) - - -/// Handles the slow corrosion of the parent [/atom]. -/datum/component/acid/process(delta_time) - process_effect?.InvokeAsync(delta_time) - if(QDELING(src)) //The process effect deals damage, and on turfs diminishes the acid volume, potentially destroying the component. Let's not destroy it twice. - return - set_volume(acid_volume - (ACID_DECAY_BASE + (ACID_DECAY_SCALING*round(sqrt(acid_volume)))) * delta_time) - -/// Handles processing on a [/obj]. -/datum/component/acid/proc/process_obj(obj/target, delta_time) - if(target.resistance_flags & ACID_PROOF) - return - target.take_damage(min(1 + round(sqrt(acid_power * acid_volume)*0.3), OBJ_ACID_DAMAGE_MAX) * delta_time, BURN, ACID, 0) - -/// Handles processing on a [/mob/living]. -/datum/component/acid/proc/process_mob(mob/living/target, delta_time) - target.acid_act(acid_power, acid_volume * delta_time) - -/// Handles processing on a [/turf]. -/datum/component/acid/proc/process_turf(turf/target_turf, delta_time) - var/acid_used = min(acid_volume * 0.05, 20) * delta_time - var/applied_targets = 0 - for(var/am in target_turf) - var/atom/movable/target_movable = am - if(target_movable.acid_act(acid_power, acid_used)) - applied_targets++ - if(applied_targets) - set_volume(acid_volume - (acid_used * applied_targets)) - // Snowflake code for handling acid melting walls. TODO: Move integrity handling to the atom level so this can be desnowflaked. - if(acid_power < ACID_POWER_MELT_TURF) - return - - parent_integrity -= delta_time - if(parent_integrity <= 0) - target_turf.visible_message("[target_turf] collapses under its own weight into a puddle of goop and undigested debris!") - target_turf.acid_melt() - else if(parent_integrity <= 4 && stage <= 3) - target_turf.visible_message("[target_turf] begins to crumble under the acid!") - stage = 4 - else if(parent_integrity <= 8 && stage <= 2) - target_turf.visible_message("[target_turf] is struggling to withstand the acid!") - stage = 3 - else if(parent_integrity <= 16 && stage <= 1) - target_turf.visible_message("[target_turf] is being melted by the acid!") - stage = 2 - else if(parent_integrity <= 24 && stage == 0) - target_turf.visible_message("[target_turf] is holding up against the acid!") - stage = 1 - -/// Used to maintain the acid overlay on the parent [/atom]. -/datum/component/acid/proc/on_update_overlays(atom/parent_atom, list/overlays) - SIGNAL_HANDLER - overlays += mutable_appearance('icons/effects/acid.dmi', parent_atom.custom_acid_overlay || ACID_OVERLAY_DEFAULT) -/// Alerts any examiners to the acid on the parent atom. -/datum/component/acid/proc/on_examine(atom/A, mob/user, list/examine_list) - SIGNAL_HANDLER - examine_list += "[A.p_theyre()] covered in corrosive liquid!" -/// Makes it possible to clean acid off of objects. -/datum/component/acid/proc/on_clean(atom/A, clean_types) - SIGNAL_HANDLER - if(!(clean_types & CLEAN_TYPE_ACID)) - return NONE - qdel(src) - return COMPONENT_CLEANED -/// Handles water diluting the acid on the object. -/datum/component/acid/proc/on_expose_reagent(atom/parent_atom, datum/reagent/exposing_reagent, reac_volume) - SIGNAL_HANDLER - if(!istype(exposing_reagent, /datum/reagent/water)) - return NONE - acid_power /= (acid_volume / (acid_volume + reac_volume)) - set_volume(acid_volume + reac_volume) - return NONE -/// Handles searing the hand of anyone who tries to touch this without protection. -/datum/component/acid/proc/on_attack_hand(atom/parent_atom, mob/living/carbon/user) - SIGNAL_HANDLER - if(!istype(user)) - return NONE - if((parent_atom == user) || (parent_atom.loc == user)) - return NONE // So people can take their own clothes off. - if((acid_power * acid_volume) < ACID_LEVEL_HANDBURN) - return NONE - if(user.gloves?.resistance_flags & (UNACIDABLE|ACID_PROOF)) - return NONE - var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm") - if(!affecting?.receive_damage(0, 5)) - return NONE - to_chat(user, "The acid on \the [parent_atom] burns your hand!") - playsound(parent_atom, 'sound/weapons/sear.ogg', 50, TRUE) - user.update_damage_overlays() - return COMPONENT_NO_ATTACK_HAND -/// Handles searing the feet of whoever walks over this without protection. Only active if the parent is a turf. -/datum/component/acid/proc/on_crossed(atom/parent_atom, mob/living/crosser) - SIGNAL_HANDLER - if(!isliving(crosser)) - return - if(crosser.movement_type & FLYING) - return - if(crosser.m_intent & MOVE_INTENT_WALK) - return - if(prob(60)) - return - var/acid_used = min(acid_volume * 0.05, 20) - if(crosser.acid_act(acid_power, acid_used, FEET)) - playsound(crosser, 'sound/weapons/sear.ogg', 50, TRUE) - to_chat(crosser, "The acid on the [parent] burns you!") - set_volume(max(acid_volume - acid_used, 10)) diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm deleted file mode 100644 index d657c0c38aae..000000000000 --- a/code/modules/admin/sql_message_system.dm +++ /dev/null @@ -1,721 +0,0 @@ -//YOGS - FILE MOVED TO yogstation/code/modules/admin/sql_message_system.dm -/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - if(!type) - return - var/target_ckey = ckey(target_key) - if(!target_key && (type == "note" || type == "message" || type == "watchlist entry")) - var/new_key = input(usr,"Who would you like to create a [type] for?","Enter a key or ckey",null) as null|text - if(!new_key) - return - var/new_ckey = ckey(new_key) - var/datum/DBQuery/query_find_ckey = SSdbcore.NewQuery( - "SELECT ckey FROM [format_table_name("player")] WHERE ckey = :ckey", - list("ckey" = new_ckey) - ) - if(!query_find_ckey.warn_execute()) - qdel(query_find_ckey) - return - if(!query_find_ckey.NextRow()) - if(tgui_alert(usr, "[new_key]/([new_ckey]) has not been seen before, are you sure you want to create a [type] for them?", "Unknown ckey", list("Yes", "No", "Cancel")) != "Yes") - qdel(query_find_ckey) - return - qdel(query_find_ckey) - target_ckey = new_ckey - target_key = new_key - if(QDELETED(usr)) - return - if(!target_key) - target_key = target_ckey - if(!admin_ckey) - admin_ckey = usr.ckey - if(!admin_ckey) - return - if(!target_ckey) - target_ckey = admin_ckey - if(!text) - text = input(usr,"Write your [type]","Create [type]") as null|message - if(!text) - return - if(!timestamp) - timestamp = SQLtime() - if(!server) - var/ssqlname = CONFIG_GET(string/serversqlname) - if (ssqlname) - server = ssqlname - if(isnull(secret)) - switch(tgui_alert(usr,"Hide note from being viewed by players?", "Secret note?",list("Yes","No","Cancel"))) - if("Yes") - secret = 1 - if("No") - secret = 0 - else - return - if(isnull(expiry)) - if(tgui_alert(usr, "Set an expiry time? Expired messages are hidden like deleted ones.", "Expiry time?", list("Yes", "No", "Cancel")) == "Yes") - var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons.", "Set expiry time", SQLtime()) as null|text - if(!expire_time) - return - var/datum/DBQuery/query_validate_expire_time = SSdbcore.NewQuery( - "SELECT IF(STR_TO_DATE(:expire_time,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:expire_time,'%Y-%c-%d %T'), 0)", - list("expire_time" = expire_time) - ) - if(!query_validate_expire_time.warn_execute()) - qdel(query_validate_expire_time) - return - if(query_validate_expire_time.NextRow()) - var/checktime = text2num(query_validate_expire_time.item[1]) - if(!checktime) - to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential=TRUE) - qdel(query_validate_expire_time) - return - expiry = query_validate_expire_time.item[1] - qdel(query_validate_expire_time) - if(type == "note" && isnull(note_severity)) - note_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") - if(!note_severity) - return - var/datum/DBQuery/query_create_message = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity) - VALUES (:type, :target_ckey, :admin_ckey, :text, :timestamp, :server, INET_ATON(:internet_address), :port, :round_id, :secret, :expiry, :note_severity) - "}, list( - "type" = type, - "target_ckey" = target_ckey, - "admin_ckey" = admin_ckey, - "text" = text, - "timestamp" = timestamp, - "server" = server, - "internet_address" = world.internet_address || "0", - "port" = "[world.port]", - "round_id" = GLOB.round_id, - "secret" = secret, - "expiry" = expiry || null, - "note_severity" = note_severity, - )) - var/pm = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]: [text]" - var/header = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]" // yogs - Yog Tickets - if(!query_create_message.warn_execute()) - qdel(query_create_message) - return - qdel(query_create_message) - if(logged) - log_admin_private(pm) - message_admins("[header]:
[text]") - admin_ticket_log(target_ckey, header) // yogs - Yog Tickets - admin_ticket_log(target_ckey, text) - if(browse) - browse_messages("[type]") - else - browse_messages(target_ckey = target_ckey, agegate = TRUE) - -/proc/delete_message(message_id, logged = 1, browse) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - message_id = text2num(message_id) - if(!message_id) - return - var/type - var/target_key - var/text - var/user_key_name = key_name(usr) - var/user_name_admin = key_name_admin(usr) - var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery( - "SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = :id AND deleted = 0", - list("id" = message_id) - ) - - if(!query_find_del_message.warn_execute()) - qdel(query_find_del_message) - return - if(query_find_del_message.NextRow()) - type = query_find_del_message.item[1] - target_key = query_find_del_message.item[2] - text = query_find_del_message.item[3] - qdel(query_find_del_message) - - var/datum/DBQuery/query_del_message = SSdbcore.NewQuery( - "UPDATE [format_table_name("messages")] SET deleted = 1 WHERE id = :id", - list("id" = message_id) - ) - - if(!query_del_message.warn_execute()) - qdel(query_del_message) - return - qdel(query_del_message) - if(logged) - var/m1 = "[user_key_name] has deleted a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for" : " made by"] [target_key]: [text]" - var/m2 = "[user_name_admin] has deleted a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for" : " made by"] [target_key]:
[text]" - log_admin_private(m1) - message_admins(m2) - if(browse) - browse_messages("[type]") - else - browse_messages(target_ckey = ckey(target_key), agegate = TRUE) - -/proc/edit_message(message_id, browse) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - message_id = text2num(message_id) - if(!message_id) - return - var/editor_ckey = usr.ckey - var/editor_key = usr.key - var/kn = key_name(usr) - var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery({" - SELECT - type, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), - text - FROM [format_table_name("messages")] - WHERE id = :id AND deleted = 0 - "}, list("id" = message_id)) - if(!query_find_edit_message.warn_execute()) - qdel(query_find_edit_message) - return - if(query_find_edit_message.NextRow()) - var/type = query_find_edit_message.item[1] - var/target_key = query_find_edit_message.item[2] - var/admin_key = query_find_edit_message.item[3] - var/old_text = query_find_edit_message.item[4] - var/new_text = input("Input new [type]", "New [type]", "[old_text]") as null|message - if(!new_text) - qdel(query_find_edit_message) - return - var/edit_text = "Edited by [editor_key] on [SQLtime()] from
[old_text]
to
[new_text]
" - var/datum/DBQuery/query_edit_message = SSdbcore.NewQuery({" - UPDATE [format_table_name("messages")] - SET text = :text, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text) - WHERE id = :id AND deleted = 0 - "}, list("text" = new_text, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id)) - if(!query_edit_message.warn_execute()) - qdel(query_edit_message) - return - qdel(query_edit_message) - log_admin_private("[kn] has edited a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_text] to [new_text]") - message_admins("[kna] has edited a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from
[old_text]
to
[new_text]") - if(browse) - browse_messages("[type]") - else - browse_messages(target_ckey = ckey(target_key), agegate = TRUE) - qdel(query_find_edit_message) - -/proc/edit_message_expiry(message_id, browse) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - message_id = text2num(message_id) - if(!message_id) - return - var/editor_ckey = usr.ckey - var/editor_key = usr.key - var/kn = key_name(usr) - var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_edit_expiry_message = SSdbcore.NewQuery({" - SELECT - type, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), - expire_timestamp - FROM [format_table_name("messages")] - WHERE id = :id AND deleted = 0 - "}, list("id" = message_id)) - if(!query_find_edit_expiry_message.warn_execute()) - qdel(query_find_edit_expiry_message) - return - if(query_find_edit_expiry_message.NextRow()) - var/type = query_find_edit_expiry_message.item[1] - var/target_key = query_find_edit_expiry_message.item[2] - var/admin_key = query_find_edit_expiry_message.item[3] - var/old_expiry = query_find_edit_expiry_message.item[4] - var/new_expiry - var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons. Enter -1 to remove expiry time.", "Set expiry time", old_expiry) as null|text - if(!expire_time) - qdel(query_find_edit_expiry_message) - return - if(expire_time == "-1") - new_expiry = "non-expiring" - else - var/datum/DBQuery/query_validate_expire_time_edit = SSdbcore.NewQuery({" - SELECT IF(STR_TO_DATE(:expire_time,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:expire_time,'%Y-%c-%d %T'), 0) - "}, list("expire_time" = expire_time)) - if(!query_validate_expire_time_edit.warn_execute()) - qdel(query_validate_expire_time_edit) - qdel(query_find_edit_expiry_message) - return - if(query_validate_expire_time_edit.NextRow()) - var/checktime = text2num(query_validate_expire_time_edit.item[1]) - if(!checktime) - to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.", confidential=TRUE) - qdel(query_validate_expire_time_edit) - qdel(query_find_edit_expiry_message) - return - new_expiry = query_validate_expire_time_edit.item[1] - qdel(query_validate_expire_time_edit) - var/edit_text = "Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]
" - var/datum/DBQuery/query_edit_message_expiry = SSdbcore.NewQuery({" - UPDATE [format_table_name("messages")] - SET expire_timestamp = :expire_time, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text) - WHERE id = :id AND deleted = 0 - "}, list("expire_time" = (expire_time == "-1" ? null : new_expiry), "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id)) - if(!query_edit_message_expiry.warn_execute()) - qdel(query_edit_message_expiry) - qdel(query_find_edit_expiry_message) - return - qdel(query_edit_message_expiry) - log_admin_private("[kn] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]") - message_admins("[kna] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]") - if(browse) - browse_messages("[type]") - else - browse_messages(target_ckey = ckey(target_key), agegate = TRUE) - qdel(query_find_edit_expiry_message) - -/proc/edit_message_severity(message_id) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - message_id = text2num(message_id) - if(!message_id) - return - var/kn = key_name(usr) - var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_edit_note_severity = SSdbcore.NewQuery({" - SELECT - type, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), - severity - FROM [format_table_name("messages")] - WHERE id = :id AND deleted = 0 - "}, list("id" = message_id)) - if(!query_find_edit_note_severity.warn_execute()) - qdel(query_find_edit_note_severity) - return - if(query_find_edit_note_severity.NextRow()) - var/type = query_find_edit_note_severity.item[1] - var/target_key = query_find_edit_note_severity.item[2] - var/admin_key = query_find_edit_note_severity.item[3] - var/old_severity = query_find_edit_note_severity.item[4] - if(!old_severity) - old_severity = "NA" - var/editor_key = usr.key - var/editor_ckey = usr.ckey - var/new_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("high", "medium", "minor", "none") //lowercase for edit log consistency - if(!new_severity) - qdel(query_find_edit_note_severity) - return - var/edit_text = "Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]
" - var/datum/DBQuery/query_edit_note_severity = SSdbcore.NewQuery({" - UPDATE [format_table_name("messages")] - SET severity = :severity, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text) - WHERE id = :id AND deleted = 0 - "}, list("severity" = new_severity, "lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id)) - if(!query_edit_note_severity.warn_execute(async = TRUE)) - qdel(query_edit_note_severity) - qdel(qdel(query_find_edit_note_severity)) - return - qdel(query_edit_note_severity) - log_admin_private("[kn] has edited the severity of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]") - message_admins("[kna] has edited the severity time of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]") - browse_messages(target_ckey = ckey(target_key), agegate = TRUE) - qdel(query_find_edit_note_severity) - -/proc/toggle_message_secrecy(message_id) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - message_id = text2num(message_id) - if(!message_id) - return - var/editor_ckey = usr.ckey - var/editor_key = usr.key - var/kn = key_name(usr) - var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery({" - SELECT - type, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), - secret - FROM [format_table_name("messages")] - WHERE id = :id AND deleted = 0 - "}, list("id" = message_id)) - if(!query_find_message_secret.warn_execute()) - qdel(query_find_message_secret) - return - if(query_find_message_secret.NextRow()) - var/type = query_find_message_secret.item[1] - var/target_key = query_find_message_secret.item[2] - var/admin_key = query_find_message_secret.item[3] - var/secret = text2num(query_find_message_secret.item[4]) - var/edit_text = "Made [secret ? "not secret" : "secret"] by [editor_key] on [SQLtime()]
" - var/datum/DBQuery/query_message_secret = SSdbcore.NewQuery({" - UPDATE [format_table_name("messages")] - SET secret = NOT secret, lasteditor = :lasteditor, edits = CONCAT(IFNULL(edits,''),:edit_text) - WHERE id = :id - "}, list("lasteditor" = editor_ckey, "edit_text" = edit_text, "id" = message_id)) - if(!query_message_secret.warn_execute()) - qdel(query_find_message_secret) - qdel(query_message_secret) - return - qdel(query_message_secret) - log_admin_private("[kn] has toggled [target_key]'s [type] made by [admin_key] to [secret ? "not secret" : "secret"]") - message_admins("[kna] has toggled [target_key]'s [type] made by [admin_key] to [secret ? "not secret" : "secret"]") - browse_messages(target_ckey = ckey(target_key), agegate = TRUE) - qdel(query_find_message_secret) - -/proc/browse_messages(type, target_ckey, index, linkless = FALSE, filter, agegate = FALSE) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - var/list/output = list() - var/ruler = "
" - var/list/navbar = list("All#") - for(var/letter in GLOB.alphabet) - navbar += "[letter]" - navbar += "MemosWatchlist" - navbar += "
\ - \ - [HrefTokenFormField()]\ - \ -
" - if(!linkless) - output = navbar - if(type == "memo" || type == "watchlist entry") - if(type == "memo") - output += "

Admin memos

" - output += "Add memo" - else if(type == "watchlist entry") - output += "

Watchlist entries

" - output += "Add watchlist entry" - if(filter) - output += "Unfilter clients" - else - output += "Filter offline clients" - output += ruler - var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery({" - SELECT - id, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - targetckey, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), - text, - timestamp, - server, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), - expire_timestamp - FROM [format_table_name("messages")] - WHERE type = :type AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) - "}, list("type" = type)) - if(!query_get_type_messages.warn_execute()) - qdel(query_get_type_messages) - return - while(query_get_type_messages.NextRow()) - if(QDELETED(usr)) - return - var/id = query_get_type_messages.item[1] - var/t_key = query_get_type_messages.item[2] - var/t_ckey = query_get_type_messages.item[3] - if(type == "watchlist entry" && filter && !(t_ckey in GLOB.directory)) - continue - var/admin_key = query_get_type_messages.item[4] - var/text = query_get_type_messages.item[5] - var/timestamp = query_get_type_messages.item[6] - var/server = query_get_type_messages.item[7] - var/editor_key = query_get_type_messages.item[8] - var/expire_timestamp = query_get_type_messages.item[9] - output += "" - if(type == "watchlist entry") - output += "[t_key] | " - output += "[timestamp] | [server] | [admin_key]" - if(expire_timestamp) - output += " | Expires [expire_timestamp]" - output += "" - output += " Change Expiry Time" - output += " Delete" - output += " Edit" - if(editor_key) - output += " Last edit by [editor_key] (Click here to see edit log)" - output += "
[text]
" - qdel(query_get_type_messages) - if(target_ckey) - var/target_key - var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery({" - SELECT - type, - secret, - id, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), - text, - timestamp, - server, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), - DATEDIFF(NOW(), timestamp), - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), - expire_timestamp, severity - FROM [format_table_name("messages")] - WHERE type <> 'memo' AND targetckey = :targetckey AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) - ORDER BY timestamp DESC - "}, list("targetckey" = target_ckey)) - if(!query_get_messages.warn_execute()) - qdel(query_get_messages) - return - var/list/messagedata = list() - var/list/watchdata = list() - var/list/notedata = list() - var/skipped = 0 - while(query_get_messages.NextRow()) - if(QDELETED(usr)) - return - type = query_get_messages.item[1] - if(type == "memo") - continue - var/secret = text2num(query_get_messages.item[2]) - if(linkless && secret) - continue - var/id = query_get_messages.item[3] - var/admin_key = query_get_messages.item[4] - var/text = query_get_messages.item[5] - var/timestamp = query_get_messages.item[6] - var/server = query_get_messages.item[7] - var/editor_key = query_get_messages.item[8] - var/age = text2num(query_get_messages.item[9]) - target_key = query_get_messages.item[10] - var/expire_timestamp = query_get_messages.item[11] - var/severity = query_get_messages.item[12] - var/alphatext = "" - var/nsd = CONFIG_GET(number/note_stale_days) - var/nfd = CONFIG_GET(number/note_fresh_days) - if (agegate && type == "note" && isnum(nsd) && isnum(nfd) && nsd > nfd) - var/alpha = clamp(100 - (age - nfd) * (85 / (nsd - nfd)), 15, 100) - if (alpha < 100) - if (alpha <= 15) - if (skipped) - skipped++ - continue - alpha = 10 - skipped = TRUE - alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];" - var/list/data = list("

") - if(severity) - data += " " - data += "[timestamp] | [server] | [admin_key][secret ? " | - Secret" : ""]" - if(expire_timestamp) - data += " | Expires [expire_timestamp]" - data += "

" - if(!linkless) - if(type == "note") - if(severity) - data += "[severity=="none" ? "No" : "[capitalize(severity)]"] Severity" - else - data += "N/A Severity" - data += " Change Expiry Time" - data += " Delete" - if(type == "note") - data += " [secret ? "Secret" : "Not secret"]" - if(type == "message sent") - data += " Message has been sent" - if(editor_key) - data += "|" - else - data += " Edit" - if(editor_key) - data += " Last edit by [editor_key] (Click here to see edit log)" - data += "
" - data += "

[text]


" - switch(type) - if("message") - messagedata += data - if("message sent") - messagedata += data - if("watchlist entry") - watchdata += data - if("note") - notedata += data - qdel(query_get_messages) - if(!target_key) - var/datum/DBQuery/query_get_message_key = SSdbcore.NewQuery({" - SELECT byond_key FROM [format_table_name("player")] WHERE ckey = :ckey - "}, list("ckey" = target_ckey)) - if(!query_get_message_key.warn_execute()) - qdel(query_get_message_key) - return - if(query_get_message_key.NextRow()) - target_key = query_get_message_key.item[1] - qdel(query_get_message_key) - output += "

[target_key]

" - if(!linkless) - output += "Add note" - output += " Add message" - output += " Add to watchlist" - output += " Refresh page
" - else - output += " Refresh page" - output += ruler - if(messagedata) - output += "

Messages

" - output += messagedata - if(watchdata) - output += "

Watchlist

" - output += watchdata - if(notedata) - output += "

Notes

" - output += notedata - if(!linkless) - if (agegate) - if (skipped) //the first skipped message is still shown so that we can put this link over it. - output += "
Show [skipped] hidden messages
" - else - output += "
Show All
" - else - output += "
Hide Old
" - if(index) - var/search - output += "
Add messageAdd watchlist entryAdd note
" - output += ruler - switch(index) - if(1) - search = "^." - if(2) - search = "^\[^\[:alpha:\]\]" - else - search = "^[index]" - var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery({" - SELECT DISTINCT - targetckey, - (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) - FROM [format_table_name("messages")] - WHERE type <> 'memo' - AND targetckey REGEXP :search - AND deleted = 0 - AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) - ORDER BY targetckey - "}, list("search" = search)) - if(!query_list_messages.warn_execute()) - qdel(query_list_messages) - return - while(query_list_messages.NextRow()) - if(QDELETED(usr)) - return - var/index_ckey = query_list_messages.item[1] - var/index_key = query_list_messages.item[2] - output += "[index_key]
" - qdel(query_list_messages) - else if(!type && !target_ckey && !index) - output += "
Add messageAdd watchlist entryAdd note
" - output += ruler - var/datum/browser/browser = new(usr, "Note panel", "Manage player notes", 1000, 500) - var/datum/asset/notes_assets = get_asset_datum(/datum/asset/simple/notes) - notes_assets.send(usr.client) - browser.set_content(jointext(output, "")) - browser.open() - -/proc/get_message_output(type, target_ckey) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Failed to establish database connection."), confidential=TRUE) - return - if(!type) - return - var/output - var/datum/DBQuery/query_get_message_output = SSdbcore.NewQuery({" - SELECT - id, - (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), - text, - timestamp, - (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor) - FROM [format_table_name("messages")] - WHERE type = :type - AND deleted = 0 - AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) - AND ((type != 'message' AND type != 'watchlist entry') OR targetckey = :targetckey) - "}, list("targetckey" = target_ckey, "type" = type)) - if(!query_get_message_output.warn_execute()) - qdel(query_get_message_output) - return - while(query_get_message_output.NextRow()) - var/message_id = query_get_message_output.item[1] - var/admin_key = query_get_message_output.item[2] - var/text = query_get_message_output.item[3] - var/timestamp = query_get_message_output.item[4] - var/editor_key = query_get_message_output.item[5] - switch(type) - if("message") - output += "Admin message left by [span_prefix("[admin_key]")] on [timestamp]" - output += "
[text]
" - var/datum/DBQuery/query_message_read = SSdbcore.NewQuery( - "UPDATE [format_table_name("messages")] SET type = 'message sent' WHERE id = :id", - list("id" = message_id) - ) - if(!query_message_read.warn_execute()) - qdel(query_get_message_output) - qdel(query_message_read) - return - qdel(query_message_read) - if("watchlist entry") - message_admins("Notice: [key_name_admin(target_ckey)] has been on the watchlist since [timestamp] and has just connected - Reason: [text]") - send2irc_adminless_only("Watchlist", "[key_name(target_ckey)] is on the watchlist and has just connected - Reason: [text]") - if("memo") - output += "Memo by [span_prefix("[admin_key]")] on [timestamp]" - if(editor_key) - output += "
Last edit by [editor_key] (Click here to see edit log)" - output += "
[text]

" - qdel(query_get_message_output) - return output - -#define NOTESFILE "data/player_notes.sav" -//if the AUTOCONVERT_NOTES is turned on, anytime a player connects this will be run to try and add all their notes to the databas -/proc/convert_notes_sql(ckey) - if(!fexists(NOTESFILE)) - return - - var/savefile/notesfile = new(NOTESFILE) - if(!notesfile) - log_game("Error: Cannot access [NOTESFILE]") - return - notesfile.cd = "/[ckey]" - while(!notesfile.eof) - var/notetext - notesfile >> notetext - var/server - var/ssqlname = CONFIG_GET(string/serversqlname) - if (ssqlname) - server = ssqlname - var/regex/note = new("^(\\d{2}-\\w{3}-\\d{4}) \\| (.+) ~(\\w+)$", "i") - note.Find(notetext) - var/timestamp = note.group[1] - notetext = note.group[2] - var/admin_ckey = note.group[3] - var/datum/DBQuery/query_convert_time = SSdbcore.NewQuery("SELECT ADDTIME(STR_TO_DATE(:timestamp,'%d-%b-%Y'), '0')", list("timestamp" = timestamp)) - if(!query_convert_time.Execute()) - qdel(query_convert_time) - return - if(query_convert_time.NextRow()) - timestamp = query_convert_time.item[1] - qdel(query_convert_time) - if(ckey && notetext && timestamp && admin_ckey && server) - create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0, null, 0, 0) - notesfile.cd = "/" - notesfile.dir.Remove(ckey) - -/*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off -this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live -/proc/mass_convert_notes() - to_chat(world, "Beginning mass note conversion") - var/savefile/notesfile = new(NOTESFILE) - if(!notesfile) - log_game("Error: Cannot access [NOTESFILE]") - return - notesfile.cd = "/" - for(var/ckey in notesfile.dir) - convert_notes_sql(ckey) - to_chat(world, "Deleting NOTESFILE") - fdel(NOTESFILE) - to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES")*/ -#undef NOTESFILE diff --git a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm deleted file mode 100644 index d910a564a5e1..000000000000 --- a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm +++ /dev/null @@ -1,63 +0,0 @@ -#define DEBRIS_DENSITY (length(core.contents) / length(overmind.blobs_legit)) // items per blob - -// Accumulates junk liberally -/datum/blobstrain/debris_devourer - name = "Debris Devourer" - description = "will launch accumulated debris into targets." - analyzerdescdamage = "Does medium brute damage and may grab onto melee weapons." - analyzerdesceffect = "Devours loose items left on the station, and releases them when attacking or attacked." - color = "#8B1000" - complementary_color = "#00558B" - blobbernaut_message = "blasts" - message = "The blob blasts you" - - -/datum/blobstrain/debris_devourer/attack_living(mob/living/L, list/nearby_blobs) - send_message(L) - for (var/obj/structure/blob/blob in nearby_blobs) - debris_attack(L, blob) - -/datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore) - for(var/i in 1 to 3) - var/obj/item/I = locate() in overmind.blob_core - if (I && !QDELETED(I)) - I.forceMove(get_turf(spore)) - I.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 3, 5) - -/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this - for (var/obj/item/I in T) - I.forceMove(overmind.blob_core) - -/datum/blobstrain/debris_devourer/proc/debris_attack(mob/living/L, source) - var/obj/structure/blob/core/core = overmind.blob_core - if (prob(20 * DEBRIS_DENSITY)) // Pretend the items are spread through the blob and its mobs and not in the core. - var/obj/item/I = locate() in core - if (I && !QDELETED(I)) - I.forceMove(get_turf(source)) - I.throw_at(L, 2, 5) - -/datum/blobstrain/debris_devourer/blobbernaut_attack(mob/living/L, mob/living/blobbernaut) // When this blob's blobbernaut attacks people - debris_attack(L,blobbernaut) - -/datum/blobstrain/debris_devourer/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this - var/obj/structure/blob/core/core = overmind.blob_core - return round(max((coefficient*damage)-min(coefficient*DEBRIS_DENSITY, 10), 0)) // reduce damage taken by items per blob, up to 10 - -/datum/blobstrain/debris_devourer/examine(mob/user) - . = ..() - var/obj/structure/blob/core/core = overmind.blob_core - if (isobserver(user)) - . += span_notice("Absorbed debris is currently reducing incoming damage by [round(max(min(DEBRIS_DENSITY, 10),0))]") - else - switch (round(max(min(DEBRIS_DENSITY, 10),0))) - if (0) - . += span_notice("There is not currently enough absorbed debris to reduce damage.") - if (1 to 3) - . += span_notice("Absorbed debris is currently reducing incoming damage by a very low amount.") // these roughly correspond with force description strings - if (4 to 7) - . += span_notice("Absorbed debris is currently reducing incoming damage by a low amount.") - if (8 to 10) - . += span_notice("Absorbed debris is currently reducing incoming damage by a medium amount.") - - -#undef DEBRIS_DENSITY diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm deleted file mode 100644 index 5b186f6696c5..000000000000 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ /dev/null @@ -1,187 +0,0 @@ -//////////////////YOGS - !!!MOVED TO yogstation/code/modules/antagonists/slaughter/slaughter.dm!!! - -/mob/living/simple_animal/slaughter - name = "slaughter demon" - real_name = "slaughter demon" - desc = "A large, menacing creature covered in armored black scales." - speak_emote = list("gurgles") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" - attack_vis_effect = ATTACK_EFFECT_CLAW - icon = 'icons/mob/mob.dmi' - icon_state = "daemon" - icon_living = "daemon" - mob_biotypes = MOB_ORGANIC|MOB_HUMANOID - speed = 1 - a_intent = INTENT_HARM - stop_automated_movement = 1 - status_flags = CANPUSH - attack_sound = 'sound/magic/demon_attack1.ogg' - var/feast_sound = 'sound/magic/demon_consume.ogg' - deathsound = 'sound/magic/demon_dies.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = INFINITY - faction = list("slaughter") - attacktext = "wildly tears into" - maxHealth = 200 - health = 200 - healable = 0 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES - obj_damage = 50 - melee_damage_lower = 30 - melee_damage_upper = 30 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - bloodcrawl = BLOODCRAWL_EAT - var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill. \ - You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and disappearing from the station at will. \ - Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \ - You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. " - - loot = list(/obj/effect/decal/cleanable/blood, \ - /obj/effect/decal/cleanable/blood/innards, \ - /obj/item/organ/heart/demon) - del_on_death = 1 - deathmessage = "screams in anger as it collapses into a puddle of viscera!" - -/mob/living/simple_animal/slaughter/Initialize(mapload) - ..() - var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new - AddSpell(bloodspell) - if(istype(loc, /obj/effect/dummy/phased_mob)) - bloodspell.phased = TRUE - -/obj/effect/decal/cleanable/blood/innards - name = "pile of viscera" - desc = "A repulsive pile of guts and gore." - gender = NEUTER - icon = 'icons/obj/surgery.dmi' - icon_state = "innards" - random_icon_states = null - -/mob/living/simple_animal/slaughter/phasein() - . = ..() - add_movespeed_modifier(MOVESPEED_ID_SLAUGHTER, update=TRUE, priority=100, multiplicative_slowdown=-1) - addtimer(CALLBACK(src, PROC_REF(remove_movespeed_modifier), MOVESPEED_ID_SLAUGHTER, TRUE), 6 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) - - -//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl -/obj/item/organ/heart/demon - name = "demon heart" - desc = "Still it beats furiously, emanating an aura of utter hate." - icon = 'icons/obj/surgery.dmi' - icon_state = "demon_heart-on" - -/obj/item/organ/heart/demon/Initialize(mapload) - AddElement(/datum/element/update_icon_blocker) - return ..() - -/obj/item/organ/heart/demon/attack(mob/M, mob/living/carbon/user, obj/target) - if(M != user) - return ..() - user.visible_message(span_warning("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!"), \ - span_danger("An unnatural hunger consumes you. You raise [src] your mouth and devour it!")) - playsound(user, 'sound/magic/demon_consume.ogg', 50, 1) - for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) - if(knownspell.type == /obj/effect/proc_holder/spell/bloodcrawl) - to_chat(user, span_warning("...and you don't feel any different.")) - qdel(src) - return - user.visible_message(span_warning("[user]'s eyes flare a deep crimson!"), \ - span_userdanger("You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!")) - user.temporarilyRemoveItemFromInventory(src, TRUE) - src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E - -/obj/item/organ/heart/demon/Insert(mob/living/carbon/M, special = 0) - ..() - if(M.mind) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null)) - -/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0) - ..() - if(M.mind) - M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl) - -/obj/item/organ/heart/demon/Stop() - return 0 // Always beating. - -/mob/living/simple_animal/slaughter/laughter - // The laughter demon! It's everyone's best friend! It just wants to hug - // them so much, it wants to hug everyone at once! - name = "laughter demon" - real_name = "laughter demon" - desc = "A large, adorable creature covered in armor with pink bows." - speak_emote = list("giggles","titters","chuckles") - emote_hear = list("guffaws","laughs") - response_help = "hugs" - attacktext = "wildly tickles" - attack_vis_effect = null - - attack_sound = 'sound/items/bikehorn.ogg' - feast_sound = 'sound/spookoween/scary_horn2.ogg' - deathsound = 'sound/misc/sadtrombone.ogg' - - icon_state = "bowmon" - icon_living = "bowmon" - deathmessage = "fades out, as all of its friends are released from its \ - prison of hugs." - loot = list(/mob/living/simple_animal/pet/cat/kitten{name = "Laughter"}) - - // Keep the people we hug! - var/list/consumed_mobs = list() - - playstyle_string = "You are a laughter \ - demon, a wonderful creature from another realm. You have a single \ - desire: [span_clown("To hug and tickle.")]
\ - You may use the \"Blood Crawl\" ability near blood pools to travel \ - through them, appearing and disappearing from the station at will. \ - Pulling a dead or unconscious mob while you enter a pool will pull \ - them in with you, allowing you to hug them and regain your health.
\ - You move quickly upon leaving a pool of blood, but the material world \ - will soon sap your strength and leave you sluggish.
\ - What makes you a little sad is that people seem to die when you tickle \ - them; but don't worry! When you die, everyone you hugged will be \ - released and fully healed, because in the end it's just a jape, \ - sibling!
" - -/mob/living/simple_animal/slaughter/laughter/Destroy() - release_friends() - . = ..() - -/mob/living/simple_animal/slaughter/laughter/ex_act(severity) - switch(severity) - if(1) - death() - if(2) - adjustBruteLoss(60) - if(3) - adjustBruteLoss(30) - -/mob/living/simple_animal/slaughter/laughter/proc/release_friends() - if(!consumed_mobs) - return - - for(var/mob/living/M in consumed_mobs) - if(!M) - continue - var/turf/T = find_safe_turf() - if(!T) - T = get_turf(src) - M.forceMove(T) - if(M.revive(full_heal = TRUE, admin_revive = TRUE)) - M.grab_ghost(force = TRUE) - playsound(T, feast_sound, 50, 1, -1) - to_chat(M, span_clown("You leave [src]'s warm embrace, and feel ready to take on the world.")) - -/mob/living/simple_animal/slaughter/laughter/bloodcrawl_swallow(mob/living/victim) - if(consumed_mobs) - // Keep their corpse so rescue is possible - consumed_mobs += victim - else - // Be safe and just eject the corpse - victim.forceMove(get_turf(victim)) - victim.exit_blood_effect() - victim.visible_message("[victim] falls out of the air, covered in blood, looking highly confused. And dead.") diff --git a/code/modules/events/wizard/madness.dm b/code/modules/events/wizard/madness.dm deleted file mode 100644 index 6c52dabe2023..000000000000 --- a/code/modules/events/wizard/madness.dm +++ /dev/null @@ -1,28 +0,0 @@ -/datum/round_event_control/wizard/madness - name = "Curse of Madness" - weight = 1 - typepath = /datum/round_event/wizard/madness - earliest_start = 0 MINUTES - - var/forced_secret - -/datum/round_event_control/wizard/madness/admin_setup() - if(!check_rights(R_FUN)) - return - - var/suggested = pick(strings(REDPILL_FILE, "redpill_questions")) - - forced_secret = (input(usr, "What horrifying truth will you reveal?", "Curse of Madness", suggested) as text) || suggested - -/datum/round_event/wizard/madness/start() - var/datum/round_event_control/wizard/madness/C = control - - var/horrifying_truth - - if(C.forced_secret) - horrifying_truth = C.forced_secret - C.forced_secret = null - else - horrifying_truth = pick(strings(REDPILL_FILE, "redpill_questions")) - - curse_of_madness(null, horrifying_truth) diff --git a/code/modules/holiday/halloween.dm b/code/modules/holiday/halloween.dm deleted file mode 100644 index b679d039de2c..000000000000 --- a/code/modules/holiday/halloween.dm +++ /dev/null @@ -1,241 +0,0 @@ -////////////////////////////// -//Spookoween trapped closets// -////////////////////////////// - -#define SPOOKY_SKELETON 1 -#define ANGRY_FAITHLESS 2 -#define SCARY_BATS 3 -#define INSANE_CLOWN 4 -#define HOWLING_GHOST 5 - -//Spookoween variables -/obj/structure/closet - var/trapped = 0 - var/mob/trapped_mob - -/obj/structure/closet/Initialize(mapload) - ..() - if(prob(30)) - set_spooky_trap() - -/obj/structure/closet/dump_contents() - ..() - trigger_spooky_trap() - -/obj/structure/closet/proc/set_spooky_trap() - if(prob(0.1)) - trapped = INSANE_CLOWN - return - if(prob(1)) - trapped = ANGRY_FAITHLESS - return - if(prob(15)) - trapped = SCARY_BATS - return - if(prob(20)) - trapped = HOWLING_GHOST - return - else - var/mob/living/carbon/human/H = new(loc) - H.makeSkeleton() - H.health = 1e5 - insert(H) - trapped_mob = H - trapped = SPOOKY_SKELETON - return - -/obj/structure/closet/proc/trigger_spooky_trap() - if(!trapped) - return - - else if(trapped == SPOOKY_SKELETON) - visible_message(span_userdanger("BOO!")) - playsound(loc, 'sound/spookoween/girlscream.ogg', 300, 1) - trapped = 0 - QDEL_IN(trapped_mob, 90) - - else if(trapped == HOWLING_GHOST) - visible_message(span_userdanger("[pick("OooOOooooOOOoOoOOooooOOOOO", "BooOOooOooooOOOO", "BOO!", "WoOOoOoooOooo")]")) - playsound(loc, 'sound/spookoween/ghosty_wind.ogg', 300, 1) - new /mob/living/simple_animal/shade/howling_ghost(loc) - trapped = 0 - - else if(trapped == SCARY_BATS) - visible_message(span_userdanger("Protect your hair!")) - playsound(loc, 'sound/spookoween/bats.ogg', 300, 1) - var/number = rand(1,3) - for(var/i=0,i < number,i++) - new /mob/living/simple_animal/hostile/retaliate/bat(loc) - trapped = 0 - - else if(trapped == ANGRY_FAITHLESS) - visible_message(span_userdanger("The closet bursts open!")) - visible_message(span_userdanger("THIS BEING RADIATES PURE EVIL! YOU BETTER RUN!!!")) - playsound(loc, 'sound/hallucinations/wail.ogg', 300, 1) - var/mob/living/simple_animal/hostile/faithless/F = new(loc) - trapped = 0 - QDEL_IN(F, 120) - - else if(trapped == INSANE_CLOWN) - visible_message(span_userdanger("...")) - playsound(loc, 'sound/spookoween/scary_clown_appear.ogg', 300, 1) - spawn_atom_to_turf(/mob/living/simple_animal/hostile/retaliate/clown/insane, loc, 1, FALSE) - trapped = 0 - -//don't spawn in crates -/obj/structure/closet/crate/trigger_spooky_trap() - return - -/obj/structure/closet/crate/set_spooky_trap() - return - - -//////////////////// -//Spookoween Ghost// -//////////////////// - -/mob/living/simple_animal/shade/howling_ghost - name = "ghost" - real_name = "ghost" - icon = 'icons/mob/mob.dmi' - maxHealth = 1e6 - health = 1e6 - speak_emote = list("howls") - emote_hear = list("wails","screeches") - density = FALSE - anchored = TRUE - incorporeal_move = 1 - layer = 4 - var/timer = 0 - -/mob/living/simple_animal/shade/howling_ghost/Initialize(mapload) - . = ..() - icon_state = pick("ghost","ghostian","ghostian2","ghostking","ghost1","ghost2") - icon_living = icon_state - status_flags |= GODMODE - timer = rand(1,15) - -/mob/living/simple_animal/shade/howling_ghost/Life(seconds_per_tick = SSMOBS_DT, times_fired) - ..() - timer-- - if(prob(20)) - roam() - if(timer == 0) - spooky_ghosty() - timer = rand(1,15) - -/mob/living/simple_animal/shade/howling_ghost/proc/EtherealMove(direction) - forceMove(get_step(src, direction)) - setDir(direction) - -/mob/living/simple_animal/shade/howling_ghost/proc/roam() - if(prob(80)) - var/direction = pick(NORTH,SOUTH,EAST,WEST,NORTHEAST,NORTHWEST,SOUTHEAST,SOUTHWEST) - EtherealMove(direction) - -/mob/living/simple_animal/shade/howling_ghost/proc/spooky_ghosty() - if(prob(20)) //haunt - playsound(loc, pick('sound/spookoween/ghosty_wind.ogg','sound/spookoween/ghost_whisper.ogg','sound/spookoween/chain_rattling.ogg'), 300, 1) - if(prob(10)) //flickers - var/obj/machinery/light/L = locate(/obj/machinery/light) in view(5, src) - if(L) - L.flicker() - if(prob(5)) //poltergeist - var/obj/item/I = locate(/obj/item) in view(3, src) - if(I) - var/direction = pick(NORTH,SOUTH,EAST,WEST,NORTHEAST,NORTHWEST,SOUTHEAST,SOUTHWEST) - step(I,direction) - return - -/mob/living/simple_animal/shade/howling_ghost/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = 0 - -/////////////////////////// -//Spookoween Insane Clown// -/////////////////////////// - -/mob/living/simple_animal/hostile/retaliate/clown/insane - name = "insane clown" - desc = "Some clowns do not manage to be accepted, and go insane. This is one of them." - icon_state = "scary_clown" - icon_living = "scary_clown" - icon_dead = "scary_clown" - icon_gib = "scary_clown" - speak = list("...", ". . .") - maxHealth = 1e6 - health = 1e6 - emote_see = list("silently stares") - unsuitable_atmos_damage = 0 - var/timer - -/mob/living/simple_animal/hostile/retaliate/clown/insane/Initialize(mapload) - . = ..() - timer = rand(5,15) - status_flags = (status_flags | GODMODE) - return - -/mob/living/simple_animal/hostile/retaliate/clown/insane/Retaliate() - return - -/mob/living/simple_animal/hostile/retaliate/clown/insane/ex_act() - return - -/mob/living/simple_animal/hostile/retaliate/clown/insane/Life(seconds_per_tick = SSMOBS_DT, times_fired) - timer-- - if(target) - stalk() - return - -/mob/living/simple_animal/hostile/retaliate/clown/insane/proc/stalk() - var/mob/living/M = target - if(M.stat == DEAD) - playsound(M.loc, 'sound/spookoween/insane_low_laugh.ogg', 300, 1) - qdel(src) - if(timer == 0) - timer = rand(5,15) - playsound(M.loc, pick('sound/spookoween/scary_horn.ogg','sound/spookoween/scary_horn2.ogg', 'sound/spookoween/scary_horn3.ogg'), 300, 1) - spawn(12) - forceMove(M.loc) - -/mob/living/simple_animal/hostile/retaliate/clown/insane/MoveToTarget() - stalk(target) - -/mob/living/simple_animal/hostile/retaliate/clown/insane/AttackingTarget() - return - -/mob/living/simple_animal/hostile/retaliate/clown/insane/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = 0 - if(prob(5)) - playsound(loc, 'sound/spookoween/insane_low_laugh.ogg', 300, 1) - -/mob/living/simple_animal/hostile/retaliate/clown/insane/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/nullrod)) - if(prob(5)) - visible_message("[src] finally found the peace it deserves. You hear honks echoing off into the distance.") - playsound(loc, 'sound/spookoween/insane_low_laugh.ogg', 300, 1) - qdel(src) - else - visible_message(span_danger("[src] seems to be resisting the effect!")) - else - ..() - -/mob/living/simple_animal/hostile/retaliate/clown/insane/handle_temperature_damage() - return - -///////////////////////// -// Spooky Uplink Items // -///////////////////////// - -/datum/uplink_item/dangerous/crossbow/candy - name = "Candy Corn Crossbow" - desc = "A standard miniature energy crossbow that uses a hard-light projector to transform bolts into candy corn. Happy Halloween!" - category = "Holiday" - item = /obj/item/gun/energy/kinetic_accelerator/crossbow/halloween - surplus = 0 - -/datum/uplink_item/device_tools/emag/hack_o_lantern - name = "Hack-o'-Lantern" - desc = "An emag fitted to support the Halloween season. Candle not included." - category = "Holiday" - item = /obj/item/card/emag/halloween - surplus = 0 diff --git a/code/modules/instruments/instrument_data/synth_tones.dm b/code/modules/instruments/instrument_data/synth_tones.dm deleted file mode 100644 index 9ad9250f40d1..000000000000 --- a/code/modules/instruments/instrument_data/synth_tones.dm +++ /dev/null @@ -1,19 +0,0 @@ -/datum/instrument/tones - name = "Ideal tone" - category = "Tones" - abstract_type = /datum/instrument/tones - -/datum/instrument/tones/square_wave - name = "Ideal square wave" - id = "square" - real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg') - -/datum/instrument/tones/sine_wave - name = "Ideal sine wave" - id = "sine" - real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg') - -/datum/instrument/tones/saw_wave - name = "Ideal sawtooth wave" - id = "saw" - real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg') diff --git a/code/modules/mob/dead/emote.dm b/code/modules/mob/dead/emote.dm deleted file mode 100644 index 0f93953fdf57..000000000000 --- a/code/modules/mob/dead/emote.dm +++ /dev/null @@ -1,18 +0,0 @@ - -/* DEAD EMOTE DATUMS */ -/datum/emote/dead - mob_type_allowed_typecache = /mob/dead/observer - -/datum/emote/dead/dab - key = "dab" - key_third_person = "dabs" - message = "dabs." - message_param = "dabs on %t." - hands_use_check = TRUE - -/datum/emote/dead/dab/run_emote(mob/user, params) - . = ..() - var/mob/dead/observer/H = user - var/light_dab_angle = rand(35,55) - var/light_dab_speed = rand(3,7) - H.DabAnimation(angle = light_dab_angle , speed = light_dab_speed) diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm deleted file mode 100644 index 096ac89b188b..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ /dev/null @@ -1,672 +0,0 @@ - -GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians - -#define GUARDIAN_HANDS_LAYER 1 -#define GUARDIAN_TOTAL_LAYERS 1 -#define GUARDIAN_SCAN_DISTANCE 50 - -/mob/living/simple_animal/hostile/guardian - name = "Guardian Spirit" - real_name = "Guardian Spirit" - desc = "A mysterious being that stands by its charge, ever vigilant." - speak_emote = list("hisses") - gender = NEUTER - mob_biotypes = MOB_INORGANIC|MOB_SPIRIT - bubble_icon = "guardian" - response_help = "passes through" - response_disarm = "flails at" - response_harm = "punches" - icon = 'icons/mob/guardian.dmi' - icon_state = "magicOrange" - icon_living = "magicOrange" - icon_dead = "magicOrange" - speed = 0 - a_intent = INTENT_HARM - stop_automated_movement = 1 - movement_type = FLYING // Immunity to chasms and landmines, etc. - attack_sound = 'sound/weapons/punch1.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = INFINITY - attacktext = "punches" - maxHealth = INFINITY //The spirit itself is invincible - health = INFINITY - healable = FALSE //don't brusepack the guardian - damage_coeff = list(BRUTE = 0.5, BURN = 0.5, TOX = 0.5, CLONE = 0.5, STAMINA = 0, OXY = 0.5) //how much damage from each damage type we transfer to the owner - environment_smash = ENVIRONMENT_SMASH_STRUCTURES - obj_damage = 40 - melee_damage_lower = 15 - melee_damage_upper = 15 - butcher_results = list(/obj/item/ectoplasm = 1) - AIStatus = AI_OFF - light_system = MOVABLE_LIGHT - light_range = 3 - light_on = FALSE - hud_type = /datum/hud/guardian - dextrous_hud_type = /datum/hud/dextrous/guardian //if we're set to dextrous, account for it. - var/list/guardian_overlays[GUARDIAN_TOTAL_LAYERS] - var/reset = 0 //if the summoner has reset the guardian already - var/cooldown = 0 - var/mob/living/summoner - var/range = 10 //how far from the user the spirit can be - var/toggle_button_type = /atom/movable/screen/guardian/ToggleMode/Inactive //what sort of toggle button the hud uses - var/datum/guardianname/namedatum = new/datum/guardianname() - var/playstyle_string = "You are a standard Guardian. You shouldn't exist!" - var/magic_fluff_string = span_holoparasite("You draw the Coder, symbolizing bugs and errors. This shouldn't happen! Submit a bug report!") - var/tech_fluff_string = span_holoparasite("BOOT SEQUENCE COMPLETE. ERROR MODULE LOADED. THIS SHOULDN'T HAPPEN. Submit a bug report!") - var/carp_fluff_string = span_holoparasite("CARP CARP CARP SOME SORT OF HORRIFIC BUG BLAME THE CODERS CARP CARP CARP") - -/mob/living/simple_animal/hostile/guardian/Initialize(mapload, theme) - GLOB.parasites += src - setthemename(theme) - - . = ..() - -/mob/living/simple_animal/hostile/guardian/med_hud_set_health() - if(summoner) - var/image/holder = hud_list[HEALTH_HUD] - holder.icon_state = "hud[RoundHealth(summoner)]" - -/mob/living/simple_animal/hostile/guardian/med_hud_set_status() - if(summoner) - var/image/holder = hud_list[STATUS_HUD] - var/icon/I = icon(icon, icon_state, dir) - holder.pixel_y = I.Height() - world.icon_size - if(summoner.stat == DEAD) - holder.icon_state = "huddead" - else - holder.icon_state = "hudhealthy" - -/mob/living/simple_animal/hostile/guardian/Destroy() - GLOB.parasites -= src - return ..() - -/mob/living/simple_animal/hostile/guardian/proc/setthemename(pickedtheme) //set the guardian's theme to something cool! - if(!pickedtheme) - pickedtheme = pick("magic", "tech", "carp") - var/list/possible_names = list() - switch(pickedtheme) - if("magic") - for(var/type in (subtypesof(/datum/guardianname/magic) - namedatum.type)) - possible_names += new type - if("tech") - for(var/type in (subtypesof(/datum/guardianname/tech) - namedatum.type)) - possible_names += new type - if("carp") - for(var/type in (subtypesof(/datum/guardianname/carp) - namedatum.type)) - possible_names += new type - namedatum = pick(possible_names) - updatetheme(pickedtheme) - -/mob/living/simple_animal/hostile/guardian/proc/updatetheme(theme) //update the guardian's theme to whatever its datum is; proc for adminfuckery - name = "[namedatum.prefixname] [namedatum.suffixcolor]" - real_name = "[name]" - icon_living = "[namedatum.parasiteicon]" - icon_state = "[namedatum.parasiteicon]" - icon_dead = "[namedatum.parasiteicon]" - bubble_icon = "[namedatum.bubbleicon]" - - if (namedatum.stainself) - add_atom_colour(namedatum.color, FIXED_COLOUR_PRIORITY) - - //Special case holocarp, because #snowflake code - if(theme == "carp") - speak_emote = list("gnashes") - desc = "A mysterious fish that stands by its charge, ever vigilant." - - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - - -/mob/living/simple_animal/hostile/guardian/Login() //if we have a mind, set its name to ours when it logs in - ..() - if(mind) - mind.name = "[real_name]" - if(!summoner) - to_chat(src, "For some reason, somehow, you have no summoner. Please report this bug immediately.") - return - to_chat(src, span_holoparasite("You are [real_name], bound to serve [summoner.real_name].")) - to_chat(src, span_holoparasite("You are capable of manifesting or recalling to your master with the buttons on your HUD. You will also find a button to communicate with [summoner.p_them()] privately there.")) - to_chat(src, span_holoparasite("While personally invincible, you will die if [summoner.real_name] does, and any damage dealt to you will have a portion passed on to [summoner.p_them()] as you feed upon [summoner.p_them()] to sustain yourself.")) - to_chat(src, playstyle_string) - -//mob/living/simple_animal/hostile/guardian/Life is in the yogstation folder - -/mob/living/simple_animal/hostile/guardian/get_status_tab_items() - . += ..() - if(summoner) - var/resulthealth - if(iscarbon(summoner)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100) - else - resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5) - . += "Summoner Health: [resulthealth]%" - if(cooldown >= world.time) - . += "Manifest/Recall Cooldown Remaining: [DisplayTimeText(cooldown - world.time)]" - -/mob/living/simple_animal/hostile/guardian/Move() //Returns to summoner if they move out of range - . = ..() - snapback() - -/mob/living/simple_animal/hostile/guardian/proc/snapback() - if(summoner) - if(get_dist(get_turf(summoner),get_turf(src)) <= range) - return - else - to_chat(src, span_holoparasite("You moved out of range, and were pulled back! You can only move [range] meters from [summoner.real_name]!")) - visible_message(span_danger("\The [src] jumps back to its user.")) - if(istype(summoner.loc, /obj/effect)) - Recall(TRUE) - else - new /obj/effect/temp_visual/guardian/phase/out(loc) - forceMove(summoner.loc) - new /obj/effect/temp_visual/guardian/phase(loc) - -/mob/living/simple_animal/hostile/guardian/canSuicide() - return FALSE - -/mob/living/simple_animal/hostile/guardian/proc/is_deployed() - return loc != summoner - -/mob/living/simple_animal/hostile/guardian/AttackingTarget() - if(!is_deployed()) - to_chat(src, "You must be manifested to attack!") - return FALSE - else - return ..() - -/mob/living/simple_animal/hostile/guardian/death() - drop_all_held_items() - ..() - if(summoner) - to_chat(summoner, "Your [name] died somehow!") - summoner.death() - -/mob/living/simple_animal/hostile/guardian/update_health_hud() - if(summoner && hud_used && hud_used.healths) - var/resulthealth - if(iscarbon(summoner)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100) - else - resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5) - hud_used.healths.maptext = "
[resulthealth]%
" - -/mob/living/simple_animal/hostile/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE) //The spirit is invincible, but passes on damage to the summoner - . = amount - if(summoner) - if(loc == summoner) - return FALSE - summoner.adjustBruteLoss(amount) - if(amount > 0) - to_chat(summoner, "Your [name] is under attack! You take damage!") - summoner.visible_message(span_danger("Blood sprays from [summoner] as [src] takes damage!")) - if(summoner.stat == UNCONSCIOUS) - to_chat(summoner, "Your body can't take the strain of sustaining [src] in this condition, it begins to fall apart!") - summoner.adjustCloneLoss(amount * 0.5) //dying hosts take 50% bonus damage as cloneloss - update_health_hud() - -/mob/living/simple_animal/hostile/guardian/ex_act(severity, target) - switch(severity) - if(1) - gib() - return - if(2) - adjustBruteLoss(60) - if(3) - adjustBruteLoss(30) - -/mob/living/simple_animal/hostile/guardian/gib() - if(summoner) - to_chat(summoner, "Your [src] was blown up!") - summoner.gib() - ghostize() - qdel(src) - -//HAND HANDLING - -/mob/living/simple_animal/hostile/guardian/equip_to_slot(obj/item/I, slot) - if(!slot) - return FALSE - if(!istype(I)) - return FALSE - - . = TRUE - var/index = get_held_index_of_item(I) - if(index) - held_items[index] = null - update_inv_hands() - - if(I.pulledby) - I.pulledby.stop_pulling() - - I.screen_loc = null // will get moved if inventory is visible - I.forceMove(src) - I.equipped(src, slot) - I.layer = ABOVE_HUD_LAYER - I.plane = ABOVE_HUD_PLANE - -/mob/living/simple_animal/hostile/guardian/proc/apply_overlay(cache_index) - if((. = guardian_overlays[cache_index])) - add_overlay(.) - -/mob/living/simple_animal/hostile/guardian/proc/remove_overlay(cache_index) - var/I = guardian_overlays[cache_index] - if(I) - cut_overlay(I) - guardian_overlays[cache_index] = null - -/mob/living/simple_animal/hostile/guardian/update_inv_hands() - remove_overlay(GUARDIAN_HANDS_LAYER) - var/list/hands_overlays = list() - var/obj/item/l_hand = get_item_for_held_index(1) - var/obj/item/r_hand = get_item_for_held_index(2) - - if(r_hand) - var/r_state = r_hand.item_state - if(!r_state) - r_state = r_hand.icon_state - - hands_overlays += r_hand.build_worn_icon(state = r_state, default_layer = GUARDIAN_HANDS_LAYER, default_icon_file = r_hand.righthand_file, isinhands = TRUE) - - if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) - r_hand.layer = ABOVE_HUD_LAYER - r_hand.plane = ABOVE_HUD_PLANE - r_hand.screen_loc = ui_hand_position(get_held_index_of_item(r_hand)) - client.screen |= r_hand - - if(l_hand) - var/l_state = l_hand.item_state - if(!l_state) - l_state = l_hand.icon_state - - hands_overlays += l_hand.build_worn_icon(state = l_state, default_layer = GUARDIAN_HANDS_LAYER, default_icon_file = l_hand.lefthand_file, isinhands = TRUE) - - if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) - l_hand.layer = ABOVE_HUD_LAYER - l_hand.plane = ABOVE_HUD_PLANE - l_hand.screen_loc = ui_hand_position(get_held_index_of_item(l_hand)) - client.screen |= l_hand - - if(hands_overlays.len) - guardian_overlays[GUARDIAN_HANDS_LAYER] = hands_overlays - apply_overlay(GUARDIAN_HANDS_LAYER) - -/mob/living/simple_animal/hostile/guardian/regenerate_icons() - update_inv_hands() - -//MANIFEST, RECALL, TOGGLE MODE/LIGHT, SHOW TYPE - -/mob/living/simple_animal/hostile/guardian/proc/Manifest(forced) - if(istype(summoner.loc, /obj/effect) || (cooldown > world.time && !forced)) - return FALSE - if(loc == summoner) - forceMove(summoner.loc) - new /obj/effect/temp_visual/guardian/phase(loc) - cooldown = world.time + 10 - reset_perspective() - return TRUE - return FALSE - -/mob/living/simple_animal/hostile/guardian/proc/Recall(forced) - if(!summoner || loc == summoner || (cooldown > world.time && !forced)) - return FALSE - new /obj/effect/temp_visual/guardian/phase/out(loc) - - forceMove(summoner) - cooldown = world.time + 10 - return TRUE - -/mob/living/simple_animal/hostile/guardian/proc/ToggleMode() - to_chat(src, "You don't have another mode!") - -/mob/living/simple_animal/hostile/guardian/proc/ToggleLight() - if(!light_on) - to_chat(src, span_notice("You activate your light.")) - set_light_on(TRUE) - else - to_chat(src, span_notice("You deactivate your light.")) - set_light_on(FALSE) - -/mob/living/simple_animal/hostile/guardian/verb/ShowType() - set name = "Check Guardian Type" - set category = "Guardian" - set desc = "Check what type you are." - to_chat(src, playstyle_string) - -//COMMUNICATION - -/mob/living/simple_animal/hostile/guardian/proc/Communicate() - if(summoner) - var/input = stripped_input(src, "Please enter a message to tell your summoner.", "Guardian", "") - if(!input) - return - - var/preliminary_message = "[input]" //apply basic color/bolding - var/my_message = "[src]: [preliminary_message]" //add source, color source with the guardian's color - - to_chat(summoner, my_message) - var/list/guardians = summoner.hasparasites() - for(var/para in guardians) - to_chat(para, my_message) - for(var/M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, src) - to_chat(M, "[link] [my_message]") - - src.log_talk(input, LOG_SAY, tag="guardian") - -/mob/living/proc/guardian_comm() - set name = "Communicate" - set category = "Guardian" - set desc = "Communicate telepathically with your guardian." - var/input = stripped_input(src, "Please enter a message to tell your guardian.", "Message", "") - if(!input) - return - - var/preliminary_message = "[input]" //apply basic color/bolding - var/my_message = "[src]: [preliminary_message]" //add source, color source with default grey... - - to_chat(src, my_message) - var/list/guardians = hasparasites() - for(var/para in guardians) - var/mob/living/simple_animal/hostile/guardian/G = para - to_chat(G, "[src]: [preliminary_message]" ) - for(var/M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, src) - to_chat(M, "[link] [my_message]") - - src.log_talk(input, LOG_SAY, tag="guardian") - -//FORCE RECALL/RESET -/mob/living/proc/guardian_recall() - set name = "Recall Guardian" - set category = "Guardian" - set desc = "Forcibly recall your guardian." - var/list/guardians = hasparasites() - for(var/para in guardians) - var/mob/living/simple_animal/hostile/guardian/G = para - G.Recall() - -/mob/living/proc/guardian_reset() - set name = "Reset Guardian Player (One Use)" - set category = "Guardian" - set desc = "Re-rolls which ghost will control your Guardian. One use per Guardian." - - var/list/guardians = hasparasites() - for(var/para in guardians) - var/mob/living/simple_animal/hostile/guardian/P = para - if(P.reset) - guardians -= P //clear out guardians that are already reset - if(guardians.len) - var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in guardians - if(G) - to_chat(src, span_holoparasite("You attempt to reset [G.real_name]'s personality...")) - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_PAI, null, FALSE, 100) - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) - to_chat(G, span_holoparasite("Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.")) - to_chat(src, "Your [G.real_name] has been successfully reset.") - message_admins("[key_name_admin(C)] has taken control of ([ADMIN_LOOKUPFLW(G)])") - G.ghostize(0) - G.setthemename(G.namedatum.theme) //give it a new color, to show it's a new person - G.key = C.key - G.reset = 1 - switch(G.namedatum.theme) - if("tech") - to_chat(src, span_holoparasite("[G.real_name] is now online!")) - if("magic") - to_chat(src, span_holoparasite("[G.real_name] has been summoned!")) - guardians -= G - if(!guardians.len) - remove_verb(src, /mob/living/proc/guardian_reset) - else - to_chat(src, span_holoparasite("There were no ghosts willing to take control of [G.real_name]. Looks like you're stuck with it for now.")) - else - to_chat(src, span_holoparasite("You decide not to reset [guardians.len > 1 ? "any of your guardians":"your guardian"].")) - else - remove_verb(src, /mob/living/proc/guardian_reset) - -////////parasite tracking/finding procs - -/mob/living/proc/hasparasites() //returns a list of guardians the mob is a summoner for - . = list() - for(var/P in GLOB.parasites) - var/mob/living/simple_animal/hostile/guardian/G = P - if(G.summoner == src) - . += G - -/mob/living/simple_animal/hostile/guardian/proc/hasmatchingsummoner(mob/living/simple_animal/hostile/guardian/G) //returns 1 if the summoner matches the target's summoner - return (istype(G) && G.summoner == summoner) - - -////////Creation - -/obj/item/guardiancreator - name = "deck of tarot cards" - desc = "An enchanted deck of tarot cards, rumored to be a source of unimaginable power." - icon = 'icons/obj/toy.dmi' - icon_state = "deck_syndicate_full" - var/used = FALSE - var/theme = "magic" - var/mob_name = "Guardian Spirit" - var/use_message = span_holoparasite("You shuffle the deck...") - var/used_message = span_holoparasite("All the cards seem to be blank now.") - var/failure_message = "..And draw a card! It's...blank? Maybe you should try again later." - var/ling_failure = "The deck refuses to respond to a souless creature such as you." - var/list/possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support", "Gravitokinetic") - var/random = TRUE - var/allowmultiple = FALSE - var/allowling = TRUE - var/allowguardian = FALSE - -/obj/item/guardiancreator/attack_self(mob/living/user) - if(isguardian(user) && !allowguardian) - to_chat(user, span_holoparasite("[mob_name] chains are not allowed.")) - return - var/list/guardians = user.hasparasites() - if(guardians.len && !allowmultiple) - to_chat(user, span_holoparasite("You already have a [mob_name]!")) - return - if(user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling) && !allowling) - to_chat(user, "[ling_failure]") - return - if(used == TRUE) - to_chat(user, "[used_message]") - return - used = TRUE - to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE) - - if(LAZYLEN(candidates)) - var/mob/dead/observer/C = pick(candidates) - spawn_guardian(user, C.key) - else - to_chat(user, "[failure_message]") - used = FALSE - - -/obj/item/guardiancreator/proc/spawn_guardian(mob/living/user, key) - var/guardiantype = "Standard" - if(random) - guardiantype = pick(possible_guardians) - else - guardiantype = input(user, "Pick the type of [mob_name]", "[mob_name] Creation") as null|anything in possible_guardians - if(!guardiantype) - to_chat(user, "[failure_message]" ) - used = FALSE - return - var/pickedtype = /mob/living/simple_animal/hostile/guardian/punch - switch(guardiantype) - - if("Chaos") - pickedtype = /mob/living/simple_animal/hostile/guardian/fire - - if("Standard") - pickedtype = /mob/living/simple_animal/hostile/guardian/punch - - if("Ranged") - pickedtype = /mob/living/simple_animal/hostile/guardian/ranged - - if("Support") - pickedtype = /mob/living/simple_animal/hostile/guardian/healer - - if("Explosive") - pickedtype = /mob/living/simple_animal/hostile/guardian/bomb - - if("Lightning") - pickedtype = /mob/living/simple_animal/hostile/guardian/beam - - if("Protector") - pickedtype = /mob/living/simple_animal/hostile/guardian/protector - - if("Charger") - pickedtype = /mob/living/simple_animal/hostile/guardian/charger - - if("Assassin") - pickedtype = /mob/living/simple_animal/hostile/guardian/assassin - - if("Dextrous") - pickedtype = /mob/living/simple_animal/hostile/guardian/dextrous - - if("Gravitokinetic") - pickedtype = /mob/living/simple_animal/hostile/guardian/gravitokinetic - - var/list/guardians = user.hasparasites() - if(guardians.len && !allowmultiple) - to_chat(user, span_holoparasite("You already have a [mob_name]!") ) - used = FALSE - return - var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user, theme) - G.summoner = user - G.key = key - G.mind.enslave_mind_to_creator(user) - log_game("[key_name(user)] has summoned [key_name(G)], a [guardiantype] holoparasite.") - switch(theme) - if("tech") - to_chat(user, "[G.tech_fluff_string]") - to_chat(user, span_holoparasite("[G.real_name] is now online!")) - if("magic") - to_chat(user, "[G.magic_fluff_string]") - to_chat(user, span_holoparasite("[G.real_name] has been summoned!")) - if("carp") - to_chat(user, "[G.carp_fluff_string]") - to_chat(user, span_holoparasite("[G.real_name] has been caught!")) - - add_verb(user, list(/mob/living/proc/guardian_comm, \ - /mob/living/proc/guardian_recall, \ - /mob/living/proc/guardian_reset, \ - /mob/living/proc/finduser)) - G?.client.init_verbs() - -/obj/item/guardiancreator/choose - random = FALSE - -/obj/item/guardiancreator/choose/dextrous - possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support", "Gravitokinetic") - -/obj/item/guardiancreator/choose/wizard - possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Gravitokinetic") - allowmultiple = TRUE - -/obj/item/guardiancreator/tech - name = "holoparasite injector" - desc = "It contains an alien nanoswarm of unknown origin. Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, it requires an organic host as a home base and source of fuel." - icon = 'icons/obj/syringe.dmi' - icon_state = "old_combat_hypo" - theme = "tech" - mob_name = "Holoparasite" - use_message = span_holoparasite("You start to power on the injector...") - used_message = span_holoparasite("The injector has already been used.") - failure_message = "...ERROR. BOOT SEQUENCE ABORTED. AI FAILED TO INTIALIZE. PLEASE CONTACT SUPPORT OR TRY AGAIN LATER." - ling_failure = "The holoparasites recoil in horror. They want nothing to do with a creature like you." - -/obj/item/guardiancreator/tech/choose/traitor - possible_guardians = list("Assassin", "Chaos", "Charger", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support", "Gravitokinetic") - -/obj/item/guardiancreator/tech/choose - random = FALSE - -/obj/item/guardiancreator/tech/choose/dextrous - possible_guardians = list("Assassin", "Chaos", "Charger", "Dextrous", "Explosive", "Lightning", "Protector", "Ranged", "Standard", "Support", "Gravitokinetic") - -/obj/item/paper/guides/antag/guardian - name = "Holoparasite Guide" - icon_state = "paper_words" - info = {"A list of Holoparasite Types
- -
- Assassin: Does medium damage and takes full damage, but can enter stealth, causing its next attack to do massive damage and ignore armor. However, it becomes briefly unable to recall after attacking from stealth.
-
- Chaos: Ignites enemies on touch and causes them to hallucinate all nearby people as the parasite. Automatically extinguishes the user if they catch on fire.
-
- Charger: Moves extremely fast, does medium damage on attack, and can charge at targets, damaging the first target hit and forcing them to drop any items they are holding.
-
- Explosive: High damage resist and medium power attack that may explosively teleport targets. Can turn any object, including objects too large to pick up, into a bomb, dealing explosive damage to the next person to touch it. The object will return to normal after the trap is triggered or after a delay.
-
- Lightning: Attacks apply lightning chains to targets. Has a lightning chain to the user. Lightning chains shock everything near them, doing constant damage.
-
- Protector: Causes you to teleport to it when out of range, unlike other parasites. Has two modes; Combat, where it does and takes medium damage, and Protection, where it does and takes almost no damage but moves slightly slower.
-
- Ranged: Has two modes. Ranged; which fires a constant stream of weak, armor-ignoring projectiles. Scout; Cannot attack, but can move through walls and is quite hard to see. Can lay surveillance snares, which alert it when crossed, in either mode.
-
- Standard: Devastating close combat attacks and high damage resist. Can smash through weak walls.
-
- Gravitokinetic: Attacks will apply crushing gravity to the target. Can target the ground as well to slow targets advancing on you, but this will affect the user.
-
-"} - -/obj/item/paper/guides/antag/guardian/Initialize(mapload) - AddElement(/datum/element/update_icon_blocker) - return ..() - -/obj/item/paper/guides/antag/guardian/wizard - name = "Guardian Guide" - info = {"A list of Guardian Types
- -
- Assassin: Does medium damage and takes full damage, but can enter stealth, causing its next attack to do massive damage and ignore armor. However, it becomes briefly unable to recall after attacking from stealth.
-
- Chaos: Ignites enemies on touch and causes them to hallucinate all nearby people as the guardian. Automatically extinguishes the user if they catch on fire.
-
- Charger: Moves extremely fast, does medium damage on attack, and can charge at targets, damaging the first target hit and forcing them to drop any items they are holding.
-
- Dexterous: Does low damage on attack, but is capable of holding items and storing a single item within it. It will drop items held in its hands when it recalls, but it will retain the stored item.
-
- Explosive: High damage resist and medium power attack that may explosively teleport targets. Can turn any object, including objects too large to pick up, into a bomb, dealing explosive damage to the next person to touch it. The object will return to normal after the trap is triggered or after a delay.
-
- Lightning: Attacks apply lightning chains to targets. Has a lightning chain to the user. Lightning chains shock everything near them, doing constant damage.
-
- Protector: Causes you to teleport to it when out of range, unlike other parasites. Has two modes; Combat, where it does and takes medium damage, and Protection, where it does and takes almost no damage but moves slightly slower.
-
- Ranged: Has two modes. Ranged; which fires a constant stream of weak, armor-ignoring projectiles. Scout; Cannot attack, but can move through walls and is quite hard to see. Can lay surveillance snares, which alert it when crossed, in either mode.
-
- Standard: Devastating close combat attacks and high damage resist. Can smash through weak walls.
-
- Gravitokinetic: Attacks will apply crushing gravity to the target. Can target the ground as well to slow targets advancing on you, but this will affect the user.
-
-"} - - -/obj/item/storage/box/syndie_kit/guardian - name = "holoparasite injector kit" - -/obj/item/storage/box/syndie_kit/guardian/PopulateContents() - new /obj/item/guardiancreator/tech/choose/traitor(src) - new /obj/item/paper/guides/antag/guardian(src) - -/obj/item/guardiancreator/carp - name = "holocarp fishsticks" - desc = "Using the power of Carp'sie, you can catch a carp from byond the veil of Carpthulu, and bind it to your fleshy flesh form." - icon = 'icons/obj/food/food.dmi' - icon_state = "fishfingers" - theme = "carp" - mob_name = "Holocarp" - use_message = span_holoparasite("You put the fishsticks in your mouth...") - used_message = span_holoparasite("Someone's already taken a bite out of these fishsticks! Ew.") - failure_message = "You couldn't catch any carp spirits from the seas of Lake Carp. Maybe there are none, maybe you fucked up." - ling_failure = "Carp'sie is fine with changelings, so you shouldn't be seeing this message." - allowmultiple = TRUE - allowling = TRUE - random = TRUE - -/obj/item/guardiancreator/carp/choose - random = FALSE diff --git a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm b/code/modules/mob/living/simple_animal/guardian/types/assassin.dm deleted file mode 100644 index e221fbd02e01..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/assassin.dm +++ /dev/null @@ -1,100 +0,0 @@ -//Assassin -/mob/living/simple_animal/hostile/guardian/assassin - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) - playstyle_string = span_holoparasite("As an assassin type you do medium damage and have no damage resistance, but can enter stealth, massively increasing the damage of your next attack and causing it to ignore armor. Stealth is broken when you attack or take damage.") - magic_fluff_string = span_holoparasite("..And draw the Space Ninja, a lethal, invisible assassin.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Assassin modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one! It's an assassin carp! Just when you thought it was safe to go back to the water... which is unhelpful, because we're in space.") - attack_vis_effect = ATTACK_EFFECT_SLASH - - toggle_button_type = /atom/movable/screen/guardian/ToggleMode/Assassin - var/toggle = FALSE - var/stealthcooldown = 160 - var/atom/movable/screen/alert/canstealthalert - var/atom/movable/screen/alert/instealthalert - -/mob/living/simple_animal/hostile/guardian/assassin/Initialize(mapload) - . = ..() - stealthcooldown = 0 - -/mob/living/simple_animal/hostile/guardian/assassin/Life(seconds_per_tick = SSMOBS_DT, times_fired) - . = ..() - updatestealthalert() - if(loc == summoner && toggle) - ToggleMode(0) - -/mob/living/simple_animal/hostile/guardian/assassin/get_status_tab_items() - . = ..() - if(stealthcooldown >= world.time) - . += "Stealth Cooldown Remaining: [DisplayTimeText(stealthcooldown - world.time)]" - -/mob/living/simple_animal/hostile/guardian/assassin/AttackingTarget() - . = ..() - if(.) - if(toggle && (isliving(target) || istype(target, /obj/structure/window) || istype(target, /obj/structure/grille))) - ToggleMode(1) - -/mob/living/simple_animal/hostile/guardian/assassin/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(. > 0 && toggle) - ToggleMode(1) - -/mob/living/simple_animal/hostile/guardian/assassin/Recall() - if(..() && toggle) - ToggleMode(0) - -/mob/living/simple_animal/hostile/guardian/assassin/ToggleMode(forced = 0) - if(toggle) - melee_damage_lower = initial(melee_damage_lower) - melee_damage_upper = initial(melee_damage_upper) - armour_penetration = initial(armour_penetration) - obj_damage = initial(obj_damage) - environment_smash = initial(environment_smash) - alpha = initial(alpha) - if(!forced) - to_chat(src, "You exit stealth.") - else - visible_message(span_danger("\The [src] suddenly appears!")) - stealthcooldown = world.time + initial(stealthcooldown) //we were forced out of stealth and go on cooldown - cooldown = world.time + 40 //can't recall for 4 seconds - updatestealthalert() - toggle = FALSE - else if(stealthcooldown <= world.time) - if(src.loc == summoner) - to_chat(src, "You have to be manifested to enter stealth!") - return - melee_damage_lower = 50 - melee_damage_upper = 50 - armour_penetration = 100 - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - new /obj/effect/temp_visual/guardian/phase/out(get_turf(src)) - alpha = 15 - if(!forced) - to_chat(src, "You enter stealth, empowering your next attack.") - updatestealthalert() - toggle = TRUE - else if(!forced) - to_chat(src, "You cannot yet enter stealth, wait another [DisplayTimeText(stealthcooldown - world.time)]!") - -/mob/living/simple_animal/hostile/guardian/assassin/proc/updatestealthalert() - if(stealthcooldown <= world.time) - if(toggle) - if(!instealthalert) - instealthalert = throw_alert("instealth", /atom/movable/screen/alert/instealth) - clear_alert("canstealth") - canstealthalert = null - else - if(!canstealthalert) - canstealthalert = throw_alert("canstealth", /atom/movable/screen/alert/canstealth) - clear_alert("instealth") - instealthalert = null - else - clear_alert("instealth") - instealthalert = null - clear_alert("canstealth") - canstealthalert = null diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm deleted file mode 100644 index cd097c46fb6f..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm +++ /dev/null @@ -1,73 +0,0 @@ -//Charger -/mob/living/simple_animal/hostile/guardian/charger - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 //technically - ranged_message = "charges" - ranged_cooldown_time = 40 - speed = -1 - damage_coeff = list(BRUTE = 0.6, BURN = 0.6, TOX = 0.6, CLONE = 0.6, STAMINA = 0, OXY = 0.6) - playstyle_string = span_holoparasite("As a charger type you do medium damage, have medium damage resistance, move very fast, and can charge at a location, damaging any target hit and forcing them to drop any items they are holding.") - magic_fluff_string = span_holoparasite("..And draw the Hunter, an alien master of rapid assault.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Charge modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one! It's a charger carp, that likes running at people. But it doesn't have any legs...") - var/charging = 0 - var/atom/movable/screen/alert/chargealert - -/mob/living/simple_animal/hostile/guardian/charger/Life(seconds_per_tick = SSMOBS_DT, times_fired) - . = ..() - if(ranged_cooldown <= world.time) - if(!chargealert) - chargealert = throw_alert("charge", /atom/movable/screen/alert/cancharge) - else - clear_alert("charge") - chargealert = null - -/mob/living/simple_animal/hostile/guardian/charger/OpenFire(atom/A) - if(!charging) - visible_message(span_danger("[src] [ranged_message] at [A]!")) - ranged_cooldown = world.time + ranged_cooldown_time - clear_alert("charge") - chargealert = null - Shoot(A) - -/mob/living/simple_animal/hostile/guardian/charger/Shoot(atom/targeted_atom) - charging = 1 - throw_at(targeted_atom, range, 1, src, FALSE, TRUE, callback = CALLBACK(src, PROC_REF(charging_end))) - -/mob/living/simple_animal/hostile/guardian/charger/proc/charging_end() - charging = 0 - -/mob/living/simple_animal/hostile/guardian/charger/Move() - if(charging) - new /obj/effect/temp_visual/decoy/fading(loc,src) - . = ..() - -/mob/living/simple_animal/hostile/guardian/charger/snapback() - if(!charging) - ..() - -/mob/living/simple_animal/hostile/guardian/charger/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) - if(!charging) - return ..() - - else if(hit_atom) - if(isliving(hit_atom) && hit_atom != summoner) - var/mob/living/L = hit_atom - var/blocked = FALSE - if(hasmatchingsummoner(hit_atom)) //if the summoner matches don't hurt them - blocked = TRUE - if(ishuman(hit_atom)) - var/mob/living/carbon/human/H = hit_atom - if(H.check_shields(src, 90, "[name]", attack_type = THROWN_PROJECTILE_ATTACK)) - blocked = TRUE - if(!blocked) - L.drop_all_held_items() - L.visible_message(span_danger("[src] slams into [L]!"), span_userdanger("[src] slams into you!")) - L.apply_damage(20, BRUTE) - playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1) - shake_camera(L, 4, 3) - shake_camera(src, 2, 3) - - charging = 0 - diff --git a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm b/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm deleted file mode 100644 index d4b977fba945..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/dextrous.dm +++ /dev/null @@ -1,95 +0,0 @@ -//Dextrous -/mob/living/simple_animal/hostile/guardian/dextrous - melee_damage_lower = 10 - melee_damage_upper = 10 - damage_coeff = list(BRUTE = 0.75, BURN = 0.75, TOX = 0.75, CLONE = 0.75, STAMINA = 0, OXY = 0.75) - playstyle_string = span_holoparasite("As a dextrous type you can hold items, store an item within yourself, and have medium damage resistance, but do low damage on attacks. Recalling and leashing will force you to drop unstored items!") - magic_fluff_string = span_holoparasite("..And draw the Drone, a dextrous master of construction and repair.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Dextrous combat modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! You caught one! It can hold stuff in its fins, sort of.") - dextrous = TRUE - held_items = list(null, null) - var/obj/item/internal_storage //what we're storing within ourself - -/mob/living/simple_animal/hostile/guardian/dextrous/death(gibbed) - ..() - if(internal_storage) - dropItemToGround(internal_storage) - -/mob/living/simple_animal/hostile/guardian/dextrous/examine(mob/user) - if(dextrous) - . = list("This is [icon2html(src)] \a [src]!\n[desc]") - for(var/obj/item/I in held_items) - if(!(I.item_flags & ABSTRACT)) - . += "It has [I.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(I))]." - if(internal_storage && !(internal_storage.item_flags & ABSTRACT)) - . += "It is holding [internal_storage.get_examine_string(user)] in its internal storage." - . += "" - else - return ..() - -/mob/living/simple_animal/hostile/guardian/dextrous/Recall(forced) - if(!summoner || loc == summoner || (cooldown > world.time && !forced)) - return FALSE - drop_all_held_items() - return ..() //lose items, then return - -/mob/living/simple_animal/hostile/guardian/dextrous/snapback() - if(summoner && !(get_dist(get_turf(summoner),get_turf(src)) <= range)) - drop_all_held_items() - ..() //lose items, then return - -//SLOT HANDLING BULLSHIT FOR INTERNAL STORAGE -/mob/living/simple_animal/hostile/guardian/dextrous/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE) - if(..()) - update_inv_hands() - if(I == internal_storage) - internal_storage = null - update_inv_internal_storage() - return 1 - return 0 - -/mob/living/simple_animal/hostile/guardian/dextrous/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE) - switch(slot) - if(ITEM_SLOT_DEX_STORAGE) - if(internal_storage) - return 0 - return 1 - ..() - -/mob/living/simple_animal/hostile/guardian/dextrous/get_item_by_slot(slot_id) - if(slot_id == ITEM_SLOT_DEX_STORAGE) - return internal_storage - return ..() - -/mob/living/simple_animal/hostile/guardian/dextrous/get_slot_by_item(obj/item/looking_for) - if(internal_storage == looking_for) - return ITEM_SLOT_DEX_STORAGE - return ..() - - -/mob/living/simple_animal/hostile/guardian/dextrous/equip_to_slot(obj/item/I, slot) - if(!..()) - return - - switch(slot) - if(ITEM_SLOT_DEX_STORAGE) - internal_storage = I - update_inv_internal_storage() - else - to_chat(src, span_danger("You are trying to equip this item to an unsupported inventory slot. Report this to a coder!")) - -/mob/living/simple_animal/hostile/guardian/dextrous/getBackSlot() - return ITEM_SLOT_DEX_STORAGE - -/mob/living/simple_animal/hostile/guardian/dextrous/getBeltSlot() - return ITEM_SLOT_DEX_STORAGE - -/mob/living/simple_animal/hostile/guardian/dextrous/proc/update_inv_internal_storage() - if(internal_storage && client && hud_used && hud_used.hud_shown) - internal_storage.screen_loc = ui_id - client.screen += internal_storage - -/mob/living/simple_animal/hostile/guardian/dextrous/regenerate_icons() - ..() - update_inv_internal_storage() diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm deleted file mode 100644 index 6aa83963a038..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm +++ /dev/null @@ -1,97 +0,0 @@ -//Bomb -/mob/living/simple_animal/hostile/guardian/bomb - melee_damage_lower = 15 - melee_damage_upper = 15 - damage_coeff = list(BRUTE = 0.6, BURN = 0.6, TOX = 0.6, CLONE = 0.6, STAMINA = 0, OXY = 0.6) - range = 13 - playstyle_string = span_holoparasite("As an explosive type, you have moderate close combat abilities, may explosively teleport targets on attack, and are capable of converting nearby items and objects into disguised bombs via alt click.") - magic_fluff_string = span_holoparasite("..And draw the Scientist, master of explosive death.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Explosive modules active. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one! It's an explosive carp! Boom goes the fishy.") - var/bomb_cooldown = 0 - -/mob/living/simple_animal/hostile/guardian/bomb/get_status_tab_items() - . = ..() - if(bomb_cooldown >= world.time) - . += "Bomb Cooldown Remaining: [DisplayTimeText(bomb_cooldown - world.time)]" - -/mob/living/simple_animal/hostile/guardian/bomb/AttackingTarget() - . = ..() - if(. && prob(40) && isliving(target)) - var/mob/living/M = target - if(!M.anchored && M != summoner && !hasmatchingsummoner(M)) - new /obj/effect/temp_visual/guardian/phase/out(get_turf(M)) - do_teleport(M, M, 10, channel = TELEPORT_CHANNEL_BLUESPACE) - for(var/mob/living/L in range(1, M)) - if(hasmatchingsummoner(L)) //if the summoner matches don't hurt them - continue - if(L != src && L != summoner) - L.apply_damage(15, BRUTE) - new /obj/effect/temp_visual/explosion(get_turf(M)) - -/mob/living/simple_animal/hostile/guardian/bomb/AltClickOn(atom/movable/A) - if(!istype(A)) - return - if(loc == summoner) - to_chat(src, span_danger("You must be manifested to create bombs!")) - return - if(isobj(A) && Adjacent(A)) - if(bomb_cooldown <= world.time && !stat) - var/obj/guardian_bomb/B = new /obj/guardian_bomb(get_turf(A)) - to_chat(src, span_danger("Success! Bomb armed!")) - bomb_cooldown = world.time + 200 - B.spawner = src - B.disguise(A) - else - to_chat(src, span_danger("Your powers are on cooldown! You must wait 20 seconds between bombs.")) - -/obj/guardian_bomb - name = "bomb" - desc = "You shouldn't be seeing this!" - var/obj/stored_obj - var/mob/living/simple_animal/hostile/guardian/spawner - - -/obj/guardian_bomb/proc/disguise(obj/A) - A.forceMove(src) - stored_obj = A - set_opacity(A.opacity) - anchored = A.anchored - density = A.density - appearance = A.appearance - addtimer(CALLBACK(src, PROC_REF(disable)), 600) - -/obj/guardian_bomb/proc/disable() - stored_obj.forceMove(get_turf(src)) - to_chat(spawner, span_danger("Failure! Your trap didn't catch anyone this time.")) - qdel(src) - -/obj/guardian_bomb/proc/detonate(mob/living/user) - if(isliving(user)) - if(user != spawner && user != spawner.summoner && !spawner.hasmatchingsummoner(user)) - to_chat(user, span_danger("[src] was boobytrapped!")) - to_chat(spawner, span_danger("Success! Your trap caught [user]")) - var/turf/T = get_turf(src) - stored_obj.forceMove(T) - playsound(T,'sound/effects/explosion2.ogg', 'yogstation/sound/effects/bokudan.ogg', 200, 1) // yogs - bokudan sound - new /obj/effect/temp_visual/explosion(T) - user.ex_act(EXPLODE_HEAVY) - qdel(src) - else - to_chat(user, span_holoparasite("[src] glows with a strange light, and you don't touch it.")) - -/obj/guardian_bomb/Bump(atom/A) - detonate(A) - ..() - -/obj/guardian_bomb/attackby(mob/living/user) - detonate(user) - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/guardian_bomb/attack_hand(mob/living/user) - detonate(user) - -/obj/guardian_bomb/examine(mob/user) - . = stored_obj.examine(user) - if(get_dist(user,src)<=2) - . += span_holoparasite("It glows with a strange light!") diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm deleted file mode 100644 index f0fc74fdd6c5..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm +++ /dev/null @@ -1,43 +0,0 @@ -//Fire -/mob/living/simple_animal/hostile/guardian/fire - a_intent = INTENT_HELP - melee_damage_lower = 7 - melee_damage_upper = 7 - attack_sound = 'sound/items/welder.ogg' - attacktext = "ignites" - damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - range = 7 - playstyle_string = span_holoparasite("As a chaos type, you have only light damage resistance, but will ignite any enemy you bump into. In addition, your melee attacks will cause human targets to see everyone as you.") - magic_fluff_string = span_holoparasite("..And draw the Wizard, bringer of endless chaos!") - tech_fluff_string = span_holoparasite("Boot sequence complete. Crowd control modules activated. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! You caught one! OH GOD, EVERYTHING'S ON FIRE. Except you and the fish.") - -/mob/living/simple_animal/hostile/guardian/fire/Life(seconds_per_tick = SSMOBS_DT, times_fired) - . = ..() - if(summoner) - summoner.extinguish_mob() - summoner.fire_stacks = -1 - -/mob/living/simple_animal/hostile/guardian/fire/AttackingTarget() - . = ..() - if(. && ishuman(target) && target != summoner) - new /datum/hallucination/delusion(target,TRUE,"custom",200,0, icon_state,icon) - -/mob/living/simple_animal/hostile/guardian/fire/Crossed(AM as mob|obj) - ..() - collision_ignite(AM) - -/mob/living/simple_animal/hostile/guardian/fire/Bumped(atom/movable/AM) - ..() - collision_ignite(AM) - -/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj) - ..() - collision_ignite(AM) - -/mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(AM as mob|obj) - if(isliving(AM)) - var/mob/living/M = AM - if(!hasmatchingsummoner(M) && M != summoner && M.fire_stacks < 7) - M.fire_stacks = 7 - M.ignite_mob() diff --git a/code/modules/mob/living/simple_animal/guardian/types/gravitokinetic.dm b/code/modules/mob/living/simple_animal/guardian/types/gravitokinetic.dm deleted file mode 100644 index 7b7126cd9fa4..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/gravitokinetic.dm +++ /dev/null @@ -1,57 +0,0 @@ -//gravitokinetic -/mob/living/simple_animal/hostile/guardian/gravitokinetic - melee_damage_lower = 15 - melee_damage_upper = 15 - damage_coeff = list(BRUTE = 0.75, BURN = 0.75, TOX = 0.75, CLONE = 0.75, STAMINA = 0, OXY = 0.75) - playstyle_string = span_holoparasite("As a gravitokinetic type, you can alt click to make the gravity on the ground stronger, and punching applies this effect to a target.") - magic_fluff_string = span_holoparasite("..And draw the Singularity, an anomalous force of terror.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Gravitokinetic modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one! It's a gravitokinetic carp! Now do you understand the gravity of the situation?") - var/list/gravito_targets = list() - var/gravity_power_range = 10 //how close the stand must stay to the target to keep the heavy gravity - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/AttackingTarget() - . = ..() - if(isliving(target) && target != src) - to_chat(src, "Your punch has applied heavy gravity to [target]!") - add_gravity(target, 2) - to_chat(target, span_userdanger("Everything feels really heavy!")) - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/AltClickOn(atom/A) - if(isopenturf(A) && is_deployed() && stat != DEAD && in_range(src, A) && !incapacitated()) - var/turf/T = A - if(isspaceturf(T)) - to_chat(src, span_warning("You cannot add gravity to space!")) - return - visible_message(span_danger("[src] slams their fist into the [T]!"), span_notice("You modify the gravity of the [T].")) - do_attack_animation(T) - add_gravity(T, 4) - return - return ..() - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/Recall(forced) - . = ..() - to_chat(src, "You have released your gravitokinetic powers!") - for(var/datum/component/C in gravito_targets) - remove_gravity(C) - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/Moved(oldLoc, dir) - . = ..() - for(var/datum/component/C in gravito_targets) - if(get_dist(src, C.parent) > gravity_power_range) - remove_gravity(C) - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/add_gravity(atom/A, new_gravity = 2) - var/datum/component/C = A.AddComponent(/datum/component/forced_gravity,new_gravity) - RegisterSignal(A, COMSIG_MOVABLE_MOVED, PROC_REF(__distance_check)) - gravito_targets.Add(C) - playsound(src, 'sound/effects/gravhit.ogg', 100, 1) - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/remove_gravity(datum/component/C) - UnregisterSignal(C.parent, COMSIG_MOVABLE_MOVED) - gravito_targets.Remove(C) - qdel(C) - -/mob/living/simple_animal/hostile/guardian/gravitokinetic/proc/__distance_check(atom/movable/AM, OldLoc, Dir, Forced) - if(get_dist(src, AM) > gravity_power_range) - remove_gravity(AM.GetComponent(/datum/component/forced_gravity)) diff --git a/code/modules/mob/living/simple_animal/guardian/types/lightning.dm b/code/modules/mob/living/simple_animal/guardian/types/lightning.dm deleted file mode 100644 index 546b140d3cdc..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/lightning.dm +++ /dev/null @@ -1,121 +0,0 @@ -//Beam -/obj/effect/ebeam/chain - name = "lightning chain" - layer = LYING_MOB_LAYER - -/mob/living/simple_animal/hostile/guardian/beam - melee_damage_lower = 7 - melee_damage_upper = 7 - attacktext = "shocks" - melee_damage_type = BURN - attack_sound = 'sound/machines/defib_zap.ogg' - damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - range = 7 - playstyle_string = span_holoparasite("As a lightning type, you will apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them.") - magic_fluff_string = span_holoparasite("..And draw the Tesla, a shocking, lethal source of power.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Lightning modules active. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one! It's a lightning carp! Everyone else goes zap zap.") - var/datum/beam/summonerchain - var/list/enemychains = list() - var/successfulshocks = 0 - -/mob/living/simple_animal/hostile/guardian/beam/AttackingTarget() - . = ..() - if(. && isliving(target) && target != src && target != summoner) - cleardeletedchains() - for(var/chain in enemychains) - var/datum/beam/B = chain - if(B.target == target) - return //oh this guy already HAS a chain, let's not chain again - if(enemychains.len > 2) - var/datum/beam/C = pick(enemychains) - qdel(C) - enemychains -= C - enemychains += Beam(target, "lightning[rand(1,12)]", time=70, maxdistance=7, beam_type=/obj/effect/ebeam/chain) - -/mob/living/simple_animal/hostile/guardian/beam/Destroy() - removechains() - return ..() - -/mob/living/simple_animal/hostile/guardian/beam/Manifest() - . = ..() - if(.) - if(summoner) - summonerchain = Beam(summoner, "lightning[rand(1,12)]", time=INFINITY, maxdistance=INFINITY, beam_type=/obj/effect/ebeam/chain) - while(loc != summoner) - if(successfulshocks > 5) - successfulshocks = 0 - if(shockallchains()) - successfulshocks++ - SLEEP_CHECK_DEATH(3) - -/mob/living/simple_animal/hostile/guardian/beam/Recall() - . = ..() - if(.) - removechains() - -/mob/living/simple_animal/hostile/guardian/beam/proc/cleardeletedchains() - if(summonerchain && QDELETED(summonerchain)) - summonerchain = null - if(enemychains.len) - for(var/chain in enemychains) - var/datum/cd = chain - if(!chain || QDELETED(cd)) - enemychains -= chain - -/mob/living/simple_animal/hostile/guardian/beam/proc/shockallchains() - . = 0 - cleardeletedchains() - if(summoner) - if(!summonerchain) - summonerchain = Beam(summoner, "lightning[rand(1,12)]", time=INFINITY, maxdistance=INFINITY, beam_type=/obj/effect/ebeam/chain) - . += chainshock(summonerchain) - if(enemychains.len) - for(var/chain in enemychains) - . += chainshock(chain) - -/mob/living/simple_animal/hostile/guardian/beam/proc/removechains() - if(summonerchain) - qdel(summonerchain) - summonerchain = null - if(enemychains.len) - for(var/chain in enemychains) - qdel(chain) - enemychains = list() - -/mob/living/simple_animal/hostile/guardian/beam/proc/chainshock(datum/beam/B) - . = 0 - var/list/turfs = list() - for(var/E in B.elements) - var/obj/effect/ebeam/chainpart = E - if(chainpart && chainpart.x && chainpart.y && chainpart.z) - var/turf/T = get_turf_pixel(chainpart) - turfs |= T - if(T != get_turf(B.origin) && T != get_turf(B.target)) - for(var/turf/TU in circlerange(T, 1)) - turfs |= TU - for(var/turf in turfs) - var/turf/T = turf - for(var/mob/living/L in T) - if(L.stat != DEAD && L != src && L != summoner) - if(hasmatchingsummoner(L)) //if the summoner matches don't hurt them - continue - if(successfulshocks > 4) - if(iscarbon(L)) - var/mob/living/carbon/C = L - if(ishuman(C)) - var/mob/living/carbon/human/H = C - H.electrocution_animation(20) - C.jitteriness += 1000 - C.do_jitter_animation(jitteriness) - C.adjust_stutter(1 SECONDS) - spawn(20) - if(C) - C.jitteriness = max(C.jitteriness - 990, 10) - L.visible_message( - span_danger("[L] was shocked by the lightning chain!"), \ - span_userdanger("You are shocked by the lightning chain!"), \ - span_italics("You hear a heavy electrical crack.") \ - ) - L.adjustFireLoss(1.2) //adds up very rapidly - . = 1 diff --git a/code/modules/mob/living/simple_animal/guardian/types/protector.dm b/code/modules/mob/living/simple_animal/guardian/types/protector.dm deleted file mode 100644 index ad67e42118c5..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/protector.dm +++ /dev/null @@ -1,70 +0,0 @@ -//Protector -/mob/living/simple_animal/hostile/guardian/protector - melee_damage_lower = 15 - melee_damage_upper = 15 - range = 15 //worse for it due to how it leashes - damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4) - playstyle_string = span_holoparasite("As a protector type you cause your summoner to leash to you instead of you leashing to them and have two modes; Combat Mode, where you do and take medium damage, and Protection Mode, where you do and take almost no damage, but move slightly slower.") - magic_fluff_string = span_holoparasite("..And draw the Guardian, a stalwart protector that never leaves the side of its charge.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Protector modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! You caught one! Wait, no... it caught you! The fisher has become the fishy.") - toggle_button_type = /atom/movable/screen/guardian/ToggleMode - var/toggle = FALSE - -/mob/living/simple_animal/hostile/guardian/protector/ex_act(severity) - if(severity == 1) - adjustBruteLoss(400) //if in protector mode, will do 20 damage and not actually necessarily kill the summoner - else - ..() - if(QDELETED(src)) - return - if(toggle) - visible_message(span_danger("The explosion glances off [src]'s energy shielding!")) - -/mob/living/simple_animal/hostile/guardian/protector/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(. > 0 && toggle) - var/image/I = new('icons/effects/effects.dmi', src, "shield-flash", MOB_LAYER+0.01, dir = pick(GLOB.cardinals)) - if(namedatum) - I.color = namedatum.color - flick_overlay_view(I, src, 5) - -/mob/living/simple_animal/hostile/guardian/protector/ToggleMode() - if(cooldown > world.time) - return 0 - cooldown = world.time + 10 - if(toggle) - cut_overlays() - melee_damage_lower = initial(melee_damage_lower) - melee_damage_upper = initial(melee_damage_upper) - speed = initial(speed) - damage_coeff = list(BRUTE = 0.4, BURN = 0.4, TOX = 0.4, CLONE = 0.4, STAMINA = 0, OXY = 0.4) - to_chat(src, "You switch to combat mode.") - toggle = FALSE - else - var/mutable_appearance/shield_overlay = mutable_appearance('icons/effects/effects.dmi', "shield-grey") - if(namedatum) - shield_overlay.color = namedatum.color - add_overlay(shield_overlay) - melee_damage_lower = 2 - melee_damage_upper = 2 - speed = 1 - damage_coeff = list(BRUTE = 0.05, BURN = 0.05, TOX = 0.05, CLONE = 0.05, STAMINA = 0, OXY = 0.05) //damage? what's damage? - to_chat(src, "You switch to protection mode.") - toggle = TRUE - -/mob/living/simple_animal/hostile/guardian/protector/snapback() //snap to what? snap to the guardian! - if(summoner) - if(get_dist(get_turf(summoner),get_turf(src)) <= range) - return - else - if(istype(summoner.loc, /obj/effect)) - to_chat(src, span_holoparasite("You moved out of range, and were pulled back! You can only move [range] meters from [summoner.real_name]!")) - visible_message(span_danger("\The [src] jumps back to its user.")) - Recall(TRUE) - else - to_chat(summoner, span_holoparasite("You moved out of range, and were pulled back! You can only move [range] meters from [real_name]!")) - summoner.visible_message(span_danger("\The [summoner] jumps back to [summoner.p_their()] protector.")) - new /obj/effect/temp_visual/guardian/phase/out(get_turf(summoner)) - summoner.forceMove(get_turf(src)) - new /obj/effect/temp_visual/guardian/phase(get_turf(summoner)) diff --git a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm b/code/modules/mob/living/simple_animal/guardian/types/ranged.dm deleted file mode 100644 index 9ff6b8827553..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/ranged.dm +++ /dev/null @@ -1,144 +0,0 @@ -//Ranged -/obj/projectile/guardian - name = "crystal spray" - icon_state = "guardian" - damage = 5 - damage_type = BRUTE - armour_penetration = 100 - -/mob/living/simple_animal/hostile/guardian/ranged - a_intent = INTENT_HELP - friendly = "quietly assesses" - melee_damage_lower = 10 - melee_damage_upper = 10 - damage_coeff = list(BRUTE = 0.9, BURN = 0.9, TOX = 0.9, CLONE = 0.9, STAMINA = 0, OXY = 0.9) - projectiletype = /obj/projectile/guardian - ranged_cooldown_time = 1 //fast! - projectilesound = 'sound/effects/hit_on_shattered_glass.ogg' - ranged = 1 - range = 13 - playstyle_string = span_holoparasite("As a ranged type, you have only light damage resistance, but are capable of spraying shards of crystal at incredibly high speed. You can also deploy surveillance snares to monitor enemy movement. Finally, you can switch to scout mode, in which you can't attack, but can move without limit.") - magic_fluff_string = span_holoparasite("..And draw the Sentinel, an alien master of ranged combat.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Ranged combat modules active. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! Caught one, it's a ranged carp. This fishy can watch people pee in the ocean.") - see_invisible = SEE_INVISIBLE_LIVING - see_in_dark = 8 - toggle_button_type = /atom/movable/screen/guardian/ToggleMode - var/list/snares = list() - var/toggle = FALSE - -/mob/living/simple_animal/hostile/guardian/ranged/Initialize(mapload) //yogs start - . = ..() - LoadComponent(/datum/component/walk) //yogs end - -/mob/living/simple_animal/hostile/guardian/ranged/ToggleMode() - if(src.loc == summoner) - if(toggle) - ranged = initial(ranged) - melee_damage_lower = initial(melee_damage_lower) - melee_damage_upper = initial(melee_damage_upper) - obj_damage = initial(obj_damage) - environment_smash = initial(environment_smash) - alpha = 255 - range = initial(range) - to_chat(src, "You switch to combat mode.") - toggle = FALSE - else - ranged = 0 - melee_damage_lower = 0 - melee_damage_upper = 0 - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - alpha = 45 - range = 255 - to_chat(src, "You switch to scout mode.") - toggle = TRUE - else - to_chat(src, "You have to be recalled to toggle modes!") - -/mob/living/simple_animal/hostile/guardian/ranged/Shoot(atom/targeted_atom) - . = ..() - if(istype(., /obj/projectile)) - var/obj/projectile/P = . - if(namedatum) - P.color = namedatum.color - -/mob/living/simple_animal/hostile/guardian/ranged/ToggleLight() - var/msg - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - msg = "You activate your night vision." - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - msg = "You increase your night vision." - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - msg = "You maximize your night vision." - else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - msg = "You deactivate your night vision." - - to_chat(src, span_notice("[msg]")) - - -/mob/living/simple_animal/hostile/guardian/ranged/verb/Snare() - set name = "Set Surveillance Snare" - set category = "Guardian" - set desc = "Set an invisible snare that will alert you when living creatures walk over it. Max of 5" - if(src.snares.len <6) - var/turf/snare_loc = get_turf(src.loc) - var/obj/effect/snare/S = new /obj/effect/snare(snare_loc) - S.spawner = src - S.name = "[get_area(snare_loc)] snare ([rand(1, 1000)])" - src.snares |= S - to_chat(src, "Surveillance snare deployed!") - else - to_chat(src, "You have too many snares deployed. Remove some first.") - -/mob/living/simple_animal/hostile/guardian/ranged/verb/DisarmSnare() - set name = "Remove Surveillance Snare" - set category = "Guardian" - set desc = "Disarm unwanted surveillance snares." - var/picked_snare = input(src, "Pick which snare to remove", "Remove Snare") as null|anything in src.snares - if(picked_snare) - src.snares -= picked_snare - qdel(picked_snare) - to_chat(src, "Snare disarmed.") - -/obj/effect/snare - name = "snare" - desc = "You shouldn't be seeing this!" - var/mob/living/simple_animal/hostile/guardian/spawner - invisibility = INVISIBILITY_ABSTRACT - - -/obj/effect/snare/Crossed(AM as mob|obj) - . = ..() - if(isliving(AM) && spawner && spawner.summoner && AM != spawner && !spawner.hasmatchingsummoner(AM)) - to_chat(spawner.summoner, "[AM] has crossed surveillance snare, [name].") - var/list/guardians = spawner.summoner.hasparasites() - for(var/para in guardians) - to_chat(para, "[AM] has crossed surveillance snare, [name].") - -/obj/effect/snare/singularity_act() - return - -/obj/effect/snare/singularity_pull() - return - -/mob/living/simple_animal/hostile/guardian/ranged/Manifest(forced) - if (toggle) - incorporeal_move = INCORPOREAL_MOVE_BASIC - var/datum/component/walk/incorp = GetComponent(/datum/component/walk) //yogs start - if(incorp) - incorp.signal_enabled = TRUE //yogs end - . = ..() - -/mob/living/simple_animal/hostile/guardian/ranged/Recall(forced) - // To stop scout mode from moving when recalled - incorporeal_move = FALSE - var/datum/component/walk/incorp = GetComponent(/datum/component/walk) //yogs start - if(incorp) - incorp.signal_enabled = FALSE //yogs end - . = ..() diff --git a/code/modules/mob/living/simple_animal/guardian/types/standard.dm b/code/modules/mob/living/simple_animal/guardian/types/standard.dm deleted file mode 100644 index 48a8be237d3a..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/standard.dm +++ /dev/null @@ -1,31 +0,0 @@ -//Standard -/mob/living/simple_animal/hostile/guardian/punch - melee_damage_lower = 20 - melee_damage_upper = 20 - obj_damage = 80 - next_move_modifier = 0.8 //attacks 20% faster - environment_smash = ENVIRONMENT_SMASH_WALLS - playstyle_string = span_holoparasite("As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls.") - magic_fluff_string = span_holoparasite("..And draw the Assistant, faceless and generic, but never to be underestimated.") - tech_fluff_string = span_holoparasite("Boot sequence complete. Standard combat modules loaded. Holoparasite swarm online.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! You caught one! It's really boring and standard. Better punch some walls to ease the tension.") - var/battlecry = "AT" - -/mob/living/simple_animal/hostile/guardian/punch/verb/Battlecry() - set name = "Set Battlecry" - set category = "Guardian" - set desc = "Choose what you shout as you punch people." - var/input = stripped_input(src,"What do you want your battlecry to be? Max length of 6 characters.", ,"", 7) - if(input) - battlecry = input - - - -/mob/living/simple_animal/hostile/guardian/punch/AttackingTarget() - . = ..() - if(isliving(target)) - say("[battlecry][battlecry][battlecry][battlecry][battlecry][battlecry][battlecry][battlecry][battlecry][battlecry]!!", ignore_spam = TRUE) - playsound(loc, src.attack_sound, 50, 1, 1) - playsound(loc, src.attack_sound, 50, 1, 1) - playsound(loc, src.attack_sound, 50, 1, 1) - playsound(loc, src.attack_sound, 50, 1, 1) diff --git a/code/modules/mob/living/simple_animal/guardian/types/support.dm b/code/modules/mob/living/simple_animal/guardian/types/support.dm deleted file mode 100644 index 979fac94dbfd..000000000000 --- a/code/modules/mob/living/simple_animal/guardian/types/support.dm +++ /dev/null @@ -1,146 +0,0 @@ -//Healer -/mob/living/simple_animal/hostile/guardian/healer - a_intent = INTENT_HARM - friendly = "heals" - speed = 0 - damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - melee_damage_lower = 15 - melee_damage_upper = 15 - playstyle_string = span_holoparasite("As a support type, you may toggle your basic attacks to a healing mode. In addition, Alt-Clicking on an adjacent object or mob will warp them to your bluespace beacon after a short delay.") - magic_fluff_string = span_holoparasite("..And draw the CMO, a potent force of life... and death.") - carp_fluff_string = span_holoparasite("CARP CARP CARP! You caught a support carp. It's a kleptocarp!") - tech_fluff_string = span_holoparasite("Boot sequence complete. Support modules active. Holoparasite swarm online.") - toggle_button_type = /atom/movable/screen/guardian/ToggleMode - var/obj/structure/receiving_pad/beacon - var/beacon_cooldown = 0 - var/toggle = FALSE - -/mob/living/simple_animal/hostile/guardian/healer/Initialize(mapload) - . = ..() - var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - medsensor.show_to(src) - -/mob/living/simple_animal/hostile/guardian/healer/get_status_tab_items() - . = ..() - if(beacon_cooldown >= world.time) - . += "Beacon Cooldown Remaining: [DisplayTimeText(beacon_cooldown - world.time)]" - -/mob/living/simple_animal/hostile/guardian/healer/AttackingTarget() - . = ..() - if(is_deployed() && toggle && iscarbon(target)) - var/mob/living/carbon/C = target - C.adjustBruteLoss(-5) - C.adjustFireLoss(-5) - C.adjustOxyLoss(-5) - C.adjustToxLoss(-5) - var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(C)) - if(namedatum) - H.color = namedatum.color - if(C == summoner) - update_health_hud() - med_hud_set_health() - med_hud_set_status() - -/mob/living/simple_animal/hostile/guardian/healer/ToggleMode() - if(src.loc == summoner) - if(toggle) - a_intent = INTENT_HARM - speed = 0 - damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, CLONE = 0.7, STAMINA = 0, OXY = 0.7) - melee_damage_lower = 15 - melee_damage_upper = 15 - to_chat(src, "You switch to combat mode.") - toggle = FALSE - else - a_intent = INTENT_HELP - speed = 1 - damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) - melee_damage_lower = 0 - melee_damage_upper = 0 - to_chat(src, "You switch to healing mode.") - toggle = TRUE - else - to_chat(src, "You have to be recalled to toggle modes!") - - -/mob/living/simple_animal/hostile/guardian/healer/verb/Beacon() - set name = "Place Bluespace Beacon" - set category = "Guardian" - set desc = "Mark a floor as your beacon point, allowing you to warp targets to it. Your beacon will not work at extreme distances." - - if(beacon_cooldown >= world.time) - to_chat(src, span_danger("Your power is on cooldown. You must wait five minutes between placing beacons.")) - return - - var/turf/beacon_loc = get_turf(src.loc) - if(!isfloorturf(beacon_loc)) - to_chat(src, span_danger("Your beacon can only be placed on solid flooring!")) - return - - if(beacon) - beacon.disappear() - beacon = null - - beacon = new(beacon_loc, src) - - to_chat(src, "Beacon placed! You may now warp targets and objects to it, including your user, via Alt+Click.") - - beacon_cooldown = world.time + 3000 - -/obj/structure/receiving_pad - name = "bluespace receiving pad" - icon = 'icons/turf/floors.dmi' - desc = "A receiving zone for bluespace teleportations." - icon_state = "light_on-w" - light_range = MINIMUM_USEFUL_LIGHT_RANGE - density = FALSE - anchored = TRUE - layer = ABOVE_OPEN_TURF_LAYER - -/obj/structure/receiving_pad/New(loc, mob/living/simple_animal/hostile/guardian/healer/G) - . = ..() - if(G.namedatum) - add_atom_colour(G.namedatum.color, FIXED_COLOUR_PRIORITY) - -/obj/structure/receiving_pad/proc/disappear() - visible_message("[src] vanishes!") - qdel(src) - -/mob/living/simple_animal/hostile/guardian/healer/AltClickOn(atom/movable/A) - if(!istype(A)) - return - if(src.loc == summoner) - to_chat(src, "You must be manifested to warp a target!") - return - if(!beacon) - to_chat(src, "You need a beacon placed to warp things!") - return - if(!Adjacent(A)) - to_chat(src, "You must be adjacent to your target!") - return - if(A.anchored) - to_chat(src, "Your target cannot be anchored!") - return - - var/turf/T = get_turf(A) - if(beacon.z != T.z) - to_chat(src, "The beacon is too far away to warp to!") - return - - to_chat(src, "You begin to warp [A].") - A.visible_message(span_danger("[A] starts to glow faintly!"), \ - span_userdanger("You start to faintly glow, and you feel strangely weightless!")) - do_attack_animation(A) - - if(!do_after(src, 6 SECONDS, A)) //now start the channel - to_chat(src, "You need to hold still!") - return - - new /obj/effect/temp_visual/guardian/phase/out(T) - if(isliving(A)) - var/mob/living/L = A - L.flash_act() - A.visible_message(span_danger("[A] disappears in a flash of light!"), \ - span_userdanger("Your vision is obscured by a flash of light!")) - do_teleport(A, beacon, 0, channel = TELEPORT_CHANNEL_BLUESPACE) - new /obj/effect/temp_visual/guardian/phase(get_turf(A)) diff --git a/code/modules/tgui/states/debug.dm b/code/modules/tgui/states/debug.dm deleted file mode 100644 index 6c600b38cea1..000000000000 --- a/code/modules/tgui/states/debug.dm +++ /dev/null @@ -1,6 +0,0 @@ -GLOBAL_DATUM_INIT(debug_state, /datum/ui_state/debug_state, new) - -/datum/ui_state/debug_state/can_use_topic(src_object, mob/user) - if(check_rights_for(user.client, R_DEBUG)) - return UI_INTERACTIVE - return UI_CLOSE diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index af3d3c365116..71107e4c82e7 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -64,6 +64,7 @@ /// A trait source when adding traits through unit tests #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" +// BEGIN_INCLUDE #include "anchored_mobs.dm" #include "component_tests.dm" #include "dragon_expiration.dm" @@ -78,6 +79,7 @@ #include "subsystem_init.dm" #include "timer_sanity.dm" #include "unit_test.dm" +// END_INCLUDE #undef TEST_ASSERT #undef TEST_ASSERT_EQUAL diff --git a/tools/Redirector/Configurations.dm b/tools/Redirector/Configurations.dm deleted file mode 100644 index bc0d56361c0c..000000000000 --- a/tools/Redirector/Configurations.dm +++ /dev/null @@ -1,53 +0,0 @@ -/* - Written by contributor Doohl for the /tg/station Open Source project, hosted on Google Code. - (2012) - */ - -var/list/config_stream = list() -var/list/servers = list() -var/list/servernames = list() -var/list/adminfiles = list() -var/list/adminkeys = list() - -proc/gen_configs() - - config_stream = dd_file2list("config.txt") - - var/server_gen = 0 // if the stream is looking for servers - var/admin_gen = 0 // if the stream is looking for admins - for(var/line in config_stream) - - if(line == "\[SERVERS\]") - server_gen = 1 - if(admin_gen) - admin_gen = 0 - - else if(line == "\[ADMINS\]") - admin_gen = 1 - if(server_gen) - server_gen = 0 - - else - if(findtext(line, ".") && !findtext(line, "##")) - if(server_gen) - var/filterline = dd_replacetext(line, " ", "") - var/serverlink = copytext(filterline, findtext( filterline, ")") + 1) - servers.Add(serverlink) - servernames.Add( copytext(line, findtext(line, "("), findtext(line, ")") + 1)) - - else if(admin_gen) - adminfiles.Add(line) - world << line - - - // Generate the list of admins now - - for(var/file in adminfiles) - var/admin_config_stream = dd_file2list(file) - - for(var/line in admin_config_stream) - - var/akey = copytext(line, 1, findtext(line, " ")) - adminkeys.Add(akey) - - diff --git a/tools/Redirector/Redirect_Tgstation.dmb b/tools/Redirector/Redirect_Tgstation.dmb deleted file mode 100644 index 278e723b8967..000000000000 Binary files a/tools/Redirector/Redirect_Tgstation.dmb and /dev/null differ diff --git a/tools/Redirector/Redirect_Tgstation.dme b/tools/Redirector/Redirect_Tgstation.dme deleted file mode 100644 index f7d4229bf972..000000000000 --- a/tools/Redirector/Redirect_Tgstation.dme +++ /dev/null @@ -1,21 +0,0 @@ -// DM Environment file for Redirect_Tgstation.dme. -// All manual changes should be made outside the BEGIN_ and END_ blocks. -// New source code should be placed in .dm files: choose File/New --> Code File. - -// BEGIN_INTERNALS -// END_INTERNALS - -// BEGIN_FILE_DIR -#define FILE_DIR . -// END_FILE_DIR - -// BEGIN_PREFERENCES -// END_PREFERENCES - -// BEGIN_INCLUDE -#include "Configurations.dm" -#include "Redirector.dm" -#include "textprocs.dm" -#include "skin.dmf" -// END_INCLUDE - diff --git a/tools/Redirector/Redirector.dm b/tools/Redirector/Redirector.dm deleted file mode 100644 index 0b080bfa65a5..000000000000 --- a/tools/Redirector/Redirector.dm +++ /dev/null @@ -1,87 +0,0 @@ -/* - Written by contributor Doohl for the /tg/station Open Source project, hosted on Google Code. - (2012) - */ - -/* TODO: work on server selection for detected admins */ - - -#define ADMINS 1 -#define PLAYERS 0 - -var/player_weight = 1 // players are more likely to join a server with less players -var/admin_weight = 5 // admins are more likely to join a server with less admins - -var/player_substr = "players=" // search for this substring to locate # of players -var/admin_substr = "admins=" // search for this to locate # of admins - -/world - name = "TGstation Redirector" - -/world/New() - ..() - gen_configs() - -/datum/server - var/players = 0 - var/admins = 0 - var/weight = 0 // lower weight is good; highet weight is bad - - var/link = "" - -/mob/Login() - ..() - - var/list/weights = list() - var/list/servers = list() - for(var/x in global.servers) - - world << "[x] [servernames[ global.servers.Find(x) ]]" - - var/info = world.Export("[x]?status") - var/datum/server/S = new() - S.players = extract(info, PLAYERS) - S.admins = extract(info, ADMINS) - - S.weight += player_weight * S.players - S.link = x - - world << S.players - world << S.admins - - weights.Add(S.weight) - servers.Add(S) - - var/lowest = min(weights) - var/serverlink - for(var/datum/server/S in servers) - if(S.weight == lowest) - serverlink = S.link - - src << link(serverlink) - -/proc/extract(data, type = PLAYERS) - - var/nextpos = 0 - - if(type == PLAYERS) - - nextpos = findtextEx(data, player_substr) - nextpos += length(player_substr) - - else - - nextpos = findtextEx(data, admin_substr) - nextpos += length(admin_substr) - - var/returnval = "" - - for(var/i = 1, i <= 10, i++) - - var/interval = copytext(data, nextpos + (i-1), nextpos + i) - if(interval == "&") - break - else - returnval += interval - - return returnval diff --git a/tools/Redirector/config.txt b/tools/Redirector/config.txt deleted file mode 100644 index 4cf2d6bd79f5..000000000000 --- a/tools/Redirector/config.txt +++ /dev/null @@ -1,12 +0,0 @@ -[SERVERS] -## Simply enter a list of servers to poll. Be sure to specify a server name in parentheses. - -(Sibyl #1) byond://game.nanotrasen.com:1337 - -(Sibyl #2) byond://game.nanotrasen.com:2337 - - -[ADMINS] -## Specify some standard Windows filepaths (you may use relative paths) for admin txt lists to poll. - -C:\SS13\config\admins.txt diff --git a/tools/Redirector/skin.dmf b/tools/Redirector/skin.dmf deleted file mode 100644 index 2a3e8a734c44..000000000000 --- a/tools/Redirector/skin.dmf +++ /dev/null @@ -1,149 +0,0 @@ -macro "macro" - elem - name = "North+REP" - command = ".north" - is-disabled = false - elem - name = "South+REP" - command = ".south" - is-disabled = false - elem - name = "East+REP" - command = ".east" - is-disabled = false - elem - name = "West+REP" - command = ".west" - is-disabled = false - elem - name = "Northeast+REP" - command = ".northeast" - is-disabled = false - elem - name = "Northwest+REP" - command = ".northwest" - is-disabled = false - elem - name = "Southeast+REP" - command = ".southeast" - is-disabled = false - elem - name = "Southwest+REP" - command = ".southwest" - is-disabled = false - elem - name = "Center+REP" - command = ".center" - is-disabled = false - - -menu "menu" - elem - name = "&Quit" - command = ".quit" - category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false - saved-params = "is-checked" - - -window "window" - elem "window" - type = MAIN - pos = 281,0 - size = 594x231 - anchor1 = none - anchor2 = none - font-family = "" - font-size = 0 - font-style = "" - text-color = #000000 - background-color = #000000 - is-visible = false - is-disabled = false - is-transparent = false - is-default = true - border = none - drop-zone = false - right-click = false - saved-params = "pos;size;is-minimized;is-maximized" - on-size = "" - title = "" - titlebar = true - statusbar = false - can-close = true - can-minimize = true - can-resize = true - is-pane = false - is-minimized = false - is-maximized = false - can-scroll = none - icon = "" - image = "" - image-mode = stretch - keep-aspect = false - transparent-color = none - alpha = 255 - macro = "macro" - menu = "" - on-close = "" - elem "servers" - type = GRID - pos = 8,8 - size = 576x152 - anchor1 = none - anchor2 = none - font-family = "" - font-size = 0 - font-style = "" - text-color = #ffffff - background-color = #000000 - is-visible = true - is-disabled = false - is-transparent = false - is-default = false - border = none - drop-zone = true - right-click = false - saved-params = "" - on-size = "" - cells = 1x1 - current-cell = 1,1 - show-lines = none - small-icons = true - show-names = true - enable-http-images = false - link-color = #0000ff - visited-color = #ff00ff - line-color = #c0c0c0 - style = "" - is-list = false - elem "output1" - type = OUTPUT - pos = 8,168 - size = 576x56 - anchor1 = none - anchor2 = none - font-family = "" - font-size = 0 - font-style = "" - text-color = #ffffff - background-color = #000000 - is-visible = true - is-disabled = false - is-transparent = false - is-default = true - border = none - drop-zone = false - right-click = false - saved-params = "max-lines" - on-size = "" - link-color = #0000ff - visited-color = #ff00ff - style = "" - enable-http-images = false - max-lines = 1000 - image = "" - diff --git a/tools/Redirector/textprocs.dm b/tools/Redirector/textprocs.dm deleted file mode 100644 index b62232fe1177..000000000000 --- a/tools/Redirector/textprocs.dm +++ /dev/null @@ -1,153 +0,0 @@ -/* - Written by contributor Doohl for the /tg/station Open Source project, hosted on Google Code. - (2012) - - NOTE: The below functions are part of BYOND user Deadron's "TextHandling" library. - [ http://www.byond.com/developer/Deadron/TextHandling ] - */ - - -proc - /////////////////// - // Reading files // - /////////////////// - dd_file2list(file_path, separator = "\n") - var/file - if (isfile(file_path)) - file = file_path - else - file = file(file_path) - return dd_text2list(file2text(file), separator) - - - //////////////////// - // Replacing text // - //////////////////// - dd_replacetext(text, search_string, replacement_string) - // A nice way to do this is to split the text into an array based on the search_string, - // then put it back together into text using replacement_string as the new separator. - var/list/textList = dd_text2list(text, search_string) - return dd_list2text(textList, replacement_string) - - - dd_replaceText(text, search_string, replacement_string) - var/list/textList = dd_text2List(text, search_string) - return dd_list2text(textList, replacement_string) - - - ///////////////////// - // Prefix checking // - ///////////////////// - dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - - dd_hasPrefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - - - ///////////////////// - // Suffix checking // - ///////////////////// - dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if (start) - return findtext(text, suffix, start) - - dd_hasSuffix(text, suffix) - var/start = length(text) - length(suffix) - if (start) - return findtextEx(text, suffix, start) - - ///////////////////////////// - // Turning text into lists // - ///////////////////////////// - dd_text2list(text, separator) - var/textlength = length(text) - var/separatorlength = length(separator) - var/list/textList = new /list() - var/searchPosition = 1 - var/findPosition = 1 - var/buggyText - while (1) // Loop forever. - findPosition = findtext(text, separator, searchPosition, 0) - buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. - textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). - - searchPosition = findPosition + separatorlength // Skip over separator. - if (findPosition == 0) // Didn't find anything at end of string so stop here. - return textList - else - if (searchPosition > textlength) // Found separator at very end of string. - textList += "" // So add empty element. - return textList - - dd_text2List(text, separator) - var/textlength = length(text) - var/separatorlength = length(separator) - var/list/textList = new /list() - var/searchPosition = 1 - var/findPosition = 1 - var/buggyText - while (1) // Loop forever. - findPosition = findtextEx(text, separator, searchPosition, 0) - buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. - textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). - - searchPosition = findPosition + separatorlength // Skip over separator. - if (findPosition == 0) // Didn't find anything at end of string so stop here. - return textList - else - if (searchPosition > textlength) // Found separator at very end of string. - textList += "" // So add empty element. - return textList - - dd_list2text(list/the_list, separator) - var/total = the_list.len - if (total == 0) // Nothing to work with. - return - - var/newText = "[the_list[1]]" // Treats any object/number as text also. - var/count - for (count = 2, count <= total, count++) - if (separator) - newText += separator - newText += "[the_list[count]]" - return newText - - dd_centertext(message, length) - var/new_message = message - var/size = length(message) - if (size == length) - return new_message - if (size > length) - return copytext(new_message, 1, length + 1) - - // Need to pad text to center it. - var/delta = length - size - if (delta == 1) - // Add one space after it. - return new_message + " " - - // Is this an odd number? If so, add extra space to front. - if (delta % 2) - new_message = " " + new_message - delta-- - - // Divide delta in 2, add those spaces to both ends. - delta = delta / 2 - var/spaces = "" - for (var/count = 1, count <= delta, count++) - spaces += " " - return spaces + new_message + spaces - - dd_limittext(message, length) - // Truncates text to limit if necessary. - var/size = length(message) - if (size <= length) - return message - else - return copytext(message, 1, length + 1) diff --git a/tools/travis/check_dme_alphabetical.py b/tools/travis/check_dme_alphabetical.py deleted file mode 100644 index c818d323859b..000000000000 --- a/tools/travis/check_dme_alphabetical.py +++ /dev/null @@ -1,45 +0,0 @@ -import sys -if len(sys.argv) < 2 or sys.argv[1][-4:] != ".dme": - print("No suitable .dme file provided") - sys.exit(1) - -f = open(sys.argv[1]) - -hasbegun = False -oldline = "" -newline = "" -iswrong = 0 -line = 1 - -for newline in f: - line = line + 1 - newline = newline.lower() - if hasbegun: - if "// end_include" in newline: - hasbegun = False - else: - if newline > oldline or oldline == "": - oldline = newline - continue - else: - oldsplit = oldline.split("\\") - newsplit = newline.split("\\") - if len(oldsplit) != len(newsplit): - length = len(oldsplit)-1 if len(oldsplit) < len(newsplit) else len(newsplit)-1 - for x in range(length): - if(oldsplit[x] > newsplit[x]): - print("::error file=" + sys.argv[1] + ",line=" + str(line) + "::This line is out of alphabetical order") - iswrong += 1 - break - oldline = newline - continue - - else: - if "// begin_include" in newline: - hasbegun = True -if iswrong > 0: - print("There are " + str(iswrong) + " lines out of place, please organize the .DME alphabetically.") -f.close() -sys.exit(iswrong) - - diff --git a/tools/travis/ticked_file_enforcement/schemas/unit_tests.json b/tools/travis/ticked_file_enforcement/schemas/unit_tests.json new file mode 100644 index 000000000000..adf85445f7eb --- /dev/null +++ b/tools/travis/ticked_file_enforcement/schemas/unit_tests.json @@ -0,0 +1,7 @@ +{ + "file": "code/modules/unit_tests/_unit_tests.dm", + "scannable_directory": "code/modules/unit_tests/", + "subdirectories": false, + "excluded_files": [], + "forbidden_includes": [] +} diff --git a/tools/travis/ticked_file_enforcement/schemas/yogstation_dme.json b/tools/travis/ticked_file_enforcement/schemas/yogstation_dme.json new file mode 100644 index 000000000000..ea7eb8b068f8 --- /dev/null +++ b/tools/travis/ticked_file_enforcement/schemas/yogstation_dme.json @@ -0,0 +1,10 @@ +{ + "file": "yogstation.dme", + "scannable_directory": "code/", + "subdirectories": true, + "excluded_files": [], + "forbidden_includes": [ + "code/modules/tgs/**/*.dm", + "code/modules/unit_tests/[!_]*.dm" + ] +} diff --git a/tools/travis/ticked_file_enforcement/ticked_file_enforcement.py b/tools/travis/ticked_file_enforcement/ticked_file_enforcement.py new file mode 100644 index 000000000000..9a05cdbc2654 --- /dev/null +++ b/tools/travis/ticked_file_enforcement/ticked_file_enforcement.py @@ -0,0 +1,156 @@ +import codecs +import fnmatch +import functools +import glob +import json +import os +import sys + +# simple way to check if we're running on github actions, or on a local machine +on_github = os.getenv("CI") == "true" # Yogs edit: This is "GITHUB_ACTIONS" on TG. + +def green(text): + return "\033[32m" + str(text) + "\033[0m" + +def red(text): + return "\033[31m" + str(text) + "\033[0m" + +def blue(text): + return "\033[34m" + str(text) + "\033[0m" + +schema = json.load(sys.stdin) +file_reference = schema["file"] +file_reference_basename = os.path.basename(file_reference) +scannable_directory = schema["scannable_directory"] +subdirectories = schema["subdirectories"] +FORBIDDEN_INCLUDES = schema["forbidden_includes"] +excluded_files = schema["excluded_files"] + +def post_error(string): + print(red(f"Ticked File Enforcement [{file_reference}]: " + string)) + if on_github: + print(f"::error file={file_reference},line=1,title=Ticked File Enforcement::{string}") + +for excluded_file in excluded_files: + full_file_path = scannable_directory + excluded_file + if not os.path.isfile(full_file_path): + post_error(f"Excluded file {full_file_path} does not exist, please remove it!") + sys.exit(1) + +file_extensions = ("dm", "dmf") + +reading = False +lines = [] +total = 0 + +with open(file_reference, 'r') as file: + for line in file: + total += 1 + line = line.strip() + + if line == "// BEGIN_INCLUDE": + reading = True + continue + elif line == "// END_INCLUDE": + break + elif not reading: + continue + + lines.append(line) + +offset = total - len(lines) +print(blue(f"Ticked File Enforcement: {offset} lines were ignored in output for [{file_reference}].")) +fail_no_include = False + +scannable_files = [] +for file_extension in file_extensions: + compiled_directory = f"{scannable_directory}/**/*.{file_extension}" + scannable_files += glob.glob(compiled_directory, recursive=True) + +if len(scannable_files) == 0: + post_error(f"No files were found in {scannable_directory}. Ticked File Enforcement has failed!") + sys.exit(1) + +for code_file in scannable_files: + dm_path = "" + + if subdirectories is True: + dm_path = code_file.replace('/', '\\') + else: + dm_path = os.path.basename(code_file) + + included = f"#include \"{dm_path}\"" in lines + + forbid_include = False + for forbidable in FORBIDDEN_INCLUDES: + if not fnmatch.fnmatch(code_file, forbidable): + continue + + forbid_include = True + + if included: + post_error(f"{dm_path} should NOT be included.") + fail_no_include = True + + if forbid_include: + continue + + if not included: + if(dm_path == file_reference_basename): + continue + + if(dm_path in excluded_files): + continue + + post_error(f"Missing include for {dm_path}.") + fail_no_include = True + +if fail_no_include: + sys.exit(1) + +def compare_lines(a, b): + # Remove initial include as well as the final quotation mark + a = a[len("#include \""):-1].lower() + b = b[len("#include \""):-1].lower() + + split_by_period = a.split('.') + a_suffix = "" + if len(split_by_period) >= 2: + a_suffix = split_by_period[len(split_by_period) - 1] + split_by_period = b.split('.') + b_suffix = "" + if len(split_by_period) >= 2: + b_suffix = split_by_period[len(split_by_period) - 1] + + a_segments = a.split('\\') + b_segments = b.split('\\') + + for (a_segment, b_segment) in zip(a_segments, b_segments): + a_is_file = a_segment.endswith(file_extensions) + b_is_file = b_segment.endswith(file_extensions) + + # code\something.dm will ALWAYS come before code\directory\something.dm + if a_is_file and not b_is_file: + return -1 + + if b_is_file and not a_is_file: + return 1 + + # interface\something.dm will ALWAYS come after code\something.dm + if a_segment != b_segment: + # if we're at the end of a compare, then this is about the file name + # files with longer suffixes come after ones with shorter ones + if a_suffix != b_suffix: + return (a_suffix > b_suffix) - (a_suffix < b_suffix) + return (a_segment > b_segment) - (a_segment < b_segment) + + print(f"Two lines were exactly the same ({a} vs. {b})") + sys.exit(1) + +sorted_lines = sorted(lines, key = functools.cmp_to_key(compare_lines)) +for (index, line) in enumerate(lines): + if sorted_lines[index] != line: + post_error(f"The include at line {index + offset} is out of order ({line}, expected {sorted_lines[index]})") + sys.exit(1) + +print(green(f"Ticked File Enforcement: [{file_reference}] All includes (for {len(scannable_files)} scanned files) are in order!")) diff --git a/yogstation.dme b/yogstation.dme index 6a69253a9375..e90c45b269ea 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -2115,8 +2115,8 @@ #include "code\modules\client\preferences\auto_fit_viewport.dm" #include "code\modules\client\preferences\balloon_alerts.dm" #include "code\modules\client\preferences\bar_choice.dm" -#include "code\modules\client\preferences\clerk_choice.dm" #include "code\modules\client\preferences\chapel_choice.dm" +#include "code\modules\client\preferences\clerk_choice.dm" #include "code\modules\client\preferences\clothing.dm" #include "code\modules\client\preferences\credits.dm" #include "code\modules\client\preferences\donor.dm"