diff --git a/code/WorkInProgress/mantaObjects.dm b/code/WorkInProgress/mantaObjects.dm index 62b4821ede211..6afd1af591c34 100644 --- a/code/WorkInProgress/mantaObjects.dm +++ b/code/WorkInProgress/mantaObjects.dm @@ -717,6 +717,7 @@ var/obj/manta_speed_lever/mantaLever = null max_stack = 5 item_state = "cone_1" wear_state = "cone_hat_1" + hat_offset_y = 8 setupProperties() ..() diff --git a/code/datums/components/hattable.dm b/code/datums/components/hattable.dm new file mode 100644 index 0000000000000..bc5f3f14eec3e --- /dev/null +++ b/code/datums/components/hattable.dm @@ -0,0 +1,107 @@ +TYPEINFO(/datum/component/hattable) // Take a walk through my TWISTED mind.... I'm sorry + initialization_args = list( + ARG_INFO("death_remove", DATA_INPUT_BOOL, "Remove component and signals on death?", FALSE), // Use this for things that don't revive, or explode on death + ARG_INFO("free_hat", DATA_INPUT_BOOL, "Should the parent get a free hat?", FALSE), // For AIs + ARG_INFO("default_hat_y", DATA_INPUT_NUM, "Y offset to start with", 0), + ARG_INFO("default_hat_x", DATA_INPUT_NUM, "X offset to start with", 0), + ARG_INFO("scale_amount", DATA_INPUT_NUM, "Amount to change the size of the hat by", 0) + ) + +/datum/component/hattable // Compatible stuff should be given hat_offset x and y vars in their own file + dupe_mode = COMPONENT_DUPE_HIGHLANDER + var/default_hat_y = 0 + var/default_hat_x = 0 + var/obj/item/hat = null + var/death_throw = FALSE + var/death_remove = FALSE + var/free_hat = FALSE + var/scale_amount = 0 + +/datum/component/hattable/Initialize(death_remove, free_hat, default_hat_y, default_hat_x, scale_amount) + . = ..() + RegisterSignal(src.parent, COMSIG_ATTACKBY, PROC_REF(hat_on_thing), override = TRUE) + src.death_throw = FALSE + src.death_remove = death_remove + src.default_hat_y = default_hat_y + src.default_hat_x = default_hat_x + src.scale_amount = scale_amount + if (free_hat) // If the thing gets a free hat (currently just AIs with spacebux) and they haven't received it yet, do that + var/free_hat_type = pick(filtered_concrete_typesof(/obj/item/clothing/head, /proc/filter_trait_hats)) + free_hat_type = new free_hat_type(get_turf(src)) + hat_on_thing(src.parent, free_hat_type) + + +/datum/component/hattable/proc/hat_on_thing(mob/target as mob, obj/item/item as obj, mob/attacker) + if (src.hat) + return + + var/atom/movable/hatted = src.parent + var/offsetBy_y = 0 + var/offsetBy_x = 0 + src.hat = item + + if (istype(src.hat, /obj/item/clothing/head/)) + ADD_FLAG(src.hat.appearance_flags, KEEP_TOGETHER) // Flags needed for wigs! + ADD_FLAG(src.hat.vis_flags, VIS_INHERIT_DIR) + else + return + + if (attacker) + attacker.drop_item() + + offsetBy_y = src.default_hat_y + src.hat.hat_offset_y // Add a hat's own offsets if they have them + offsetBy_x = src.default_hat_x + src.hat.hat_offset_x + src.hat.pixel_y = offsetBy_y + src.hat.pixel_x = offsetBy_x + + src.hat.transform *= src.scale_amount + src.hat.layer = hatted.layer + 1 + src.hat.set_loc(src.parent) + hatted.vis_contents += src.hat + + if (src.death_remove) + RegisterSignal(src.parent, COMSIG_MOB_DEATH, PROC_REF(die), override = TRUE) + RegisterSignal(src.hat, COMSIG_ITEM_PICKUP, PROC_REF(take_hat_off), override = TRUE) + return TRUE + +/datum/component/hattable/proc/take_hat_off(mob/target, mob/user) + if (!src.hat) + return + var/atom/movable/hatted = src.parent + + src.hat.set_loc(get_turf(hatted)) + hatted.vis_contents -= src.hat + src.hat.layer = OBJ_LAYER + src.hat.transform = 1 + src.hat.pixel_y = 0 + src.hat.pixel_x = 0 + + if(src.death_throw) // If the thing dies, this proc is called and death_throw is set to true, then false + var/turf/T = get_ranged_target_turf(src.hat, pick(alldirs), 3) + src.hat.throw_at(T, 3, 1) + UnregisterSignal(src.parent, list(COMSIG_MOB_DEATH, COMSIG_ATTACKBY)) + UnregisterSignal(src.hat, COMSIG_ITEM_PICKUP) + src.death_throw = FALSE + return + else + UnregisterSignal(src.hat, COMSIG_ITEM_PICKUP) + + if (istype(user, /mob/living/silicon/ghostdrone)) + SPAWN(0) // Magtractors use an action bar until they do stuff, and then they'll clone a ghost image of the item that's on the floor. Drop that! + var/mob/living/silicon/ghostdrone/drone = user + var/obj/item/magtractor/mag = locate(/obj/item/magtractor) in drone.tools + if (mag.holding) + mag.dropItem(src) + + src.hat = null + +/datum/component/hattable/proc/die() + if (src.hat) + src.death_throw = TRUE + take_hat_off(src.parent) + + + + + + diff --git a/code/mob/living/silicon/ai.dm b/code/mob/living/silicon/ai.dm index 76671e30b5c1d..203c9232e1b59 100644 --- a/code/mob/living/silicon/ai.dm +++ b/code/mob/living/silicon/ai.dm @@ -86,6 +86,7 @@ var/global/list/ai_emotions = list("Annoyed" = "ai_annoyed-dol", \ density = 1 emaggable = 0 // Can't be emagged... syndicate_possible = 1 // ...but we can become a rogue computer. + var/default_hat_y = 14 var/datum/hud/silicon/ai/hud var/last_notice = 0//attack notices var/network = "SS13" @@ -108,6 +109,7 @@ var/global/list/ai_emotions = list("Annoyed" = "ai_annoyed-dol", \ var/termMute = FALSE var/canvox = 1 var/can_announce = 1 + var/bought_hat = FALSE var/last_announcement = -INFINITY var/announcement_cooldown = 1200 var/dismantle_stage = 0 @@ -194,7 +196,6 @@ or don't if it uses a custom topopen overlay sound_fart = 'sound/voice/farts/poo2_robot.ogg' req_access = list(access_heads) - var/obj/item/clothing/head/hat = null var/fire_res_on_core = 0 @@ -210,26 +211,6 @@ or don't if it uses a custom topopen overlay var/datum/ai_hologram_data/holoHolder = new var/list/hologramContextActions - proc/set_hat(obj/item/clothing/head/hat, var/mob/user as mob) - if( src.hat ) - src.hat.wear_image.pixel_y = 0 - src.UpdateOverlays(null, "hat") - if (user) - user.put_in_hand_or_drop(src.hat) - else - src.hat.set_loc(src.loc) - src.hat = null - // src.hat.wear_image.pixel_y = 10 - // src.UpdateOverlays(src.hat.wear_image, "hat") - var/image/hat_image = SafeGetOverlayImage(hat.icon_state, hat.icon, hat.icon_state, src.layer+0.3) - hat_image.pixel_y = 12 - if (istype(hat, /obj/item/clothing/head/bighat)) - hat_image.pixel_y = 20 - - src.UpdateOverlays(hat_image, "hat") - src.hat = hat - hat.set_loc(src) - /mob/living/silicon/ai/proc/give_feet() animate(src, pixel_y = 14, time = 5, easing = SINE_EASING) has_feet = 1 @@ -293,7 +274,11 @@ or don't if it uses a custom topopen overlay ai_station_map = new /obj/minimap/ai AddComponent(/datum/component/minimap_marker, MAP_AI | MAP_SYNDICATE, "ai") - + SPAWN(0) + if (bought_hat || prob(5)) + AddComponent(/datum/component/hattable, TRUE, TRUE, default_hat_y) + else + AddComponent(/datum/component/hattable, TRUE, FALSE, default_hat_y) light = new /datum/light/point light.set_color(0.4, 0.7, 0.95) light.set_brightness(0.6) @@ -340,9 +325,7 @@ or don't if it uses a custom topopen overlay var/datum/contextAction/ai_hologram/action = new actionType(src) hologramContextActions += action - if(prob(5)) - var/hat_type = pick(childrentypesof(/obj/item/clothing/head)) - src.set_hat(new hat_type) + SPAWN(0) @@ -595,11 +578,6 @@ or don't if it uses a custom topopen overlay user.visible_message(SPAN_ALERT("[user.name] uploads a moustache to [src.name]!")) else if (src.dismantle_stage == 4 || isdead(src)) boutput(user, SPAN_ALERT("Using this on a deactivated AI would be silly.")) - else if( istype(W,/obj/item/clothing/head)) - user.drop_item() - src.set_hat(W, user) - user.visible_message( SPAN_NOTICE("[user] places the [W] on the [src]!") ) - src.show_message( SPAN_NOTICE("[user] places the [W] on you!") ) if(istype(W, /obj/item/clothing/head/butt)) var/obj/item/clothing/head/butt/butt = W if(butt.donor == user) @@ -1010,8 +988,6 @@ or don't if it uses a custom topopen overlay return list() . = list("[SPAN_NOTICE("This is [bicon(src)] [src.name]!")] [skinsList[coreSkin]]
") // skinList[coreSkin] points to the appropriate desc for the current core skin - if (src.hat) - . += SPAN_NOTICE("[src.name] is wearing the [bicon(src.hat)] [src.hat.name].") if (isdead(src)) . += SPAN_ALERT("[src.name] is nonfunctional...") diff --git a/code/mob/living/silicon/ghostdrone.dm b/code/mob/living/silicon/ghostdrone.dm index 10e9e2dafe947..16a577f3c6e49 100644 --- a/code/mob/living/silicon/ghostdrone.dm +++ b/code/mob/living/silicon/ghostdrone.dm @@ -14,6 +14,8 @@ punchMessage = "whaps" kickMessage = "bonks" + var/default_hat_y = 7 + var/datum/hud/ghostdrone/hud var/obj/item/device/radio/radio = null @@ -25,17 +27,16 @@ var/faceType var/charging = 0 var/newDrone = 0 - var/jetpack = 1 //fuck whoever made this var/sees_static = TRUE //gimmicky things - var/obj/item/clothing/head/hat = null var/obj/item/clothing/suit/bedsheet/bedsheet = null New() ..() + AddComponent(/datum/component/hattable, TRUE, FALSE, default_hat_y, 0, 0.75) remove_lifeprocess(/datum/lifeprocess/radiation) APPLY_ATOM_PROPERTY(src, PROP_MOB_RADPROT_INT, src, 100) START_TRACKING @@ -163,7 +164,6 @@ src.visible_message(SPAN_COMBAT("[src.name] explodes in a shower of lost hopes and dreams.")) var/turf/T = get_ranged_target_turf(src, pick(alldirs), 3) if (magHeld) magHeld.throw_at(T, 3, 1) //flying...anything - if (src.hat) src.takeoffHat(pick(alldirs)) //flying hats if (src.bedsheet) //flying bedsheets bedsheet.set_loc(get_turf(src)) bedsheet.throw_at(T, 3, 1) @@ -180,7 +180,6 @@ msg = "[src.name]'s scream's gain echo and lose their electronic modulation as its soul is ripped monstrously from the cold metal body it once inhabited." src.visible_message(SPAN_COMBAT("[msg]")) - if (src.hat) src.takeoffHat() src.updateSprite() ..() @@ -425,38 +424,7 @@ step(AM, t) src.now_pushing = null - //Four very important procs follow - proc/putonHat(obj/item/clothing/head/W as obj, mob/user as mob) - src.hat = W - W.set_loc(src) - var/image/hatImage = null - // Treat wigs differently as their icon_state is always bald - if (istype(W, /obj/item/clothing/head/wig)) - hatImage = W.wear_image - hatImage.layer = src.layer+0.1 - hatImage.pixel_y = -7 - else - hatImage = image(icon = W.icon, icon_state = W.icon_state, layer = src.layer+0.1) - hatImage.pixel_y = 5 - hatImage.transform *= 0.9 - UpdateOverlays(hatImage, "hat") - return 1 - - proc/takeoffHat(forcedDir = null) - UpdateOverlays(null, "hat") - src.hat.set_loc(get_turf(src)) - - var/turf/T - if (isnum(forcedDir)) - T = get_ranged_target_turf(src, forcedDir, 3) - if (isturf(forcedDir)) - T = forcedDir - if (isturf(T)) - src.hat.throw_at(T, 3, 1) - - src.hat = null - return 1 - + // Two important procs follow proc/putonSheet(obj/item/clothing/suit/bedsheet/W as obj, mob/user as mob) W.set_loc(src) src.bedsheet = W @@ -517,16 +485,6 @@ playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) if (src.health >= 25) boutput(user, SPAN_NOTICE("The wiring is fully repaired. Now you need to weld the external plating.")) - - else if (istype(W, /obj/item/clothing/head)) - if(src.hat) - boutput(user, SPAN_ALERT("[src] is already wearing a hat!")) - return - - user.drop_item() - src.putonHat(W, user) - if (user != src) - user.visible_message("[user] gently places a hat on [src]!", "You gently place a hat on [src]!") return else if (istype(W, /obj/item/clothing/suit/bedsheet)) @@ -551,10 +509,7 @@ if(INTENT_DISARM) //Shove SPAWN(0) playsound(src.loc, 'sound/impact_sounds/Generic_Swing_1.ogg', 40, 1) user.visible_message(SPAN_ALERT("[user] shoves [src]! [prob(40) ? pick_string("descriptors.txt", "jerks") : null]")) - if (src.hat) - user.visible_message("[user] knocks \the [src.hat] off [src]!", "You knock the hat off [src]!") - src.takeoffHat() - else if (src.bedsheet) + if (src.bedsheet) user.visible_message("[user] pulls the sheet off [src]!", "You pull the sheet off [src]!") src.takeoffSheet() if(INTENT_GRAB) //Shake @@ -1080,17 +1035,6 @@ src.changeStatus("stunned", stun SECONDS) - if (src.hat) //For hats getting shot off - UpdateOverlays(null, "hat") - src.hat.set_loc(get_turf(src)) - //get target turf - var/x = round(P.xo * 4) - var/y = round(P.yo * 4) - var/turf/target = get_offset_target_turf(src, x, y) - - src.visible_message(SPAN_COMBAT("[src]'s [src.hat] goes flying!")) - src.takeoffHat(target) - if (damage < 1) return diff --git a/code/modules/barber/barber_shop.dm b/code/modules/barber/barber_shop.dm index 843fdc8455c35..afb351f5e9045 100644 --- a/code/modules/barber/barber_shop.dm +++ b/code/modules/barber/barber_shop.dm @@ -18,6 +18,8 @@ desc = "You can't tell the difference, Honest!" icon_state= "wig" wear_layer = MOB_HAIR_LAYER2 //it IS hair afterall + hat_offset_y = -15 // offsets for hattable component + hat_offset_x = 0 ///Takes a list of style ids to colors and generates a wig from it proc/setup_wig(var/style_list) diff --git a/code/modules/economy/persistent_bank_purchases.dm b/code/modules/economy/persistent_bank_purchases.dm index b46e6bed6c21c..8f700175dc9f6 100644 --- a/code/modules/economy/persistent_bank_purchases.dm +++ b/code/modules/economy/persistent_bank_purchases.dm @@ -127,11 +127,9 @@ var/global/list/persistent_bank_purchaseables = list(\ if(isAI(M)) var/mob/living/silicon/ai/AI = M - if (ispath(path, /obj/item/clothing)) - if(ispath(path,/obj/item/clothing/head)) - AI.set_hat(new path(AI)) - equip_success = 1 - + path = null + AI.bought_hat = TRUE + return //The AI can't really wear items... @@ -788,7 +786,6 @@ var/global/list/persistent_bank_purchaseables = list(\ Create(var/mob/living/M) if (isAI(M)) var/mob/living/silicon/ai/A = M - var/picked = pick(filtered_concrete_typesof(/obj/item/clothing/head, /proc/filter_trait_hats)) - A.set_hat(new picked()) + A.bought_hat = TRUE return 1 return 0 diff --git a/code/modules/mechanics/MechanicMadness.dm b/code/modules/mechanics/MechanicMadness.dm index c8844c5479605..941a910fe8f65 100644 --- a/code/modules/mechanics/MechanicMadness.dm +++ b/code/modules/mechanics/MechanicMadness.dm @@ -30,6 +30,8 @@ var/welded = FALSE var/can_be_welded = FALSE var/can_be_anchored = UNANCHORED + var/default_hat_y = 0 + var/default_hat_x = 0 custom_suicide = TRUE open_to_sound = TRUE @@ -224,6 +226,11 @@ icon_state="housing_cabinet" flags = FPRINT | EXTRADELAY | CONDUCT light_color = list(0, 179, 255, 255) + default_hat_y = 14 + + New() + AddComponent(/datum/component/hattable, FALSE, FALSE, default_hat_y) + ..() attack_hand(mob/user) if (istype(user,/mob/living/object) && user == src.loc) // prevent wacky nullspace bug @@ -265,6 +272,12 @@ c_flags = ONBELT light_color = list(51, 0, 0, 0) spawn_contents=list(/obj/item/mechanics/trigger/trigger) + default_hat_y = 7 + default_hat_x = -1 + + New() + AddComponent(/datum/component/hattable, FALSE, FALSE, default_hat_y, default_hat_x) + ..() proc/find_trigger() // find the trigger comp, return 1 if found. if (!istype(src.the_trigger)) diff --git a/code/obj/item.dm b/code/obj/item.dm index 037d98be4edbc..5db6e73897d10 100644 --- a/code/obj/item.dm +++ b/code/obj/item.dm @@ -22,6 +22,10 @@ ABSTRACT_TYPE(/obj/item) var/inhand_color = null /// storage datum holding it var/datum/storage/stored = null + /// Used for the hattable component + var/hat_offset_y = 0 + /// Used for the hattable component + var/hat_offset_x = 0 /*_______*/ /*Burning*/ @@ -1154,14 +1158,15 @@ ADMIN_INTERACT_PROCS(/obj/item, proc/admin_set_stack_amount) return "It is \an [t] item." /obj/item/attack_hand(mob/user) - var/checkloc = src.loc - while(checkloc && !istype(checkloc,/turf)) - if (isliving(checkloc) && checkloc != user) - if(src in bible_contents) - break - else - return 0 - checkloc = checkloc:loc + var/obj/item/checkloc = src.loc + if (!ismob(src.loc)) // Skip this loop if the FIRST loc is a mob, allowing component/hattable to proc take_hat_off on AIs/ghostdrones + while(checkloc && !istype(checkloc,/turf)) + if(isliving(checkloc) && checkloc != user) // This heinous block is to make sure you're not swiping things from other people's backpacks + if(src in bible_contents) // Bibles share their contents globally, so magically taking stuff from them is fine + break + else + return 0 + checkloc = checkloc.loc // Get the loc of the loc! The loop continues until it's the turf of what you clicked on if(!src.can_pickup(user)) // unholdable storage items diff --git a/code/obj/item/clothing/hats.dm b/code/obj/item/clothing/hats.dm index db9eaf464a8c7..620bdd1adb703 100644 --- a/code/obj/item/clothing/hats.dm +++ b/code/obj/item/clothing/hats.dm @@ -2036,6 +2036,7 @@ ABSTRACT_TYPE(/obj/item/clothing/head/mushroomcap) name = "mushroom cap" desc = "Makes your lungs feel a little fuzzy." var/additional_desc = "" + hat_offset_y = 4 icon_state = "mushroom-red" item_state = "mushroom-red" diff --git a/goonstation.dme b/goonstation.dme index cbf1c0b1b5f61..ab727c02057e5 100644 --- a/goonstation.dme +++ b/goonstation.dme @@ -224,6 +224,7 @@ var/datum/preMapLoad/preMapLoad = new #include "code\datums\components\glue_ready.dm" #include "code\datums\components\glued.dm" #include "code\datums\components\hallucinations.dm" +#include "code\datums\components\hattable.dm" #include "code\datums\components\health_maptext.dm" #include "code\datums\components\legs.dm" #include "code\datums\components\light.dm"