Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Componentizes putting hats on ghostdrones and AI cores, extends support to mechcomponent storages #18140

Merged
merged 5 commits into from Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
CalliopeSoups marked this conversation as resolved.
Show resolved Hide resolved
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