Skip to content

Commit

Permalink
Componentizes putting hats on ghostdrones and AI cores, extends suppo…
Browse files Browse the repository at this point in the history
…rt to mechcomponent storages (#18140)

Co-authored-by: CalliopeSoups <email@email.com>
  • Loading branch information
CalliopeSoups and CalliopeSoups committed Apr 4, 2024
1 parent 540bd28 commit f88eaab
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 108 deletions.
1 change: 1 addition & 0 deletions code/WorkInProgress/mantaObjects.dm
Expand Up @@ -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()
..()
Expand Down
107 changes: 107 additions & 0 deletions 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)






40 changes: 8 additions & 32 deletions code/mob/living/silicon/ai.dm
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -595,11 +578,6 @@ or don't if it uses a custom topopen overlay
user.visible_message(SPAN_ALERT("<b>[user.name]</b> 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)
Expand Down Expand Up @@ -1010,8 +988,6 @@ or don't if it uses a custom topopen overlay
return list()

. = list("[SPAN_NOTICE("This is [bicon(src)] <B>[src.name]</B>!")] [skinsList[coreSkin]]<br>") // 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...")
Expand Down
66 changes: 5 additions & 61 deletions code/mob/living/silicon/ghostdrone.dm
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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()
..()

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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("<b>[user]</b> gently places a hat on [src]!", "You gently place a hat on [src]!")
return

else if (istype(W, /obj/item/clothing/suit/bedsheet))
Expand All @@ -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("<B>[user] shoves [src]! [prob(40) ? pick_string("descriptors.txt", "jerks") : null]</B>"))
if (src.hat)
user.visible_message("<b>[user]</b> knocks \the [src.hat] off [src]!", "You knock the hat off [src]!")
src.takeoffHat()
else if (src.bedsheet)
if (src.bedsheet)
user.visible_message("<b>[user]</b> pulls the sheet off [src]!", "You pull the sheet off [src]!")
src.takeoffSheet()
if(INTENT_GRAB) //Shake
Expand Down Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions code/modules/barber/barber_shop.dm
Expand Up @@ -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)
Expand Down
11 changes: 4 additions & 7 deletions code/modules/economy/persistent_bank_purchases.dm
Expand Up @@ -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...
Expand Down Expand Up @@ -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

0 comments on commit f88eaab

Please sign in to comment.