[MIRROR] Makes the Voodoo doll scrungle people, various recycler fixes. (#11087)

Co-authored-by: eghughguhhhhhh <Hawoogabooga@gmail.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-06-18 22:11:09 +02:00
committed by GitHub
co-authored by eghughguhhhhhh
parent d1e9a9fbf0
commit c5988b41fc
12 changed files with 308 additions and 107 deletions
@@ -4,7 +4,7 @@
/obj/machinery/maint_recycler //fresh outta 2288 baby
name = "Decrepit Machine"
name = "\improper Decrepit Machine"
//icon etc are self contained because it's dumb that they not to begin with.
//it's not a MODULE if everything required for it to work is thrown everywhere. grr.
@@ -196,7 +196,7 @@
if(!door_open)
if(O.has_tool_quality(TOOL_CROWBAR))
if(stat & (BROKEN|NOPOWER))
to_chat(user, span_warning("you lever \The [src]'s door open!"))
to_chat(user, span_warning("you lever \the [src]'s door open!"))
open_door(user)
eject_item(user) //also kick out the item they're looking for
playsound(src,"sound/machines/door/airlock_creaking.ogg",4,FALSE)
@@ -204,11 +204,11 @@
else
to_chat(user, span_warning("\The [src]'s door won't budge!"))
else
to_chat(user, span_warning("\The [src] doesn't have it's door open!"))
to_chat(user, span_warning("\The [src] doesn't have its door open!"))
return
if(inserted_item)
to_chat(user, span_warning("\The [src] already has [inserted_item] in it's recycling compartment!"))
to_chat(user, span_warning("\The [src] already has [inserted_item] in its recycling compartment!"))
return
switch(get_item_whitelist(O))
@@ -220,7 +220,7 @@
evil_act(O,user)
return
to_chat(user, span_notice("you put \The [O] into \The [src]'s processing compartment!"))
to_chat(user, span_notice("You put \the [O] into \the [src]'s processing compartment!"))
if(istype(O,/obj/item/holder))
var/obj/item/holder/h = O
var/mob/m = h.held_mob
@@ -250,7 +250,7 @@
if(prob(75))
if(get_item_whitelist(AM) == RECYCLER_ALLOWED)
if(inserted_item == null)
visible_message("\The [AM] lands in \the [src].")
visible_message("\The [AM] lands in \the [src].",runemessage = "swish")
AM.forceMove(src)
inserted_item = AM
update_icon()
@@ -264,7 +264,7 @@
/obj/machinery/maint_recycler/proc/deny_act(var/obj/item/O,var/mob/user)
set_screen_state("screen_deny",10)
to_chat(user, span_warning("\The [src] rejects \The [O]!"))
to_chat(user, span_warning("\The [src] rejects \the [O]!"))
if(prob(99))
playsound(src, 'code/modules/maint_recycler/sfx/generaldeny.ogg', 75, 1)
return
@@ -282,7 +282,7 @@
if(!isRepeat) GLOB.global_announcer.autosay("HARM ALERT: Crewmember [user] recorded displaying murderous tendencies towards innocent creatures in [get_area(src)]. Please schedule psych evaluation and ensure the wellbeing of recorded victim: [h.held_mob]", "[src]", "Security")
audible_message("[src] states, \"AMORAL INTENT DETECTED.\" ", "\The [src]'s screen briefly flashes to an angry red graphic!" , runemessage = ">:(")
else
if(!isRepeat) GLOB.global_announcer.autosay("PROPERTY DESTRUCTION ALERT: Crewmember [user] has been recorded attempting to destroy high priority station equipment in [get_area(src)]. Please ensure the integrity of \The [O].", "[src]", "Security")
if(!isRepeat) GLOB.global_announcer.autosay("PROPERTY DESTRUCTION ALERT: Crewmember [user] has been recorded attempting to destroy high priority station equipment in [get_area(src)]. Please ensure the integrity of \the [O].", "[src]", "Security")
audible_message("[src] states, \"CRIMINAL INTENT DETECTED.\" ", "\The [src]'s screen briefly flashes to an angry red graphic!" , runemessage = ">:(")
playsound(src,pick(angry_sounds),80)
@@ -344,7 +344,7 @@
/obj/machinery/maint_recycler/proc/eject_item_act(var/mob/user)
inserted_item.forceMove(get_turf(src))
visible_message(span_warning("[src] ejects \The [inserted_item] from it's recycling chamber!"))
visible_message(span_warning("[src] ejects \the [inserted_item] from its recycling chamber!"))
inserted_item.throw_at(get_step(src,SOUTH),5,1,src)
inserted_item = null;
update_icon()
@@ -389,7 +389,7 @@
//todo, assoc list for icon states from type
if(istype(inserted_item,/mob/living/simple_mob/animal/passive/mouse))
item_overlay.icon = src.icon
item_overlay.icon_state = "hepme" //the creature deserves it's horrible end
item_overlay.icon_state = "hepme" //the creature deserves its horrible end
item_overlay.vis_flags = VIS_INHERIT_ID //gotta reapply
item_overlay.appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PASS_MOUSE
@@ -474,6 +474,7 @@ TGUI PROCS
/obj/machinery/maint_recycler/tgui_close(mob/user)
. = ..()
if(LAZYLEN(open_tguis) > 0) return
set_on_state(FALSE)
@@ -484,7 +485,7 @@ UTILITY PROCS
/obj/machinery/maint_recycler/proc/credit_user(var/mob/user, var/amount)
if(!user || !user.client || !user.client.prefs) return
var/currentValue = user.client?.prefs?.read_preference(/datum/preference/numeric/recycler_points)
user.client?.prefs?.write_preference_by_type(/datum/preference/numeric/recycler_points, currentValue + amount)
user.client?.prefs?.write_preference_by_type(/datum/preference/numeric/recycler_points, min(currentValue + amount,999))
if(granted_points[user.key])
granted_points[user.key] += amount
else
@@ -496,13 +497,13 @@ UTILITY PROCS
/obj/machinery/maint_recycler/proc/canRecycle(var/mob/user, var/potentialValue)
if(!user.key) return FALSE
if(granted_points[user.key]+potentialValue > point_cap) return FALSE
if(granted_points[user.key]+potentialValue > point_cap) return FALSE
return TRUE
/obj/machinery/maint_recycler/proc/mob_consent_check(var/mob/probable_victim)
if(probable_victim.key)
if(probable_victim.client) //sanity check to make sure they are alright with getting squished to death
return (tgui_alert(usr,"Do you want to be put in \The [src]? Industrial machinery is pretty damn deadly, you'll probably die. to death. A fine paste.", "Welcome to the Hydralulic Press Prompt", list("OSHA is for chumps", "what the fuck? get me outta here!")) == "OSHA is for chumps")
return (tgui_alert(probable_victim,"Do you want to be put in \The [src]? Industrial machinery is pretty damn deadly, you'll probably die. to death. A fine paste.", "Welcome to the Hydralulic Press Prompt", list("OSHA is for chumps", "what the fuck? get me outta here!")) == "OSHA is for chumps")
else return FALSE //no logged out users
else return TRUE //mindless mobs that've never felt the gentle touch of a client are fine
@@ -1,5 +1,5 @@
/obj/machinery/maint_vendor
name = "Large Decrepit Machine"
name = "\improper Large Decrepit Machine"
desc = "A long since abandoned \"trash 4 cash\" rewards kiosk. Now featuring a state of the art, monochrome holographic tube display!"
icon = 'code/modules/maint_recycler/icons/maint_vendor.dmi'
icon_state = "default"
@@ -117,7 +117,7 @@
playsound(src, 'code/modules/maint_recycler/sfx/ejectgoodies.ogg', 75, 1)
used_entry.spawn_with_delay(src);
audible_message("[src] states, \"[used_entry.tagline]\" ", "\The [src]'s screen briefly flashes an $!" , runemessage = "$$$")
audible_message("[src] states, \"[used_entry.tagline]\" ", "\The [src]'s screen briefly flashes a $!" , runemessage = "$$$")
if(prob(95))
set_screen_state("screen_cashout",10)
else
@@ -187,6 +187,7 @@
/obj/machinery/maint_vendor/tgui_close(mob/user)
. = ..()
if(LAZYLEN(open_tguis) > 0) return
set_on_state(FALSE)
/obj/machinery/maint_vendor/tgui_data(mob/user)
@@ -202,7 +203,7 @@
"desc" = entry.desc,
"ad" = entry.ad_message,
"icon" = entry.icon_state,
"index" = i //TODO
"index" = i
))
data["items"] = items
data["userBalance"] = user_balance(user)
@@ -21,6 +21,16 @@
object_type_to_spawn = /obj/item/stack/material/wood/hard{amount = 20}
/datum/maint_recycler_vendor_entry/cardboard
vendor_category = MAINTVENDOR_CONSTRUCTION
name = "C.A.R.D.B.O.A.R.D"
ad_message = "5 sheets!"
desc = "Not an acronym (maybe?), we just asked the person in charge of designating stationbounds to make this listing, we have no idea what it means!"
object_type_to_spawn = /obj/item/stack/material/cardboard{amount = 5}
item_cost = 10 //not too pricy
per_person_cap = 1
per_round_cap = 3 //limited supply!
/datum/maint_recycler_vendor_entry/DIY_Steel
vendor_category = MAINTVENDOR_CONSTRUCTION
name = "steel!"
@@ -21,6 +21,7 @@
tagline = "I HAVE 8.2 PETABYTES OF MECHHAMMER MODELS!"
item_cost = 50 //even discount bin warhammer's comically expensive
object_type_to_spawn = /obj/random/fromList/mecha_toys
icon_state = "copyright"
/datum/maint_recycler_vendor_entry/burger //packaged 40 years ago. you should regret this.
name = "TRASH 4 BURGER REDEMPTION"
@@ -104,7 +104,7 @@
/datum/maint_recycler_vendor_entry/NIF_laws
name = "COMM4ND UR N1F!"
ad_message = "KNEEL!"
icon_state = "law"
icon_state = "laws"
desc = "Unlocks the TRUE potential of your NIF! You set laws, you follow laws! Simple as! (we are not liable for usage on other parties)"
object_type_to_spawn = /obj/item/disk/nifsoft/compliance
tagline = "You didn't get it from us!"
@@ -113,7 +113,7 @@
/datum/maint_recycler_vendor_entry/NIFlet_laws
name = "Implnt law?"
ad_message = "Niflets seething!"
icon_state = "law"
icon_state = "laws"
desc = "Underlings not listening to you? Someone forgetting to do whta you tell them to? No more! this implant WILL make people bend to your will! KNEEL!"
object_type_to_spawn = /obj/item/implanter/compliance
item_cost = 25
@@ -10,18 +10,6 @@
required_access = list(access_security)
vendor_category = MAINTVENDOR_WEAPONS
/datum/maint_recycler_vendor_entry/foamforce_ammo //fun!
name = "MUNITIONS FOR GUN"
ad_message = "FOAM FLINGING PAIN!"
desc = "BIG BOX? BIG AMMO. THAT'S RIGHT. WE'LL BIG YOUR AMMO. FOAM DARTS, FOR FOAM DART GUNS - LIMITED EDITION FRAG MONTAGE BUY NOW"
object_type_to_spawn = /obj/item/toy/russian_revolver/trick_revolver
item_cost = 15 //cheap!
tagline = "to war!"
per_person_cap = 1
per_round_cap = 3 //limited supply!
required_access = list(access_security)
vendor_category = MAINTVENDOR_WEAPONS
/datum/maint_recycler_vendor_entry/foamforce_ammo //fun!
name = "MUNITIONS FOR GUN"
ad_message = "FOAM FLINGING PAIN!"
@@ -0,0 +1,19 @@
//todo, figure out a better place for this
/proc/get_humanoid_displacement_map_image(var/mob/living/carbon/human/target,var/list/discarded_layer_indicies, var/icon/displacement_map, var/outlineWidth = 0, var/render_target = null)
var/list/to_overlays = target.overlays_standing.Copy()
for(var/layer_to_kill in discarded_layer_indicies)
to_overlays[layer_to_kill] = null
var/image/displacement_render_image = image(icon = null, icon_state = "empty", dir = SOUTH)
displacement_render_image.appearance_flags = KEEP_TOGETHER
displacement_render_image.overlays = to_overlays
displacement_render_image.filters += filter(type = "displace", size = 128, icon=displacement_map) //size is 128 because that's the god given standard. if you're doing something unhinged enough to need a bigger or smaller size, by all means.
if(outlineWidth)
displacement_render_image.filters += filter(type="outline", size = outlineWidth)
if(render_target)
displacement_render_image.render_target = render_target
return displacement_render_image
@@ -1,6 +1,13 @@
//remote stuff! like uhhhh
// the funny stickers
// the funny voodoo doll
/*
Remote scene tools! These, in effects, serve 2 purposes:
* Forward emotes from wearer of one to wearer of its partner
* Provide simple feedback - is your partner dead, SSD, did they log out? - some are more explicit.
why aren't these accessories?
They easily could be! I'm just lazy and don't wanna forward the "see emote" from wearables to all the accessories
*/
/obj/item/remote_scene_tool
@@ -12,11 +19,15 @@
icon_state = "sticker_inactive"
name = "Bluespace Sticker"
desc = "A stretchable, flexible sticker that induces a quasi-stable 4th dimensional bluespace buzzword rift, enabling moderate levels of touch between the two."
description_info = "These stickers act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other sticker! It's vauge, so use it how you want in RP! Just remember bystander consent!"
description_info = "These stickers act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other sticker! It's vague, so use it how you want in RP! Just remember bystander consent!"
slot_flags = (SLOT_OCLOTHING | SLOT_ICLOTHING | SLOT_GLOVES | SLOT_MASK | SLOT_HEAD | SLOT_FEET | SLOT_ID | SLOT_BELT | SLOT_BACK | SLOT_POCKET)
w_class = ITEMSIZE_SMALL
var/mob/worn_mob = null
var/last_loc
var/can_summon = TRUE
var/can_replace = TRUE
var/replacementType = /obj/item/remote_scene_tool
/obj/item/remote_scene_tool/proc/link_to(var/obj/item/remote_scene_tool/to_link)
//link to a remote scene tool
@@ -37,43 +48,43 @@
RegisterSignal(mob, COMSIG_MOB_LOGIN, PROC_REF(worn_mob_logged_in))
RegisterSignal(mob, COMSIG_MOB_LOGOUT, PROC_REF(worn_mob_logged_out))
transmit_emote(src, span_notice("The [src]\'s has been put on by [mob]!"))
transmit_emote(src, span_notice("\The [src] has been put on by [mob]!"))
/obj/item/remote_scene_tool/proc/unregister_from_mob(var/mob)
if(worn_mob == null) return
UnregisterSignal(worn_mob, COMSIG_MOB_LOGIN)
UnregisterSignal(worn_mob, COMSIG_MOB_LOGOUT)
worn_mob = null
transmit_emote(src, span_warning("The [src]\'s wearer has removed it!"))
transmit_emote(src, span_warning("\The [src]'s wearer has removed it!"))
//called when the mob wearing this item logs out
/obj/item/remote_scene_tool/proc/worn_mob_logged_out()
SIGNAL_HANDLER
if(!linked)
return
transmit_emote(src, span_warning("The [src]\'s wearer has gone SSD!"))
transmit_emote(src, span_warning("\The [src]'s wearer has gone SSD!"))
linked?.linked_updated()
//called when the mob wearing this item logs out
/obj/item/remote_scene_tool/proc/worn_mob_logged_in()
SIGNAL_HANDLER
//called when the mob wearing this item logs in
if(!linked)
return
transmit_emote(src, "The [src]\'s wearer has returned from SSD!")
transmit_emote(src, "\The [src]'s wearer has returned from SSD!")
linked?.linked_updated()
/obj/item/remote_scene_tool/see_emote(var/mob/M as mob, var/text, var/emote_type)
//to_world_log("emote: [text] from [M] to [linked]")
if(M == loc)
if(M == getWearer())
//we're the one doing the emote
transmit_emote(src, text,emote_type)
/obj/item/remote_scene_tool/proc/transmit_emote(var/mob/M as mob, var/text, var/emote_type)
if(ismob(linked.loc))
var/mob/m = linked.loc
if(linked == null) return
if(ismob(linked.getWearer()))
var/mob/m = linked.getWearer()
if(!m.client) return;
to_chat(linked.loc, icon2html(src,m.client) + text)
@@ -87,9 +98,10 @@
return FALSE
if(linked.linked != src)
return FALSE
if(!ismob(linked.loc))
var/mob/m = linked.getWearer()
if(!ismob(m))
return FALSE
var/mob/m = linked.loc
if(!m.client) //no scene partner? whoopsie!
return FALSE
@@ -104,18 +116,21 @@
/obj/item/remote_scene_tool/proc/delayed_loc_check()
//why is this delayed? because when moving stuff between slots, it considers it in a different spot, and calls the commsig multiple times - so the end
//user just gets the shit spammed out of them
if(last_loc == loc)
return
last_loc = loc
linked?.linked_updated()
linked_updated()
if(ismob(loc))
register_to_mob(loc) //handles any caching
var/mob/m = getWearer()
if(ismob(m))
register_to_mob(m) //handles any caching
else
if(worn_mob)
unregister_from_mob(worn_mob) //unregister from the mob if we aren't on it anymore
/obj/item/remote_scene_tool/proc/linked_updated()
update_icon()
@@ -136,13 +151,18 @@
/obj/item/remote_scene_tool/examine(mob/user)
. = ..()
if(linked && ismob(linked.loc))
. += span_notice("This is linked to [linked.loc]\'s [linked.name].")
var/mob/m = linked.loc
if(!m.client)
. += span_warning("The wearer of \The [src]'s counterpart doesn't appear to be all there!")
var/mob/living/carbon/human/lw = linked?.getWearer()
if(lw)
. += span_notice("This is linked to [lw]'s [linked.name].")
if(!lw.client || lw.stat == UNCONSCIOUS || lw.stat == DEAD)
. += span_warning("The wearer of \the [src]'s counterpart doesn't appear to be conscious!")
else
. += span_notice("Its counterpart seems to be in \the [get_area(linked).name]")
if(!linked)
. += span_warning("This is not linked to anything!")
if(!ismob(linked.loc))
. += span_warning("\The [src]'s counterpart isn't being worn or carried by anyone!")
@@ -168,56 +188,32 @@
contents += RST2
calibrate_size()
/obj/item/remote_scene_tool/voodoo_doll
name = "voodoo doll"
desc = "A dubiously-cursed-esq Voodoo doll that mimics whoever is wearing it's matching necklace via built in hardlight projection."
icon = 'code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi'
icon_state = "voodoo_doll_inactive"
icon_root = "voodoo_doll"
description_info = "The Doll acts as a remote scene tool - any sort of emotes or subtles that the wearer does will go DIRECTLY to the necklace! It's vauge, so use it how you want in RP! Just remember bystander consent!"
//this is just a copy of the remote scene tool, but with a different icon
//and a different name
/obj/item/remote_scene_tool/proc/getWearer()
if(ismob(loc))
return loc
if(istype(loc,/obj/item/clothing))
if(ismob(loc.loc))
return loc.loc
return null
var/last_loc_update = null
/obj/item/remote_scene_tool/verb/summon_counterpart()
set name = "Summon Counterpart"
set desc = "Forcibly moves the linked object over to you - or, if it doesn't exist, spawn a new one."
if(can_summon || (linked == null && can_replace))
if(linked == null)
create_counterpart()
to_chat(usr,span_notice("\The [src] forms a new [linked]!"))
else
var/mob/counterpart = linked.getWearer()
if(counterpart)
counterpart.remove_from_mob(linked,get_turf(src))
else
linked.forceMove(get_turf(src))
to_chat(usr,span_notice("\The [linked] materializes in front of you!"))
else
to_chat(usr,span_notice("Nothing seems to happen!"))
/obj/item/remote_scene_tool/voodoo_doll/update_icon()
. = ..()
last_loc_update = linked.loc
overlays.Cut()
dir = SOUTH
src.alpha = 255
name = initial(name)
desc = initial(desc)
if(!linked)
return
if(!ismob(linked.loc))
return
if(!ismob(loc)) //only show the person's icon if we're holding it. makes "found in the hall" interactions less weird
return
var/mob/M = linked.loc
if(ismob(M))
src.alpha = 190 //make it a bit transparent so it doesn't look like a mob holder.
name = "Voodoo doll of " + M.name
desc = "A small, marketable doll that looks suspiciously like [M]... "
overlays |= M.overlays
/obj/item/remote_scene_tool/voodoo_necklace
name = "Gem-Encruseted Necklace"
desc = "A bitingly cold metal necklace with a swirling gem in the center. haters will say it's not magical"
icon_state = "necklace_inactive"
icon_root = "necklace"
slot_flags = SLOT_MASK
description_info = "The necklace and the associated doll act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other! It's vauge, so use it how you want in RP! Just remember bystander consent!"
/obj/item/storage/box/remote_scene_tools/voodoo
icon_state = "voodoobox"
name = "Voodoo Supplies Box"
desc = "a horribly dusty box from 'magici-corp' with a ludicrously corny image of a voodoo doll on the front. marketed as part of a DIY magician routine, they stopped making them after someone broke their nose when the doll was dropped."
primary = /obj/item/remote_scene_tool/voodoo_doll
secondary = /obj/item/remote_scene_tool/voodoo_necklace
/obj/item/remote_scene_tool/proc/create_counterpart()
var/obj/item/remote_scene_tool/newrst = new replacementType(get_turf(src))
link_to(newrst)
newrst.link_to(src)
@@ -0,0 +1,183 @@
#define DATA_SCALE "scale"
#define DATA_X_OFFSET "x"
#define DATA_Y_OFFSET "y"
#define DATA_ROTATION "rotation"
/obj/item/remote_scene_tool/voodoo_doll
name = "voodoo doll"
desc = "A dubiously cursed-esq Voodoo doll that mimics whoever is wearing its matching necklace via built in hardlight projection."
icon = 'code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi'
icon_state = "voodoo_doll_inactive"
icon_root = "voodoo_doll"
description_info = "The Doll acts as a remote scene tool - any sort of emotes or subtles that the wearer does will go DIRECTLY to the necklace! It's vague, so use it how you want in RP! Just remember bystander consent!"
replacementType = /obj/item/remote_scene_tool/voodoo_necklace
//this is just a copy of the remote scene tool, but with a different icon
//and a different name
var/underlayRotation = 45
//TODO: make this generic so it's easier for other stuff to use. ideally there's a magical thing that just has all the various greebles set up so u can pick and choose which ones are rendered traditionally
var/list/tail_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = -2,DATA_SCALE = 1.6, DATA_ROTATION = 45)
var/list/taur_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = 2, DATA_SCALE = 1.1, DATA_ROTATION = 45)
var/list/wing_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = -2,DATA_SCALE = 1, DATA_ROTATION = 45)
var/list/ear_data = list( DATA_X_OFFSET = 0, DATA_Y_OFFSET = 1, DATA_SCALE = 1.5, DATA_ROTATION = 45)
var/list/hat_data = list( DATA_X_OFFSET = 0, DATA_Y_OFFSET = 3, DATA_SCALE = 1, DATA_ROTATION = 45)
var/outline_size = 1
var/list/discarded_layer_indicies = list(
VORE_BELLY_LAYER, //not even gonna try with this lmfao
TAIL_LOWER_LAYER,
TAIL_UPPER_LAYER,
TAIL_UPPER_LAYER_LOW,
TAIL_UPPER_LAYER_HIGH, //drawn manually
FIRE_LAYER, //not relevant
TARGETED_LAYER, //not relevant
MOB_WATER_LAYER, //not relevant
L_HAND_LAYER, //not drawn, looks like ass
R_HAND_LAYER,
EARS_LAYER, //drawn manually
WING_LAYER,
WING_LOWER_LAYER, //drawn manually
HEAD_LAYER, //drawn manually
)
appearance_flags = KEEP_TOGETHER
var/no_fun_mode = FALSE //if true, we just draw the mob as an overlay
/obj/item/remote_scene_tool/voodoo_necklace
name = "Gem-Encrusted Necklace"
desc = "A bitingly cold metal necklace with a swirling gem in the center. Haters will say it's not magical"
icon_state = "necklace_inactive"
icon_root = "necklace"
slot_flags = SLOT_MASK
description_info = "The necklace and the associated doll act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other! It's vague, so use it how you want in RP! Just remember bystander consent!"
replacementType = /obj/item/remote_scene_tool/voodoo_doll
/obj/item/storage/box/remote_scene_tools/voodoo
icon_state = "voodoobox"
name = "Voodoo Supplies Box"
desc = "a horribly dusty box from 'magici-corp' with a ludicrously corny image of a voodoo doll on the front."
description_fluff = "originally marketed as part of a DIY magician routine, the company making them went under due to countless lawsuits from people who broke their nose upon the doll being dropped, and tesh filing descrimination suits. they haven't been made in well over a decade."
primary =/obj/item/remote_scene_tool/voodoo_doll
secondary =/obj/item/remote_scene_tool/voodoo_necklace
/obj/item/remote_scene_tool/voodoo_doll/verb/toggle_fun_mode()
set name = "Disable Special Rendering"
set desc = "Disables the displacement-based rendering on the doll, leaving it as just a doll at all times."
no_fun_mode = !no_fun_mode
to_chat(usr,span_notice("you turn the dial on the back of \the [src], and turn the advanced projection matrix [no_fun_mode ? "off" : "on"]"))
update_icon() //force a refresh
/obj/item/remote_scene_tool/voodoo_doll/Move(atom/newloc, direct, movetime)
. = ..()
dir = SOUTH //we need to do this
/obj/item/remote_scene_tool/voodoo_doll/proc/get_outline_color(var/r,var/g,var/b,var/a, var/offset = 30)
var/new_rgba = rgb(r-offset,g-offset,b-offset,a)
return new_rgba
/obj/item/remote_scene_tool/voodoo_doll/proc/generate_layer_matrix(var/list/data)
var/matrix/output = matrix()
output.Translate(data[DATA_X_OFFSET], data[DATA_Y_OFFSET])
output.Turn(data[DATA_ROTATION])
output.Scale(data[DATA_SCALE], data[DATA_SCALE])
return output
/obj/item/remote_scene_tool/voodoo_doll/proc/generate_layer_image(var/image/input, var/matrix/newtransform, var/outline_width = 0, var/outline_color = rgb(50,50,50))
if(input == null)
return null
input.dir = SOUTH
input.transform = newtransform
input.layer = FLOAT_LAYER
if(outline_width > 0)
input.filters += filter(type="outline", size = outline_width, color = outline_color)
return input
/obj/item/remote_scene_tool/voodoo_doll/update_icon()
. = ..()
//reset
overlays?.Cut()
underlays?.Cut()
filters = null
dir = SOUTH
name = initial(name)
desc = initial(desc)
var/matrix/rotateMatrix = matrix()
transform = rotateMatrix
if(!linked)
return
var/mob/owner = linked.getWearer()
if(!owner) return;
name = "Voodoo doll of " + owner.name
desc = "A small, marketable doll that looks suspiciously like [owner]... "
if(istype(owner,/mob/living/carbon/human/teshari)) return //no flesh horror, sorry
if(!ismob(loc) && !ismob(loc.loc)) return //only render the funny when we're on a humie
if(no_fun_mode) return //no fun allowed
if(ishuman(owner)) //TODO, refactor the fuck out of this once I port the tgui filter manager stuff over.
var/mob/living/carbon/human/buddy = owner
var/is_tail_taur = istaurtail(buddy.tail_style)
var/icon/displacement_map = icon(src.icon,is_tail_taur ? "taurdisplacement" : "displacement",SOUTH)
var/image/displacement_rendered = get_humanoid_displacement_map_image(buddy,discarded_layer_indicies,displacement_map,2,null)
//tails need special attention because taurs
var/tailoutlinecolor = rgb(50,50,50) //default to a grey, otherwise we use a darker version of one of the tail colors
if(buddy.tail_style?.do_colouration)
tailoutlinecolor = get_outline_color(buddy.r_tail,buddy.g_tail,buddy.b_tail,buddy.a_tail)
var/image/tail_image = buddy.get_tail_image()
if(is_tail_taur)
tail_image = generate_layer_image(buddy.get_tail_image(),generate_layer_matrix(taur_data),outline_size,tailoutlinecolor)
tail_image.filters += filter(type="alpha", flags = MASK_INVERSE)
else
tail_image = generate_layer_image(buddy.get_tail_image(),generate_layer_matrix(tail_data),outline_size,tailoutlinecolor)
//same kind of scenario for wings
var/wingoutlinecolor = rgb(50,50,50)
if(buddy.wing_style?.do_colouration)
wingoutlinecolor = get_outline_color(buddy.r_wing,buddy.g_wing,buddy.b_wing,buddy.a_wing)
var/image/wing_image = generate_layer_image(buddy.get_wing_image(FALSE),generate_layer_matrix(wing_data),outline_size,wingoutlinecolor)
var/image/under_wing_image = generate_layer_image(buddy.get_wing_image(TRUE),generate_layer_matrix(wing_data),outline_size,wingoutlinecolor)
//the rest are pretty simple
var/ear_icon = buddy.get_ears_overlay()
var/image/ear_image = generate_layer_image(image(icon = ear_icon),generate_layer_matrix(ear_data))
//hat
var/hat_icon = buddy.head?.make_worn_icon(body_type = buddy.species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER)
var/image/hat_image = generate_layer_image(image(icon = hat_icon),generate_layer_matrix(hat_data))
//add them all
overlays |= displacement_rendered
overlays += ear_image
overlays += hat_image
if(!is_tail_taur)
underlays += tail_image
else
overlays += tail_image
underlays |= wing_image
underlays |= under_wing_image
//filtering time
rotateMatrix.Turn(2) //very slight blur. this is unironically the best way to do it, any of the blur filters are just way too strong at a minimum value.
transform = rotateMatrix
#undef DATA_X_OFFSET
#undef DATA_Y_OFFSET
#undef DATA_SCALE
#undef DATA_ROTATION
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 B

+3 -1
View File
@@ -3057,8 +3057,10 @@
#include "code\modules\maint_recycler\code\vendor_datums\entries\misc_entries.dm"
#include "code\modules\maint_recycler\code\vendor_datums\entries\scene_tools.dm"
#include "code\modules\maint_recycler\code\vendor_datums\entries\weapons_and_arms.dm"
#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools.dm"
#include "code\modules\maint_recycler\code\vendor_goodies\vendor_goodies.dm"
#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\displacement_map_generation.dm"
#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\remote_scene_tools.dm"
#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\voodoo.dm"
#include "code\modules\makeup\nailpolish.dm"
#include "code\modules\maps\merge_conflicts.dm"
#include "code\modules\maps\bapi-dmm\bapi_bindings.dm"