Files
de1f01fe76 General maintenance for hand held camera (#95295)
## About The Pull Request
- Merged a lot of procs into procs `attempt_picture()` and
`interact_with_atom()` reducing proc overhead
- Taking pictures will now always use the cameras internal
`picture_size_x` & `picture_size_y`. It no longer accepts variable sizes
as parameters
- All camera operations are asynchronous and respects checks on if the
target can be captured on camera or not.
- A bunch of other code rearrangement that makes readability easier
- Fixes the following camera bugs. They fall under the same category
  - Fixes #95286
  - Fixes #95256

## Changelog
🆑
fix: camera devices work again, camera flash turns on in most instances
refactor: camera code has been refactored. Report bugs on github
/🆑

---------

Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
2026-04-04 12:41:39 -04:00

210 lines
7.2 KiB
Plaintext

/*
Slimecrossing Items
General items added by the slimecrossing system.
Collected here for clarity.
*/
//Rewind camera - I'm already Burning Sepia
/obj/item/camera/rewind
name = "sepia-tinted camera"
desc = "They say a picture is like a moment stopped in time."
pictures_left = 1
pictures_max = 1
can_customise = FALSE
default_picture_name = "A nostalgic picture"
/datum/saved_bodypart
var/obj/item/bodypart/old_part
var/bodypart_type
var/brute_dam
var/burn_dam
/datum/saved_bodypart/New(obj/item/bodypart/part)
old_part = part
bodypart_type = part.type
brute_dam = part.brute_dam
burn_dam = part.burn_dam
/mob/living/carbon/proc/apply_saved_bodyparts(list/datum/saved_bodypart/parts)
var/list/dont_chop = list()
for(var/zone in parts)
var/datum/saved_bodypart/saved_part = parts[zone]
var/obj/item/bodypart/already = get_bodypart(zone)
if(QDELETED(saved_part.old_part))
saved_part.old_part = new saved_part.bodypart_type
if(!already || already != saved_part.old_part)
saved_part.old_part.replace_limb(src)
saved_part.old_part.heal_damage(INFINITY, INFINITY, null, FALSE)
saved_part.old_part.receive_damage(saved_part.brute_dam, saved_part.burn_dam, wound_bonus=CANT_WOUND)
dont_chop[zone] = TRUE
/mob/living/carbon/proc/save_bodyparts()
var/list/datum/saved_bodypart/ret = list()
for(var/obj/item/bodypart/part as anything in get_bodyparts(include_stumps = TRUE))
var/datum/saved_bodypart/saved_part = new(part)
ret[part.body_zone] = saved_part
return ret
/obj/item/camera/rewind/on_flash(atom/target, mob/user)
. = ..()
if(user == target)
to_chat(user, span_notice("You take a selfie!"))
else
to_chat(user, span_notice("You take a photo with [target]!"))
to_chat(target, span_notice("[user] takes a photo with you!"))
to_chat(target, span_boldnotice("You'll remember this moment forever!"))
target.AddComponent(/datum/component/dejavu, 2)
//Timefreeze camera - Old Burning Sepia result. Kept in case admins want to spawn it
/obj/item/camera/timefreeze
name = "sepia-tinted camera"
desc = "They say a picture is like a moment stopped in time."
pictures_left = 1
pictures_max = 1
/obj/item/camera/timefreeze/on_flash(atom/target, mob/user)
. = ..()
new /obj/effect/timestop(get_turf(target), 2, 50, list(user))
//Hypercharged slime cell - Charged Yellow
/obj/item/stock_parts/power_store/cell/high/slime_hypercharged
name = "hypercharged slime core"
desc = "A charged yellow slime extract, infused with plasma. It almost hurts to touch."
icon = 'icons/mob/simple/slimes.dmi'
icon_state = "yellow-core"
rating = 7
custom_materials = null
maxcharge = 50 * STANDARD_CELL_CHARGE
chargerate = 2.5 * STANDARD_CELL_RATE
charge_light_type = null
connector_type = "slimecore"
//Barrier cube - Chilling Grey
/obj/item/barriercube
name = "barrier cube"
desc = "A compressed cube of slime. When squeezed, it grows to massive size!"
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "barriercube"
w_class = WEIGHT_CLASS_TINY
/obj/item/barriercube/attack_self(mob/user)
if(locate(/obj/structure/barricade/slime) in get_turf(loc))
to_chat(user, span_warning("You can't fit more than one barrier in the same space!"))
return
to_chat(user, span_notice("You squeeze [src]."))
var/obj/B = new /obj/structure/barricade/slime(get_turf(loc))
B.visible_message(span_warning("[src] suddenly grows into a large, gelatinous barrier!"))
qdel(src)
//Slime barricade - Chilling Grey
/obj/structure/barricade/slime
name = "gelatinous barrier"
desc = "A huge chunk of grey slime. Bullets might get stuck in it."
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "slimebarrier"
proj_pass_rate = 40
max_integrity = 60
//Melting Gel Wall - Chilling Metal
/obj/effect/forcefield/slimewall
name = "solidified gel"
desc = "A mass of solidified slime gel - completely impenetrable, but it's melting away!"
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "slimebarrier_thick"
can_atmos_pass = ATMOS_PASS_NO
opacity = TRUE
initial_duration = 10 SECONDS
//Rainbow barrier - Chilling Rainbow
/obj/effect/forcefield/slimewall/rainbow
name = "rainbow barrier"
desc = "Despite others' urgings, you probably shouldn't taste this."
icon_state = "rainbowbarrier"
//Ice stasis block - Chilling Dark Blue
/obj/structure/ice_stasis
name = "ice block"
desc = "A massive block of ice. You can see something vaguely humanoid inside."
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "frozen"
density = TRUE
max_integrity = 100
armor_type = /datum/armor/structure_ice_stasis
/datum/armor/structure_ice_stasis
melee = 30
bullet = 50
laser = -50
energy = -50
fire = -80
acid = 30
/obj/structure/ice_stasis/Initialize(mapload)
. = ..()
playsound(src, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE)
/obj/structure/ice_stasis/Destroy()
for(var/atom/movable/M in contents)
M.forceMove(loc)
playsound(src, 'sound/effects/glass/glassbr3.ogg', 50, TRUE)
return ..()
//Gold capture device - Chilling Gold
/obj/item/capturedevice
name = "gold capture device"
desc = "Bluespace technology packed into a roughly egg-shaped device, used to store nonhuman creatures. Can't catch them all, though - it only fits one."
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "capturedevice"
///traits we give and remove from the mob on exit and entry
var/static/list/traits_on_transfer = list(
TRAIT_IMMOBILIZED,
TRAIT_HANDS_BLOCKED,
TRAIT_AI_PAUSED,
)
/obj/item/capturedevice/attack(mob/living/pokemon, mob/user)
if(length(contents))
to_chat(user, span_warning("The device already has something inside."))
return
if(!isanimal_or_basicmob(pokemon))
to_chat(user, span_warning("The capture device only works on simple creatures."))
return
if(pokemon.mind)
to_chat(user, span_notice("You offer the device to [pokemon]."))
if(tgui_alert(pokemon, "Would you like to enter [user]'s capture device?", "Gold Capture Device", list("Yes", "No")) == "Yes")
if(user.can_perform_action(src) && user.can_perform_action(pokemon))
to_chat(user, span_notice("You store [pokemon] in the capture device."))
to_chat(pokemon, span_notice("The world warps around you, and you're suddenly in an endless void, with a window to the outside floating in front of you."))
store(pokemon, user)
else
to_chat(user, span_warning("You were too far away from [pokemon]."))
to_chat(pokemon, span_warning("You were too far away from [user]."))
else
to_chat(user, span_warning("[pokemon] refused to enter the device."))
return
else if(!pokemon.has_faction(FACTION_NEUTRAL))
to_chat(user, span_warning("This creature is too aggressive to capture."))
return
to_chat(user, span_notice("You store [pokemon] in the capture device."))
store(pokemon)
/obj/item/capturedevice/attack_self(mob/user)
if(contents.len)
to_chat(user, span_notice("You open the capture device!"))
release()
else
to_chat(user, span_warning("The device is empty..."))
/obj/item/capturedevice/proc/store(mob/living/pokemon)
pokemon.forceMove(src)
pokemon.add_traits(traits_on_transfer, ABSTRACT_ITEM_TRAIT)
pokemon.cancel_camera()
/obj/item/capturedevice/proc/release()
for(var/mob/living/pokemon in contents)
pokemon.forceMove(get_turf(loc))
pokemon.remove_traits(traits_on_transfer, ABSTRACT_ITEM_TRAIT)
pokemon.cancel_camera()