mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 10:01:58 +00:00
## About The Pull Request ~~Kept you waitin huh!~~ The projectile refactor is finally here, 4 years later. This PR (almost) completely rewrites projectile logic to be more maintainable and performant. ### Key changes: * Instead of moving by a fixed amount of pixels, potentially skipping tile corners and being performance-heavy, projectiles now use raymarching in order to teleport through tiles and only visually animate themselves. This allows us to do custom per-projectile animations and makes the code much more reliable, sane and maintainable. You (did not) serve us well, pixel_move. * Speed variable now measures how many tiles (if SSprojectiles has default values) a projectile passes in a tick instead of being a magical Kevinz Unit™️ coefficient. pixel_speed_multiplier has been retired because it never had a right to exist in the first place. __This means that downstreams will need to set all of their custom projectiles' speed values to ``pixel_speed_multiplier / speed``__ in order to prevent projectiles from inverting their speed. * Hitscans no longer operate with spartial vectors and instead only store key points in which the projectile impacted something or changed its angle. This should similarly make the code much easier to work with, as well as fixing some visual jank due to incorrect calculations. * Projectiles only delete themselves the ***next*** tick after impacting something or reaching their maximum range. Doing so allows them to finish their impact animation and hide themselves between ticks via animation chains. This means that projectiles no longer disappear ~a tile before hitting their target, and that we can finally make impact markers be consistent with where the projectile actually landed instead of being entirely random. <details> <summary>Here is an example of how this affects our slowest-moving projectile: Magic Missiles.</summary> Before: https://github.com/user-attachments/assets/06b3a980-4701-4aeb-aa3e-e21cd056020e After: https://github.com/user-attachments/assets/abe8ed5c-4b81-4120-8d2f-cf16ff5be915 </details> <details> <summary>And here is a much faster, and currently jankier, disabler SMG.</summary> Before: https://github.com/user-attachments/assets/2d84aef1-0c83-44ef-a698-8ec716587348 After: https://github.com/user-attachments/assets/2e7c1336-f611-404f-b3ff-87433398d238 </details> ### But how will this affect the ~~trout population~~ gameplay? Beyond improved visuals, smoother movement and a few minor bugfixes, this should not have a major gameplay impact. If something changed its behavior in an unexpected way or started looking odd, please make an issue report. Projectile impacts should now be consistent with their visual position, so hitting and dodging shots should be slightly easier and more intuitive. This PR should be testmerged extensively due to the amount of changes it brings and considerable difficulty in reviewing them. Please contact me to ensure its good to merge. Closes #71822 Closes #78547 Closes #78871 Closes #83901 Closes #87802 Closes #88073 ## Why It's Good For The Game Our core projectile code is an ungodly abomination that nobody except me, Kapu and Potato dared to poke in the past months (potentially longer). It is laggy, overcomplicated and absolutely unmaintaineable - while a lot of decisions made sense 4 years ago when we were attempting to introduce pixel movement, nowadays they are only acting as major roadblocks for any contributor who is attempting to make projectile behavior that differs from normal in any way. Huge thanks to Kapu and Potato (Lemon) on the discord for providing insights, ideas and advice throughout the past months regarding potential improvements to projectile code, almost all of which made it in. ## Changelog 🆑 qol: Projectiles now visually impact their targets instead of disappearing about a tile short of it. fix: Fixed multiple minor issues with projectile behavior refactor: Completely rewrote almost all of our projectile code - if anything broke or started looking/behaving oddly, make an issue report! /🆑
441 lines
15 KiB
Plaintext
441 lines
15 KiB
Plaintext
|
|
// Normal Mirrors
|
|
|
|
#define CHANGE_HAIR "Change Hair"
|
|
#define CHANGE_BEARD "Change Beard"
|
|
|
|
// Magic Mirrors!
|
|
|
|
#define CHANGE_RACE "Change Race"
|
|
#define CHANGE_SEX "Change Sex"
|
|
#define CHANGE_NAME "Change Name"
|
|
#define CHANGE_EYES "Change Eyes"
|
|
|
|
#define INERT_MIRROR_OPTIONS list(CHANGE_HAIR, CHANGE_BEARD)
|
|
#define PRIDE_MIRROR_OPTIONS list(CHANGE_HAIR, CHANGE_BEARD, CHANGE_RACE, CHANGE_SEX, CHANGE_EYES)
|
|
#define MAGIC_MIRROR_OPTIONS list(CHANGE_HAIR, CHANGE_BEARD, CHANGE_RACE, CHANGE_SEX, CHANGE_EYES, CHANGE_NAME)
|
|
|
|
/obj/structure/mirror
|
|
name = "mirror"
|
|
desc = "Mirror mirror on the wall, who's the most robust of them all?"
|
|
icon = 'icons/obj/watercloset.dmi'
|
|
icon_state = "mirror"
|
|
movement_type = FLOATING
|
|
density = FALSE
|
|
anchored = TRUE
|
|
integrity_failure = 0.5
|
|
max_integrity = 200
|
|
var/list/mirror_options = INERT_MIRROR_OPTIONS
|
|
|
|
///Flags this race must have to be selectable with this type of mirror.
|
|
var/race_flags = MIRROR_MAGIC
|
|
///List of all Races that can be chosen, decided by its Initialize.
|
|
var/list/selectable_races = list()
|
|
|
|
/obj/structure/mirror/Initialize(mapload)
|
|
. = ..()
|
|
update_choices()
|
|
|
|
/obj/structure/mirror/Destroy()
|
|
mirror_options = null
|
|
selectable_races = null
|
|
return ..()
|
|
|
|
/obj/structure/mirror/proc/update_choices()
|
|
for(var/i in mirror_options)
|
|
mirror_options[i] = icon('icons/hud/radial.dmi', i)
|
|
|
|
/obj/structure/mirror/Initialize(mapload)
|
|
. = ..()
|
|
var/static/list/reflection_filter = alpha_mask_filter(icon = icon('icons/obj/watercloset.dmi', "mirror_mask"))
|
|
var/static/matrix/reflection_matrix = matrix(0.75, 0, 0, 0, 0.75, 0)
|
|
var/datum/callback/can_reflect = CALLBACK(src, PROC_REF(can_reflect))
|
|
var/list/update_signals = list(COMSIG_ATOM_BREAK)
|
|
AddComponent(/datum/component/reflection, reflection_filter = reflection_filter, reflection_matrix = reflection_matrix, can_reflect = can_reflect, update_signals = update_signals)
|
|
|
|
/obj/structure/mirror/proc/can_reflect(atom/movable/target)
|
|
///I'm doing it this way too, because the signal is sent before the broken variable is set to TRUE.
|
|
if(atom_integrity <= integrity_failure * max_integrity)
|
|
return FALSE
|
|
if(broken || !isliving(target) || HAS_TRAIT(target, TRAIT_NO_MIRROR_REFLECTION))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
|
|
|
|
/obj/structure/mirror/Initialize(mapload)
|
|
. = ..()
|
|
find_and_hang_on_wall()
|
|
|
|
/obj/structure/mirror/broken
|
|
icon_state = "mirror_broke"
|
|
|
|
/obj/structure/mirror/broken/Initialize(mapload)
|
|
. = ..()
|
|
atom_break(null, mapload)
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
|
|
|
|
/obj/structure/mirror/attack_hand(mob/living/carbon/human/user)
|
|
. = ..()
|
|
|
|
if(. || !ishuman(user) || broken)
|
|
return TRUE
|
|
|
|
if(!istype(src, /obj/structure/mirror/magic) && !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
|
|
return TRUE //no tele-grooming (if nonmagical)
|
|
|
|
return display_radial_menu(user)
|
|
|
|
/obj/structure/mirror/proc/display_radial_menu(mob/living/carbon/human/user)
|
|
var/pick = show_radial_menu(user, src, mirror_options, user, radius = 36, require_near = TRUE, tooltips = TRUE)
|
|
if(!pick)
|
|
return TRUE //get out
|
|
|
|
switch(pick)
|
|
if(CHANGE_HAIR)
|
|
change_hair(user)
|
|
if(CHANGE_BEARD)
|
|
change_beard(user)
|
|
if(CHANGE_RACE)
|
|
change_race(user)
|
|
if(CHANGE_SEX) // sex: yes
|
|
change_sex(user)
|
|
if(CHANGE_NAME)
|
|
change_name(user)
|
|
if(CHANGE_EYES)
|
|
change_eyes(user)
|
|
|
|
return display_radial_menu(user)
|
|
|
|
/obj/structure/mirror/proc/change_beard(mob/living/carbon/human/beard_dresser)
|
|
if(beard_dresser.physique == FEMALE)
|
|
if(beard_dresser.facial_hairstyle == "Shaved")
|
|
balloon_alert(beard_dresser, "nothing to shave!")
|
|
return TRUE
|
|
var/shave_beard = tgui_alert(beard_dresser, "Shave your beard?", "Grooming", list("Yes", "No"))
|
|
if(shave_beard == "Yes")
|
|
beard_dresser.set_facial_hairstyle("Shaved", update = TRUE)
|
|
return TRUE
|
|
|
|
var/new_style = tgui_input_list(beard_dresser, "Select a facial hairstyle", "Grooming", SSaccessories.facial_hairstyles_list)
|
|
|
|
if(isnull(new_style))
|
|
return TRUE
|
|
|
|
if(HAS_TRAIT(beard_dresser, TRAIT_SHAVED))
|
|
to_chat(beard_dresser, span_notice("If only growing back facial hair were that easy for you... The reminder makes you feel terrible."))
|
|
beard_dresser.add_mood_event("bald_hair_day", /datum/mood_event/bald_reminder)
|
|
return TRUE
|
|
|
|
beard_dresser.set_facial_hairstyle(new_style, update = TRUE)
|
|
|
|
/obj/structure/mirror/proc/change_hair(mob/living/carbon/human/hairdresser)
|
|
var/new_style = tgui_input_list(hairdresser, "Select a hairstyle", "Grooming", SSaccessories.hairstyles_list)
|
|
if(isnull(new_style))
|
|
return TRUE
|
|
if(HAS_TRAIT(hairdresser, TRAIT_BALD))
|
|
to_chat(hairdresser, span_notice("If only growing back hair were that easy for you... The reminder makes you feel terrible."))
|
|
hairdresser.add_mood_event("bald_hair_day", /datum/mood_event/bald_reminder)
|
|
return TRUE
|
|
|
|
hairdresser.set_hairstyle(new_style, update = TRUE)
|
|
|
|
/obj/structure/mirror/proc/change_name(mob/living/carbon/human/user)
|
|
var/newname = sanitize_name(tgui_input_text(user, "Who are we again?", "Name change", user.name, MAX_NAME_LEN), allow_numbers = TRUE) //It's magic so whatever.
|
|
if(!newname)
|
|
return TRUE
|
|
user.real_name = newname
|
|
user.name = newname
|
|
if(user.dna)
|
|
user.dna.real_name = newname
|
|
if(user.mind)
|
|
user.mind.name = newname
|
|
|
|
// Erm ackshually the proper term is species. Get it right??
|
|
/obj/structure/mirror/proc/change_race(mob/living/carbon/human/race_changer)
|
|
var/racechoice = tgui_input_list(race_changer, "What are we again?", "Race change", selectable_races)
|
|
if(isnull(racechoice))
|
|
return TRUE
|
|
|
|
var/new_race_path = selectable_races[racechoice]
|
|
if(!ispath(new_race_path, /datum/species))
|
|
return TRUE
|
|
|
|
var/datum/species/newrace = new new_race_path()
|
|
var/attributes_desc = newrace.get_physical_attributes()
|
|
|
|
var/answer = tgui_alert(race_changer, attributes_desc, "Become a [newrace]?", list("Yes", "No"))
|
|
if(answer != "Yes")
|
|
qdel(newrace)
|
|
change_race(race_changer) // try again
|
|
return
|
|
|
|
race_changer.set_species(newrace, icon_update = FALSE)
|
|
if(HAS_TRAIT(race_changer, TRAIT_USES_SKINTONES))
|
|
var/new_s_tone = tgui_input_list(race_changer, "Choose your skin tone", "Race change", GLOB.skin_tones)
|
|
if(new_s_tone)
|
|
race_changer.skin_tone = new_s_tone
|
|
race_changer.dna.update_ui_block(DNA_SKIN_TONE_BLOCK)
|
|
else if(HAS_TRAIT(race_changer, TRAIT_MUTANT_COLORS) && !HAS_TRAIT(race_changer, TRAIT_FIXED_MUTANT_COLORS))
|
|
var/new_mutantcolor = input(race_changer, "Choose your skin color:", "Race change", race_changer.dna.features["mcolor"]) as color|null
|
|
if(new_mutantcolor)
|
|
var/list/mutant_hsv = rgb2hsv(new_mutantcolor)
|
|
|
|
if(mutant_hsv[3] >= 50) // mutantcolors must be bright
|
|
race_changer.dna.features["mcolor"] = sanitize_hexcolor(new_mutantcolor)
|
|
race_changer.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK)
|
|
else
|
|
to_chat(race_changer, span_notice("Invalid color. Your color is not bright enough."))
|
|
return TRUE
|
|
|
|
race_changer.update_body(is_creating = TRUE)
|
|
race_changer.update_mutations_overlay() // no hulk lizard
|
|
|
|
// possible Genders: MALE, FEMALE, PLURAL, NEUTER
|
|
// possible Physique: MALE, FEMALE
|
|
// saved you a click (many)
|
|
/obj/structure/mirror/proc/change_sex(mob/living/carbon/human/sexy)
|
|
|
|
var/chosen_sex = tgui_input_list(sexy, "Become a..", "Confirmation", list("Warlock", "Witch", "Wizard", "Itzard")) // YOU try coming up with the 'it' version of wizard
|
|
|
|
switch(chosen_sex)
|
|
if("Warlock")
|
|
sexy.gender = MALE
|
|
to_chat(sexy, span_notice("Man, you feel like a man!"))
|
|
if("Witch")
|
|
sexy.gender = FEMALE
|
|
to_chat(sexy, span_notice("Man, you feel like a woman!"))
|
|
if("Wizard")
|
|
sexy.gender = PLURAL
|
|
to_chat(sexy, span_notice("Woah dude, you feel like a dude!"))
|
|
if("Itzard")
|
|
sexy.gender = NEUTER
|
|
to_chat(sexy, span_notice("Woah dude, you feel like something else!"))
|
|
|
|
var/chosen_physique = tgui_input_list(sexy, "Alter your physique as well?", "Confirmation", list("Warlock Physique", "Witch Physique", "Wizards Don't Need Gender"))
|
|
|
|
if(chosen_physique && chosen_physique != "Wizards Don't Need Gender")
|
|
sexy.physique = (chosen_physique == "Warlock Physique") ? MALE : FEMALE
|
|
|
|
sexy.dna.update_ui_block(DNA_GENDER_BLOCK)
|
|
sexy.update_body(is_creating = TRUE) // or else physique won't change properly
|
|
sexy.update_mutations_overlay() //(hulk male/female)
|
|
sexy.update_clothing(ITEM_SLOT_ICLOTHING) // update gender shaped clothing
|
|
|
|
/obj/structure/mirror/proc/change_eyes(mob/living/carbon/human/user)
|
|
var/new_eye_color = input(user, "Choose your eye color", "Eye Color", user.eye_color_left) as color|null
|
|
if(isnull(new_eye_color))
|
|
return TRUE
|
|
user.eye_color_left = sanitize_hexcolor(new_eye_color)
|
|
user.eye_color_right = sanitize_hexcolor(new_eye_color)
|
|
user.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK)
|
|
user.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
|
|
user.update_body()
|
|
to_chat(user, span_notice("You gaze at your new eyes with your new eyes. Perfect!"))
|
|
|
|
/obj/structure/mirror/examine_status(mob/living/carbon/human/user)
|
|
if(broken)
|
|
return list()// no message spam
|
|
return ..()
|
|
|
|
/obj/structure/mirror/attacked_by(obj/item/I, mob/living/user)
|
|
if(broken || !istype(user) || !I.force)
|
|
return ..()
|
|
|
|
. = ..()
|
|
if(broken) // breaking a mirror truly gets you bad luck!
|
|
to_chat(user, span_warning("A chill runs down your spine as [src] shatters..."))
|
|
user.AddComponent(/datum/component/omen, incidents_left = 7)
|
|
|
|
/obj/structure/mirror/bullet_act(obj/projectile/proj)
|
|
if(broken || !isliving(proj.firer) || !proj.damage)
|
|
return ..()
|
|
|
|
. = ..()
|
|
if(broken) // breaking a mirror truly gets you bad luck!
|
|
var/mob/living/unlucky_dude = proj.firer
|
|
to_chat(unlucky_dude, span_warning("A chill runs down your spine as [src] shatters..."))
|
|
unlucky_dude.AddComponent(/datum/component/omen, incidents_left = 7)
|
|
|
|
/obj/structure/mirror/atom_break(damage_flag, mapload)
|
|
. = ..()
|
|
if(broken)
|
|
return
|
|
icon_state = "mirror_broke"
|
|
if(!mapload)
|
|
playsound(src, SFX_SHATTER, 70, TRUE)
|
|
if(desc == initial(desc))
|
|
desc = "Oh no, seven years of bad luck!"
|
|
broken = TRUE
|
|
|
|
/obj/structure/mirror/atom_deconstruct(disassembled = TRUE)
|
|
if(!disassembled)
|
|
new /obj/item/shard(loc)
|
|
else
|
|
new /obj/item/wallframe/mirror(loc)
|
|
|
|
/obj/structure/mirror/welder_act(mob/living/user, obj/item/I)
|
|
..()
|
|
if(user.combat_mode)
|
|
return FALSE
|
|
|
|
if(!broken)
|
|
return TRUE
|
|
|
|
if(!I.tool_start_check(user, amount=1))
|
|
return TRUE
|
|
|
|
balloon_alert(user, "repairing...")
|
|
if(I.use_tool(src, user, 10, volume = 50))
|
|
balloon_alert(user, "repaired")
|
|
broken = FALSE
|
|
icon_state = initial(icon_state)
|
|
desc = initial(desc)
|
|
|
|
return TRUE
|
|
|
|
/obj/structure/mirror/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
|
switch(damage_type)
|
|
if(BRUTE)
|
|
playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE)
|
|
if(BURN)
|
|
playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE)
|
|
|
|
/obj/item/wallframe/mirror
|
|
name = "mirror"
|
|
desc = "An unmounted mirror. Attach it to a wall to use."
|
|
icon = 'icons/obj/watercloset.dmi'
|
|
icon_state = "mirror"
|
|
custom_materials = list(
|
|
/datum/material/glass = SHEET_MATERIAL_AMOUNT,
|
|
/datum/material/silver = SHEET_MATERIAL_AMOUNT,
|
|
)
|
|
result_path = /obj/structure/mirror
|
|
pixel_shift = 28
|
|
|
|
/obj/structure/mirror/magic
|
|
name = "magic mirror"
|
|
desc = "Turn and face the strange... face."
|
|
icon_state = "magic_mirror"
|
|
mirror_options = MAGIC_MIRROR_OPTIONS
|
|
|
|
/obj/structure/mirror/magic/Initialize(mapload)
|
|
. = ..()
|
|
|
|
if(length(selectable_races))
|
|
return
|
|
for(var/datum/species/species_type as anything in subtypesof(/datum/species))
|
|
if(initial(species_type.changesource_flags) & race_flags)
|
|
selectable_races[initial(species_type.name)] = species_type
|
|
selectable_races = sort_list(selectable_races)
|
|
|
|
/obj/structure/mirror/magic/change_beard(mob/living/carbon/human/beard_dresser) // magical mirrors do nothing but give you the damn beard
|
|
var/new_style = tgui_input_list(beard_dresser, "Select a facial hairstyle", "Grooming", SSaccessories.facial_hairstyles_list)
|
|
if(isnull(new_style))
|
|
return TRUE
|
|
beard_dresser.set_facial_hairstyle(new_style, update = TRUE)
|
|
return TRUE
|
|
|
|
//Magic mirrors can change hair color as well
|
|
/obj/structure/mirror/magic/change_hair(mob/living/carbon/human/user)
|
|
var/hairchoice = tgui_alert(user, "Hairstyle or hair color?", "Change Hair", list("Style", "Color"))
|
|
if(hairchoice == "Style") //So you just want to use a mirror then?
|
|
return ..()
|
|
|
|
var/new_hair_color = input(user, "Choose your hair color", "Hair Color", user.hair_color) as color|null
|
|
|
|
if(new_hair_color)
|
|
user.set_haircolor(sanitize_hexcolor(new_hair_color))
|
|
user.dna.update_ui_block(DNA_HAIR_COLOR_BLOCK)
|
|
if(user.physique == MALE)
|
|
var/new_face_color = input(user, "Choose your facial hair color", "Hair Color", user.facial_hair_color) as color|null
|
|
if(new_face_color)
|
|
user.set_facial_haircolor(sanitize_hexcolor(new_face_color))
|
|
user.dna.update_ui_block(DNA_FACIAL_HAIR_COLOR_BLOCK)
|
|
|
|
/obj/structure/mirror/magic/attack_hand(mob/living/carbon/human/user)
|
|
. = ..()
|
|
if(.)
|
|
return TRUE
|
|
|
|
if(HAS_TRAIT(user, TRAIT_ADVANCEDTOOLUSER) && HAS_TRAIT(user, TRAIT_LITERATE))
|
|
return TRUE
|
|
|
|
to_chat(user, span_alert("You feel quite intelligent."))
|
|
// Prevents wizards from being soft locked out of everything
|
|
// If this stays after the species was changed once more, well, the magic mirror did it. It's magic i aint gotta explain shit
|
|
user.add_traits(list(TRAIT_LITERATE, TRAIT_ADVANCEDTOOLUSER), SPECIES_TRAIT)
|
|
return TRUE
|
|
|
|
/obj/structure/mirror/magic/lesser/Initialize(mapload)
|
|
// Roundstart species don't have a flag, so it has to be set on Initialize.
|
|
selectable_races = get_selectable_species().Copy()
|
|
return ..()
|
|
|
|
/obj/structure/mirror/magic/badmin
|
|
race_flags = MIRROR_BADMIN
|
|
|
|
/obj/structure/mirror/magic/pride
|
|
name = "pride's mirror"
|
|
desc = "Pride cometh before the..."
|
|
race_flags = MIRROR_PRIDE
|
|
mirror_options = PRIDE_MIRROR_OPTIONS
|
|
/// If the last user has altered anything about themselves
|
|
var/changed = FALSE
|
|
|
|
/obj/structure/mirror/magic/pride/display_radial_menu(mob/living/carbon/human/user)
|
|
var/pick = show_radial_menu(user, src, mirror_options, user, radius = 36, require_near = TRUE, tooltips = TRUE)
|
|
if(!pick)
|
|
return TRUE //get out
|
|
|
|
changed = TRUE
|
|
switch(pick)
|
|
if(CHANGE_HAIR)
|
|
change_hair(user)
|
|
if(CHANGE_BEARD)
|
|
change_beard(user)
|
|
if(CHANGE_RACE)
|
|
change_race(user)
|
|
if(CHANGE_SEX) // sex: yes
|
|
change_sex(user)
|
|
if(CHANGE_NAME)
|
|
change_name(user)
|
|
if(CHANGE_EYES)
|
|
change_eyes(user)
|
|
|
|
return display_radial_menu(user)
|
|
|
|
/obj/structure/mirror/magic/pride/attack_hand(mob/living/carbon/human/user)
|
|
changed = FALSE
|
|
. = ..()
|
|
if (!changed)
|
|
return
|
|
user.visible_message(
|
|
span_bolddanger("The ground splits beneath [user] as [user.p_their()] hand leaves the mirror!"),
|
|
span_notice("Perfect. Much better! Now <i>nobody</i> will be able to resist yo-"),
|
|
)
|
|
|
|
var/turf/user_turf = get_turf(user)
|
|
var/list/levels = SSmapping.levels_by_trait(ZTRAIT_SPACE_RUINS)
|
|
var/turf/dest
|
|
if(length(levels))
|
|
dest = locate(user_turf.x, user_turf.y, pick(levels))
|
|
|
|
user_turf.ChangeTurf(/turf/open/chasm, flags = CHANGETURF_INHERIT_AIR)
|
|
var/turf/open/chasm/new_chasm = user_turf
|
|
new_chasm.set_target(dest)
|
|
new_chasm.drop(user)
|
|
|
|
#undef CHANGE_HAIR
|
|
#undef CHANGE_BEARD
|
|
|
|
#undef CHANGE_RACE
|
|
#undef CHANGE_SEX
|
|
#undef CHANGE_NAME
|
|
#undef CHANGE_EYES
|
|
|
|
#undef INERT_MIRROR_OPTIONS
|
|
#undef PRIDE_MIRROR_OPTIONS
|
|
#undef MAGIC_MIRROR_OPTIONS
|