From 4d707fc18d634eecdbc7f6eb1da70728dc4be8af Mon Sep 17 00:00:00 2001 From: Dahlular Date: Tue, 17 Mar 2020 12:22:39 -0600 Subject: [PATCH] Buggy Sizecode Interactions 1 FUCK. --- code/__DEFINES/movespeed_modification.dm | 6 +- code/game/machinery/suit_storage_unit.dm | 3 + code/game/machinery/washing_machine.dm | 4 + .../kitchen_machinery/deep_fryer.dm | 3 + code/modules/mob/living/carbon/carbon.dm | 41 +- code/modules/mob/living/carbon/examine.dm | 4 + .../mob/living/carbon/human/examine.dm | 4 + code/modules/mob/living/carbon/human/human.dm | 112 ++---- .../mob/living/carbon/monkey/combat.dm | 4 + .../mob/living/carbon/monkey/monkey.dm | 17 +- .../mob/living/carbon/monkey/update_icons.dm | 373 +++++++++++++++--- code/modules/mob/living/inhand_holder.dm | 148 ++++--- code/modules/mob/living/living.dm | 46 ++- code/modules/mob/living/living_defines.dm | 2 +- .../mob/living/simple_animal/friendly/cat.dm | 3 + .../mob/living/simple_animal/friendly/dog.dm | 6 + .../simple_animal/friendly/drone/_drone.dm | 4 + .../friendly/drone/interaction.dm | 25 +- .../mob/living/simple_animal/friendly/fox.dm | 2 +- .../living/simple_animal/friendly/lizard.dm | 5 + .../living/simple_animal/friendly/mouse.dm | 11 +- .../living/simple_animal/friendly/sloth.dm | 2 +- code/modules/recycling/disposal/bin.dm | 14 +- .../code/modules/resize/holder_micro.dm | 8 + hyperstation/code/modules/resize/resizing.dm | 160 ++++++++ hyperstation/code/modules/resize/sizechems.dm | 40 ++ hyperstation/code/modules/resize/sizegun.dm | 86 ++++ icons/mob/animals_held.dmi | Bin 0 -> 675 bytes icons/mob/animals_held_lh.dmi | Bin 0 -> 10036 bytes icons/mob/animals_held_rh.dmi | Bin 0 -> 10099 bytes 30 files changed, 889 insertions(+), 244 deletions(-) create mode 100644 hyperstation/code/modules/resize/holder_micro.dm create mode 100644 hyperstation/code/modules/resize/resizing.dm create mode 100644 hyperstation/code/modules/resize/sizechems.dm create mode 100644 hyperstation/code/modules/resize/sizegun.dm create mode 100644 icons/mob/animals_held.dmi create mode 100644 icons/mob/animals_held_lh.dmi create mode 100644 icons/mob/animals_held_rh.dmi diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 9faa37ac..757de996 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -32,7 +32,5 @@ #define MOVESPEED_ID_SANITY "MOOD_SANITY" -#define MOVESPEED_ID_TASED_STATUS "TASED" - -#define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG" -#define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY" \ No newline at end of file +#define MOVESPEED_ID_SHRUNK "SHRINK_SPEED_MODIFIER" +#define MOVESPEED_ID_GROW "GROWTH_SPEED_MODIFIER" \ No newline at end of file diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 3051a72d..c9a94ecc 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -307,6 +307,9 @@ /obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params) if(state_open && is_operational()) + if(istype(I, /obj/item/clothing/head/mob_holder)) + to_chat(user, "You can't quite fit that while you hold it!") + return if(istype(I, /obj/item/clothing/suit)) if(suit) to_chat(user, "The unit already contains a suit!.") diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index af9e47f5..69e244a6 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -219,6 +219,10 @@ update_icon() return + if(istype(W, /obj/item/clothing/head/mob_holder)) + to_chat(user, "It's too unweildy to put in this way.") + return 1 + else if(user.a_intent != INTENT_HARM) if (!state_open) diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm index dd33f696..0755fdc7 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm @@ -80,6 +80,9 @@ God bless America. I.reagents.trans_to(src, I.reagents.total_volume) qdel(I) return + if(istype(I,/obj/item/clothing/head/mob_holder)) + to_chat(user, "This does not fit in the fryer.") // TODO: Deepfrying instakills mobs, spawns a whole deep-fried mob. + return if(!reagents.has_reagent("cooking_oil")) to_chat(user, "[src] has no cooking oil to fry with!") return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 89a7ec25..d9a1716a 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -159,22 +159,35 @@ return //END OF CIT CHANGES - var/atom/movable/thrown_thing var/obj/item/I = src.get_active_held_item() - if(!I) - if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE) - var/mob/living/throwable_mob = pulling - if(!throwable_mob.buckled) - thrown_thing = throwable_mob + var/atom/movable/thrown_thing + var/mob/living/throwable_mob + + if(istype(I, /obj/item/clothing/head/mob_holder)) + var/obj/item/clothing/head/mob_holder/holder = I + if(holder.held_mob) + throwable_mob = holder.held_mob + holder.release() + + if(!I || throwable_mob) + if(!throwable_mob && pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE) + throwable_mob = pulling + + if(throwable_mob && !throwable_mob.buckled) + thrown_thing = throwable_mob + if(pulling) stop_pulling() - if(HAS_TRAIT(src, TRAIT_PACIFISM)) - to_chat(src, "You gently let go of [throwable_mob].") - adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring - var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors - var/turf/end_T = get_turf(target) - if(start_T && end_T) - log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") + if(HAS_TRAIT(src, TRAIT_PACIFISM)) + to_chat(src, "You gently let go of [throwable_mob].") + return + + adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring + var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors + var/turf/end_T = get_turf(target) + if(start_T && end_T) + + log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") else if(!CHECK_BITFIELD(I.item_flags, ABSTRACT) && !HAS_TRAIT(I, TRAIT_NODROP)) thrown_thing = I @@ -194,6 +207,8 @@ newtonian_move(get_dir(target, src)) thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src) + + /mob/living/carbon/restrained(ignore_grab) . = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE)) diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 22da4634..b4ce85c0 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -16,6 +16,10 @@ msg += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face.\n" if (wear_neck) msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n" + if(can_be_held) + msg += "[t_He] looks small enough to be picked up with Alt+Click!\n" + + for(var/obj/item/I in held_items) if(!(I.item_flags & ABSTRACT)) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 1bfff1a9..50f63044 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -107,6 +107,10 @@ //Status effects msg += status_effect_examines() + //Can be picked up? + if(can_be_held) + msg += "[t_He] looks small enough to be picked up with Alt+Click!\n" + //CIT CHANGES START HERE - adds genital details to examine text if(LAZYLEN(internal_organs)) for(var/obj/item/organ/genital/dicc in internal_organs) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index b4a8839e..5bd897f9 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -854,84 +854,52 @@ .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]" /mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) - if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS) - //If they dragged themselves and we're currently aggressively grabbing them try to piggyback - if(user == target && can_piggyback(target)) - piggyback(target) - return - //If you dragged them to you and you're aggressively grabbing try to fireman carry them - else if(user != target && can_be_firemanned(target)) - fireman_carry(target) - return + //If they dragged themselves and we're currently aggressively grabbing them try to piggyback + if(user == target && can_piggyback(target) && pulling == target && (HAS_TRAIT(src, TRAIT_PACIFISM) || grab_state >= GRAB_AGGRESSIVE) && stat == CONSCIOUS) + buckle_mob(target,TRUE,TRUE) . = ..() -//src is the user that will be carrying, target is the mob to be carried -/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/target) - return (istype(target) && target.stat == CONSCIOUS) +/mob/living/carbon/human/proc/piggyback_instant(mob/living/M) + return buckle_mob(M, TRUE, TRUE, FALSE, TRUE) -/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target) - return (ishuman(target) && target.lying) +//Can C try to piggyback at all. +/mob/living/carbon/human/proc/can_piggyback(mob/living/carbon/C) + if(istype(C) && C.stat == CONSCIOUS) + return TRUE + return FALSE -/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target) - if(can_be_firemanned(target)) - visible_message("[src] starts lifting [target] onto their back...", - "You start lifting [target] onto your back...") - if(do_after(src, 30, TRUE, target)) - //Second check to make sure they're still valid to be carried - if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE)) - target.resting = FALSE - buckle_mob(target, TRUE, TRUE, 90, 1, 0) - return - visible_message("[src] fails to fireman carry [target]!") - else - to_chat(src, "You can't fireman carry [target] while they're standing!") - -/mob/living/carbon/human/proc/piggyback(mob/living/carbon/target) - if(can_piggyback(target)) - visible_message("[target] starts to climb onto [src]...") - if(do_after(target, 15, target = src)) - if(can_piggyback(target)) - if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) - target.visible_message("[target] can't hang onto [src]!") - return - buckle_mob(target, TRUE, TRUE, FALSE, 0, 2) - else - visible_message("[target] fails to climb onto [src]!") - else - to_chat(target, "You can't piggyback ride [src] right now!") - -/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = FALSE, hands_needed = 0, target_hands_needed = 0) +/mob/living/carbon/human/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE, bypass_piggybacking = FALSE, no_delay = FALSE) if(!force)//humans are only meant to be ridden through piggybacking and special cases return - if(!is_type_in_typecache(target, can_ride_typecache)) - target.visible_message("[target] really can't seem to mount [src]...") + if(bypass_piggybacking) + return ..() + if(!is_type_in_typecache(M, can_ride_typecache)) + M.visible_message("[M] really can't seem to mount [src]...") return - buckle_lying = lying_buckle var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human) - if(target_hands_needed) - riding_datum.ride_check_rider_restrained = TRUE - if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled) + riding_datum.ride_check_rider_incapacitated = TRUE + riding_datum.ride_check_rider_restrained = TRUE + riding_datum.set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))) + if(buckled_mobs && ((M in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled || (M.stat != CONSCIOUS)) return - var/equipped_hands_self - var/equipped_hands_target - if(hands_needed) - equipped_hands_self = riding_datum.equip_buckle_inhands(src, hands_needed, target) - if(target_hands_needed) - equipped_hands_target = riding_datum.equip_buckle_inhands(target, target_hands_needed) - - if(hands_needed || target_hands_needed) - if(hands_needed && !equipped_hands_self) - src.visible_message("[src] can't get a grip on [target] because their hands are full!", - "You can't get a grip on [target] because your hands are full!") - return - else if(target_hands_needed && !equipped_hands_target) - target.visible_message("[target] can't get a grip on [src] because their hands are full!", - "You can't get a grip on [src] because your hands are full!") - return - - stop_pulling() - riding_datum.handle_vehicle_layer() - . = ..(target, force, check_loc) + if(can_piggyback(M)) + riding_datum.ride_check_ridden_incapacitated = TRUE + visible_message("[M] starts to climb onto [src]...") + if(no_delay || do_after(M, 15, target = src)) + if(can_piggyback(M)) + if(M.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE)) + M.visible_message("[M] can't hang onto [src]!") + return + if(!riding_datum.equip_buckle_inhands(M, 2)) //MAKE SURE THIS IS LAST!! + M.visible_message("[M] can't climb onto [src]!") + return + . = ..(M, force, check_loc) + stop_pulling() + else + visible_message("[M] fails to climb onto [src]!") + else + . = ..(M,force,check_loc) + stop_pulling() /mob/living/carbon/human/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this for(var/obj/item/clothing/C in get_equipped_items()) //doesn't include pockets @@ -1098,4 +1066,8 @@ race = /datum/species/zombie/infectious /mob/living/carbon/human/species/zombie/krokodil_addict - race = /datum/species/krokodil_addict \ No newline at end of file + race = /datum/species/krokodil_addict + +//define holder_type on nibbas we wanna commit scoop to +/mob/living/carbon/human + var/holder_type = /obj/item/clothing/head/mob_holder/micro \ No newline at end of file diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm index 25bc243f..92ad87f6 100644 --- a/code/modules/mob/living/carbon/monkey/combat.dm +++ b/code/modules/mob/living/carbon/monkey/combat.dm @@ -132,6 +132,10 @@ pickupTarget = null pickupTimer = 0 else if(ismob(pickupTarget.loc)) // in someones hand + if(istype(pickupTarget, /obj/item/clothing/head/mob_holder/)) + var/obj/item/clothing/head/mob_holder/h = pickupTarget + if(h && h.held_mob==src) + return//dont let them pickpocket themselves var/mob/M = pickupTarget.loc if(!pickpocketing) pickpocketing = TRUE diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 7b44a01a..47a177f7 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -15,6 +15,7 @@ bodyparts = list(/obj/item/bodypart/chest/monkey, /obj/item/bodypart/head/monkey, /obj/item/bodypart/l_arm/monkey, /obj/item/bodypart/r_arm/monkey, /obj/item/bodypart/r_leg/monkey, /obj/item/bodypart/l_leg/monkey) hud_type = /datum/hud/monkey + can_be_held = "monkey" /mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner) verbs += /mob/living/proc/mob_sleep @@ -45,6 +46,10 @@ SSmobs.cubemonkeys -= src return ..() +/mob/living/carbon/monkey/generate_mob_holder() + var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "monkey", 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi', TRUE) + return holder + /mob/living/carbon/monkey/create_internal_organs() internal_organs += new /obj/item/organ/appendix internal_organs += new /obj/item/organ/lungs @@ -169,15 +174,3 @@ var/obj/item/clothing/head/helmet/justice/escape/helmet = new(src) equip_to_slot_or_del(helmet,SLOT_HEAD) helmet.attack_self(src) // todo encapsulate toggle - -/mob/living/carbon/monkey/tumor - name = "living teratoma" - verb_say = "blabbers" - initial_language_holder = /datum/language_holder/monkey - icon = 'icons/mob/monkey.dmi' - icon_state = "" - butcher_results = list(/obj/effect/spawner/lootdrop/teratoma/minor = 5, /obj/effect/spawner/lootdrop/teratoma/major = 1) - type_of_meat = /obj/effect/spawner/lootdrop/teratoma/minor - aggressive = TRUE - bodyparts = list(/obj/item/bodypart/chest/monkey/teratoma, /obj/item/bodypart/head/monkey/teratoma, /obj/item/bodypart/l_arm/monkey/teratoma, - /obj/item/bodypart/r_arm/monkey/teratoma, /obj/item/bodypart/r_leg/monkey/teratoma, /obj/item/bodypart/l_leg/monkey/teratoma) diff --git a/code/modules/mob/living/carbon/monkey/update_icons.dm b/code/modules/mob/living/carbon/monkey/update_icons.dm index ff83b00f..75ec032c 100644 --- a/code/modules/mob/living/carbon/monkey/update_icons.dm +++ b/code/modules/mob/living/carbon/monkey/update_icons.dm @@ -1,80 +1,355 @@ +//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can. +/mob/living/carbon/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/final_pixel_y = pixel_y + var/final_dir = dir + var/changed = 0 -/mob/living/carbon/monkey/regenerate_icons() - if(!..()) - update_body_parts() - update_hair() - update_inv_wear_mask() - update_inv_head() - update_inv_back() - update_transform() + if(lying != lying_prev && rotate_on_lying) + changed++ + ntransform.TurnTo(lying_prev,lying) + if(lying == 0) //Lying to standing + final_pixel_y = get_standard_pixel_y_offset() + if(size_multiplier >= 1) //if its bigger than normal + ntransform.Translate(0,16 * (size_multiplier-1)) + else + if(lying_prev == 90) + ntransform.Translate(16 * (size_multiplier-1),16 * (size_multiplier-1)) + + if(lying_prev == 270) + ntransform.Translate(-16 * (size_multiplier-1),16 * (size_multiplier-1)) + + else //if(lying != 0) + if(lying_prev == 0) //Standing to lying + pixel_y = get_standard_pixel_y_offset() + final_pixel_y = get_standard_pixel_y_offset(lying) + if(lying == 90) //Check the angle of the sprite to offset it accordingly. + ntransform.Translate(-16 * (size_multiplier-1),0) + if(size_multiplier < 1) //if its smaller than normal + ntransform.Translate(0,16 * (size_multiplier-1)) //we additionally offset the sprite downwards + + if(lying == 270) //check the angle of the sprite to offset it accordingly + ntransform.Translate(16 * (size_multiplier-1),0) + if(size_multiplier < 1) //if its smaller than normal + ntransform.Translate(0,16 * (size_multiplier-1)) //we additionally offset the sprite downwards + + if(dir & (EAST|WEST)) //Facing east or west + final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass + + if(resize != RESIZE_DEFAULT_SIZE) + changed++ + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + //Apply size multiplier, thank NeverExisted for this + if(size_multiplier != previous_size) + changed++ + //now we offset the sprite + //Scaling affects offset. There's probably a smarter and easier way to do this, but this way it works for sure (?) + //Just to be clear. All this bullshit is needed because someone wanted to store the old transform matrix instead of using a new one each iteration + //Winfre is currently doing a great job at coating my nuts in slobber while i code this + if(!lying) //when standing. People of all sizes are affected equally + ntransform.Translate(0,-16 * (previous_size-1)) //reset the sprite + ntransform.Scale(size_multiplier/previous_size) //scale the sprite accordingly. + ntransform.Translate(0,16 * (size_multiplier-1)) //apply the new offset + else //when lying. Macros dont get an offset, Micros do. We must also check the cases when a micro becomes a macro and viceversa + if(previous_size <= 1 && size_multiplier <= 1) //micro stays a micro. We modify the side-offset + ntransform.Translate(0,-16 * (previous_size-1)) //reset the sprite + ntransform.Scale(size_multiplier/previous_size) //scale the sprite accordingly + ntransform.Translate(0,16 * (size_multiplier-1)) //apply the new offset + + if(previous_size <= 1 && size_multiplier > 1) //micro becomes a macro. We remove the side-offset + ntransform.Translate(0,-16 * (previous_size-1)) //reset the sprite + ntransform.Scale(size_multiplier/previous_size) //scale the sprite accordingly + + if(previous_size > 1 && size_multiplier <= 1) //macro becomes a micro. We add an offset + ntransform.Scale(size_multiplier/previous_size) //scale the sprite accordingly. + ntransform.Translate(0,16 * (size_multiplier-1)) //apply the new offset + + if(previous_size > 1 && size_multiplier > 1) //macro stays a macro. We just scale the sprite with no offset changes + ntransform.Scale(size_multiplier/previous_size) //scale the sprite accordingly + + previous_size = size_multiplier + + if(changed) + animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) + floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). -//////// +/mob/living/carbon + var/list/overlays_standing[TOTAL_LAYERS] + +/mob/living/carbon/proc/apply_overlay(cache_index) + if((. = overlays_standing[cache_index])) + add_overlay(.) + +/mob/living/carbon/proc/remove_overlay(cache_index) + var/I = overlays_standing[cache_index] + if(I) + cut_overlay(I) + overlays_standing[cache_index] = null + +/mob/living/carbon/regenerate_icons() + if(notransform) + return 1 + update_inv_hands() + update_inv_handcuffed() + update_inv_legcuffed() + update_fire() -/mob/living/carbon/monkey/update_hair() - remove_overlay(HAIR_LAYER) - - var/obj/item/bodypart/head/HD = get_bodypart(BODY_ZONE_HEAD) - if(!HD) //Decapitated +/mob/living/carbon/update_inv_hands() + remove_overlay(HANDS_LAYER) + if (handcuffed) + drop_all_held_items() return - if(HAS_TRAIT(src, TRAIT_HUSK)) + var/list/hands = list() + for(var/obj/item/I in held_items) + if(client && hud_used && hud_used.hud_version != HUD_STYLE_NOHUD) + I.screen_loc = ui_hand_position(get_held_index_of_item(I)) + client.screen += I + if(observers && observers.len) + for(var/M in observers) + var/mob/dead/observe = M + if(observe.client && observe.client.eye == src) + observe.client.screen += I + else + observers -= observe + if(!observers.len) + observers = null + break + + var/t_state = I.item_state + if(!t_state) + t_state = I.icon_state + + var/icon_file = I.lefthand_file + if(get_held_index_of_item(I) % 2 == 0) + icon_file = I.righthand_file + + hands += I.build_worn_icon(state = t_state, default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) + + overlays_standing[HANDS_LAYER] = hands + apply_overlay(HANDS_LAYER) + + +/mob/living/carbon/update_fire(var/fire_icon = "Generic_mob_burning") + remove_overlay(FIRE_LAYER) + if(on_fire) + var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER) + new_fire_overlay.appearance_flags = RESET_COLOR + overlays_standing[FIRE_LAYER] = new_fire_overlay + + apply_overlay(FIRE_LAYER) + + + +/mob/living/carbon/update_damage_overlays() + remove_overlay(DAMAGE_LAYER) + + var/mutable_appearance/damage_overlay = mutable_appearance('icons/mob/dam_mob.dmi', "blank", -DAMAGE_LAYER) + overlays_standing[DAMAGE_LAYER] = damage_overlay + + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + if(BP.dmg_overlay_type) + if(BP.brutestate) + damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_[BP.brutestate]0") //we're adding icon_states of the base image as overlays + if(BP.burnstate) + damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_0[BP.burnstate]") + + apply_overlay(DAMAGE_LAYER) + + +/mob/living/carbon/update_inv_wear_mask() + remove_overlay(FACEMASK_LAYER) + + if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated return - var/hair_hidden = 0 + if(client && hud_used && hud_used.inv_slots[SLOT_WEAR_MASK]) + var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_WEAR_MASK] + inv.update_icon() + + if(wear_mask) + if(!(head && (head.flags_inv & HIDEMASK))) + overlays_standing[FACEMASK_LAYER] = wear_mask.build_worn_icon(state = wear_mask.icon_state, default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/mask.dmi') + update_hud_wear_mask(wear_mask) + + apply_overlay(FACEMASK_LAYER) + +/mob/living/carbon/update_inv_neck() + remove_overlay(NECK_LAYER) + + if(client && hud_used && hud_used.inv_slots[SLOT_NECK]) + var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_NECK] + inv.update_icon() + + if(wear_neck) + if(!(head && (head.flags_inv & HIDENECK))) + overlays_standing[NECK_LAYER] = wear_neck.build_worn_icon(state = wear_neck.icon_state, default_layer = NECK_LAYER, default_icon_file = 'icons/mob/neck.dmi') + update_hud_neck(wear_neck) + + apply_overlay(NECK_LAYER) + +/mob/living/carbon/update_inv_back() + remove_overlay(BACK_LAYER) + + if(client && hud_used && hud_used.inv_slots[SLOT_BACK]) + var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_BACK] + inv.update_icon() + + if(back) + overlays_standing[BACK_LAYER] = back.build_worn_icon(state = back.icon_state, default_layer = BACK_LAYER, default_icon_file = 'icons/mob/back.dmi') + update_hud_back(back) + + apply_overlay(BACK_LAYER) + +/mob/living/carbon/update_inv_head() + remove_overlay(HEAD_LAYER) + + if(!get_bodypart(BODY_ZONE_HEAD)) //Decapitated + return + + if(client && hud_used && hud_used.inv_slots[SLOT_BACK]) + var/obj/screen/inventory/inv = hud_used.inv_slots[SLOT_HEAD] + inv.update_icon() if(head) - var/obj/item/I = head - if(I.flags_inv & HIDEHAIR) - hair_hidden = 1 - if(wear_mask) - var/obj/item/clothing/mask/M = wear_mask - if(M.flags_inv & HIDEHAIR) - hair_hidden = 1 - if(!hair_hidden) - if(!getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain - overlays_standing[HAIR_LAYER] = mutable_appearance('icons/mob/human_face.dmi', "debrained", -HAIR_LAYER) - apply_overlay(HAIR_LAYER) + overlays_standing[HEAD_LAYER] = head.build_worn_icon(state = head.icon_state, default_layer = HEAD_LAYER, default_icon_file = 'icons/mob/head.dmi') + update_hud_head(head) + + apply_overlay(HEAD_LAYER) -/mob/living/carbon/monkey/update_fire() - ..("Monkey_burning") +/mob/living/carbon/update_inv_handcuffed() + remove_overlay(HANDCUFF_LAYER) + if(handcuffed) + var/mutable_appearance/cuffs = mutable_appearance('icons/mob/restraints.dmi', handcuffed.item_state, -HANDCUFF_LAYER) + cuffs.color = handcuffed.color -/mob/living/carbon/monkey/update_inv_legcuffed() + overlays_standing[HANDCUFF_LAYER] = cuffs + apply_overlay(HANDCUFF_LAYER) + +/mob/living/carbon/update_inv_legcuffed() remove_overlay(LEGCUFF_LAYER) clear_alert("legcuffed") if(legcuffed) var/mutable_appearance/legcuffs = mutable_appearance('icons/mob/restraints.dmi', legcuffed.item_state, -LEGCUFF_LAYER) - legcuffs.color = handcuffed.color - legcuffs.pixel_y = 8 + legcuffs.color = legcuffed.color overlays_standing[HANDCUFF_LAYER] = legcuffs apply_overlay(LEGCUFF_LAYER) throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) -//monkey HUD updates for items in our inventory +//mob HUD updates for items in our inventory + +//update whether handcuffs appears on our hud. +/mob/living/carbon/proc/update_hud_handcuffed() + if(hud_used) + for(var/hand in hud_used.hand_slots) + var/obj/screen/inventory/hand/H = hud_used.hand_slots[hand] + if(H) + H.update_icon() //update whether our head item appears on our hud. -/mob/living/carbon/monkey/update_hud_head(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - I.screen_loc = ui_monkey_head - client.screen += I +/mob/living/carbon/proc/update_hud_head(obj/item/I) + return //update whether our mask item appears on our hud. -/mob/living/carbon/monkey/update_hud_wear_mask(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - I.screen_loc = ui_monkey_mask - client.screen += I +/mob/living/carbon/proc/update_hud_wear_mask(obj/item/I) + return //update whether our neck item appears on our hud. -/mob/living/carbon/monkey/update_hud_neck(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - I.screen_loc = ui_monkey_neck - client.screen += I +/mob/living/carbon/proc/update_hud_neck(obj/item/I) + return //update whether our back item appears on our hud. -/mob/living/carbon/monkey/update_hud_back(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - I.screen_loc = ui_monkey_back - client.screen += I \ No newline at end of file +/mob/living/carbon/proc/update_hud_back(obj/item/I) + return + + + +//Overlays for the worn overlay so you can overlay while you overlay +//eg: ammo counters, primed grenade flashing, etc. +//"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file +/obj/item/proc/worn_overlays(isinhands = FALSE, icon_file) + . = list() + + +/mob/living/carbon/update_body() + update_body_parts() + +/mob/living/carbon/proc/update_body_parts() + //CHECK FOR UPDATE + var/oldkey = icon_render_key + icon_render_key = generate_icon_render_key() + if(oldkey == icon_render_key) + return + + remove_overlay(BODYPARTS_LAYER) + + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + BP.update_limb() + + //LOAD ICONS + if(limb_icon_cache[icon_render_key]) + load_limb_from_cache() + return + + //GENERATE NEW LIMBS + var/list/new_limbs = list() + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + new_limbs += BP.get_limb_icon() + if(new_limbs.len) + overlays_standing[BODYPARTS_LAYER] = new_limbs + limb_icon_cache[icon_render_key] = new_limbs + + apply_overlay(BODYPARTS_LAYER) + update_damage_overlays() + + + +///////////////////// +// Limb Icon Cache // +///////////////////// +/* + Called from update_body_parts() these procs handle the limb icon cache. + the limb icon cache adds an icon_render_key to a human mob, it represents: + - skin_tone (if applicable) + - gender + - limbs (stores as the limb name and whether it is removed/fine, organic/robotic) + These procs only store limbs as to increase the number of matching icon_render_keys + This cache exists because drawing 6/7 icons for humans constantly is quite a waste + See RemieRichards on irc.rizon.net #coderbus +*/ + +//produces a key based on the mob's limbs + +/mob/living/carbon/proc/generate_icon_render_key() + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + . += "-[BP.body_zone]" + if(BP.use_digitigrade) + . += "-digitigrade[BP.use_digitigrade]" + if(BP.animal_origin) + . += "-[BP.animal_origin]" + if(BP.status == BODYPART_ORGANIC) + . += "-organic" + else + . += "-robotic" + + if(HAS_TRAIT(src, TRAIT_HUSK)) + . += "-husk" + + +//change the mob's icon to the one matching its key +/mob/living/carbon/proc/load_limb_from_cache() + if(limb_icon_cache[icon_render_key]) + remove_overlay(BODYPARTS_LAYER) + overlays_standing[BODYPARTS_LAYER] = limb_icon_cache[icon_render_key] + apply_overlay(BODYPARTS_LAYER) + update_damage_overlays() diff --git a/code/modules/mob/living/inhand_holder.dm b/code/modules/mob/living/inhand_holder.dm index b82e8cc4..353a4f4f 100644 --- a/code/modules/mob/living/inhand_holder.dm +++ b/code/modules/mob/living/inhand_holder.dm @@ -5,80 +5,128 @@ desc = "Yell at coderbrush." icon = null icon_state = "" - item_flags = DROPDEL var/mob/living/held_mob - var/can_head = TRUE - var/destroying = FALSE + var/can_head = FALSE + w_class = WEIGHT_CLASS_BULKY -/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/M, _worn_state, head_icon, lh_icon, rh_icon, _can_head = TRUE) +/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/M, _worn_state, alt_worn, lh_icon, rh_icon, _can_head_override = FALSE) . = ..() - can_head = _can_head - if(head_icon) - alternate_worn_icon = head_icon + + if(M) + M.setDir(SOUTH) + held_mob = M + M.forceMove(src) + appearance = M.appearance + name = M.name + desc = M.desc + + if(_can_head_override) + can_head = _can_head_override + if(alt_worn) + alternate_worn_icon = alt_worn if(_worn_state) item_state = _worn_state + icon_state = _worn_state if(lh_icon) lefthand_file = lh_icon if(rh_icon) righthand_file = rh_icon if(!can_head) slot_flags = NONE - deposit(M) /obj/item/clothing/head/mob_holder/Destroy() - destroying = TRUE if(held_mob) - release(FALSE) + release() return ..() -/obj/item/clothing/head/mob_holder/proc/deposit(mob/living/L) - if(!istype(L)) - return FALSE - L.setDir(SOUTH) - update_visuals(L) - held_mob = L - L.forceMove(src) - name = L.name - desc = L.desc - return TRUE +/obj/item/clothing/head/mob_holder/dropped() + ..() + if(isturf(loc))//don't release on soft-drops + release() -/obj/item/clothing/head/mob_holder/proc/update_visuals(mob/living/L) - appearance = L.appearance - -/obj/item/clothing/head/mob_holder/proc/release(del_on_release = TRUE) - if(!held_mob) - if(del_on_release && !destroying) - qdel(src) - return FALSE +/obj/item/clothing/head/mob_holder/proc/release() if(isliving(loc)) var/mob/living/L = loc - to_chat(L, "[held_mob] wriggles free!") L.dropItemToGround(src) - held_mob.forceMove(get_turf(held_mob)) - held_mob.reset_perspective() - held_mob.setDir(SOUTH) - held_mob.visible_message("[held_mob] uncurls!") - held_mob = null - if(del_on_release && !destroying) - qdel(src) - return TRUE + if(held_mob) + var/mob/living/m = held_mob + m.forceMove(get_turf(m)) + m.reset_perspective() + m.setDir(SOUTH) + held_mob = null + qdel(src) /obj/item/clothing/head/mob_holder/relaymove(mob/user) - release() + return /obj/item/clothing/head/mob_holder/container_resist() + if(isliving(loc)) + var/mob/living/L = loc + visible_message("[src] escapes [L]!") release() -/obj/item/clothing/head/mob_holder/drone/deposit(mob/living/L) - . = ..() - if(!isdrone(L)) - qdel(src) - name = "drone (hiding)" - desc = "This drone is scared and has curled up into a ball!" +/mob/living/proc/mob_pickup(mob/living/L) + var/obj/item/clothing/head/mob_holder/holder = generate_mob_holder() + if(!holder) + return + drop_all_held_items() + L.put_in_hands(holder) + return -/obj/item/clothing/head/mob_holder/drone/update_visuals(mob/living/L) - var/mob/living/simple_animal/drone/D = L - if(!D) - return ..() - icon = 'icons/mob/drone.dmi' - icon_state = "[D.visualAppearence]_hat" +/mob/living/proc/mob_try_pickup(mob/living/user) + if(!ishuman(user) || !src.Adjacent(user) || user.incapacitated() || !can_be_held || (abs(get_effective_size()/get_effective_size(user)) >= 2)) + return FALSE + if(user.get_active_held_item()) + to_chat(user, "Your hands are full!") + return FALSE + if(buckled) + to_chat(user, "[src] is buckled to something!") + return FALSE + if(src == user) + to_chat(user, "You can't pick yourself up.") + return FALSE + visible_message("[user] starts picking up [src].", \ + "[user] starts picking you up!") + if(!do_after(user, 20, target = src)) + return FALSE + + if(user.get_active_held_item()||buckled) + return FALSE + + visible_message("[user] picks up [src]!", \ + "[user] picks you up!") + to_chat(user, "You pick [src] up.") + mob_pickup(user) + return TRUE + +/mob/living/AltClick(mob/user) + . = ..() + if(mob_try_pickup(user)) + return TRUE + + +// I didn't define these for mobs, because you shouldn't be able to breathe out of mobs using their loc isn't always the logical thing to do. +/* +/obj/item/clothing/head/mob_holder/assume_air(datum/gas_mixture/env) + var/atom/location = loc + if(!loc) + return //null + var/turf/T = get_turf(loc) + while(location != T) + location = location.loc + if(ismob(location)) + return location.loc.assume_air(env) + return loc.assume_air(env) + +/obj/item/clothing/head/mob_holder/remove_air(amount) + var/atom/location = loc + if(!loc) + return //null + var/turf/T = get_turf(loc) + while(location != T) + location = location.loc + if(ismob(location)) + return location.loc.remove_air() + return loc.remove_air(amount) +*/ +//Turned off for sizecode to work right now \ No newline at end of file diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f311649d..70317492 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -40,6 +40,12 @@ QDEL_LIST(diseases) return ..() + +/mob/living/proc/generate_mob_holder() + var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, (istext(can_be_held) ? can_be_held : ""), 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi') + return holder + + /mob/living/proc/OpenCraftingMenu() return @@ -136,6 +142,11 @@ else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP)) mob_swap = 1 if(mob_swap) + + //handle micro bumping on help intent + if(handle_micro_bump_helping(M)) + return 1 + //switch our position with M if(loc && !loc.Adjacent(M.loc)) return 1 @@ -168,6 +179,11 @@ //not if he's not CANPUSH of course if(!(M.status_flags & CANPUSH)) return 1 + + //handle micro bumping on other intents + if(handle_micro_bump_other(M)) + return 1 + if(isliving(M)) var/mob/living/L = M if(HAS_TRAIT(L, TRAIT_PUSHIMMUNE)) @@ -281,7 +297,11 @@ var/datum/disease/D = thing if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) ContactContractDisease(D) -p + + if(iscarbon(L)) + var/mob/living/carbon/C = L + if(HAS_TRAIT(src, TRAIT_STRONG_GRABBER)) + C.grippedby(src) //mob verbs are a lot faster than object verbs //for more info on why this is not atom/pull, see examinate() in mob.dm @@ -1124,23 +1144,6 @@ p if(can_be_held) mob_try_pickup(over) -/mob/living/proc/mob_pickup(mob/living/L) - return - -/mob/living/proc/mob_try_pickup(mob/living/user) - if(!ishuman(user)) - return - if(user.get_active_held_item()) - to_chat(user, "Your hands are full!") - return FALSE - if(buckled) - to_chat(user, "[src] is buckled to something!") - return FALSE - user.visible_message("[user] starts trying to scoop up [src]!") - if(!do_after(user, 20, target = src)) - return FALSE - mob_pickup(user) - return TRUE /mob/living/proc/get_static_viruses() //used when creating blood and other infective objects if(!LAZYLEN(diseases)) @@ -1236,3 +1239,10 @@ p update_canmove() for(var/chem in healing_chems) reagents.add_reagent(chem, healing_chems[chem]) + +//retard edits below +/mob/living + var/size_multiplier = 1 //multiplier for the mob's icon size atm + var/previous_size = 1 + var/holder_default +// can_be_held = "micro" \ No newline at end of file diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index b6e2c16a..3740827b 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -108,7 +108,7 @@ var/list/obj/effect/proc_holder/abilities = list() - var/can_be_held = FALSE //whether this can be picked up and held. + var/can_be_held = TRUE //whether this can be picked up and held. Currently needs to be on for sizecode to work, fix later. var/radiation = 0 //If the mob is irradiated. var/ventcrawl_layer = PIPING_LAYER_DEFAULT diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 7df851ec..f2047435 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -31,6 +31,7 @@ var/mob/living/simple_animal/mouse/movement_target gold_core_spawnable = FRIENDLY_SPAWN collar_type = "cat" + can_be_held = "cat2" do_footstep = TRUE @@ -80,6 +81,7 @@ pass_flags = PASSMOB mob_size = MOB_SIZE_SMALL collar_type = "kitten" + can_be_held = "cat" //RUNTIME IS ALIVE! SQUEEEEEEEE~ /mob/living/simple_animal/pet/cat/Runtime @@ -262,6 +264,7 @@ attacked_sound = 'sound/items/eatfood.ogg' deathmessage = "loses its false life and collapses!" death_sound = "bodyfall" + can_be_held = "cak" /mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) ..() diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index c194233c..9760b066 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -15,6 +15,7 @@ turns_per_move = 10 do_footstep = TRUE + can_be_held = TRUE //Corgis and pugs are now under one dog subtype @@ -35,6 +36,7 @@ var/obj/item/inventory_back var/shaved = FALSE var/nofur = FALSE //Corgis that have risen past the material plane of existence. + can_be_held = "corgi" /mob/living/simple_animal/pet/dog/corgi/Destroy() QDEL_NULL(inventory_head) @@ -63,6 +65,7 @@ butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/pug = 3) gold_core_spawnable = FRIENDLY_SPAWN collar_type = "pug" + can_be_held = "pug" /mob/living/simple_animal/pet/dog/corgi/exoticcorgi name = "Exotic Corgi" @@ -360,6 +363,7 @@ icon_dead = "old_corgi_dead" desc = "At a ripe old age of [record_age] Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP turns_per_move = 20 + can_be_held = "old_corgi" /mob/living/simple_animal/pet/dog/corgi/Ian/Life() if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) @@ -582,6 +586,7 @@ unsuitable_atmos_damage = 0 minbodytemp = TCMB maxbodytemp = T0C + 40 + can_be_held = "void_puppy" /mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = 0) return 1 //Void puppies can navigate space. @@ -603,6 +608,7 @@ response_harm = "kicks" var/turns_since_scan = 0 var/puppies = 0 + can_be_held = "lisa" //Lisa already has a cute bow! /mob/living/simple_animal/pet/dog/corgi/Lisa/Topic(href, href_list) diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 6d312147..f880adb7 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -274,3 +274,7 @@ /mob/living/simple_animal/drone/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0, illusion = 0, stun = TRUE) return 0 //So they don't die trying to fix wiring + +/mob/living/simple_animal/drone/generate_mob_holder() + var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "[visualAppearence]_hat", null, null, null, TRUE) + return holder diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm index 33031fd8..e40eb585 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm @@ -29,28 +29,11 @@ if("Nothing") return -//ATTACK HAND IGNORING PARENT RETURN VALUE +//picky up the drone c: /mob/living/simple_animal/drone/attack_hand(mob/user) - if(ishuman(user)) - if(stat == DEAD || status_flags & GODMODE || !can_be_held) - ..() - return - if(user.get_active_held_item()) - to_chat(user, "Your hands are full!") - return - visible_message("[user] starts picking up [src].", \ - "[user] starts picking you up!") - if(!do_after(user, 20, target = src)) - return - visible_message("[user] picks up [src]!", \ - "[user] picks you up!") - if(buckled) - to_chat(user, "[src] is buckled to [buckled] and cannot be picked up!") - return - to_chat(user, "You pick [src] up.") - drop_all_held_items() - var/obj/item/clothing/head/mob_holder/drone/DH = new(get_turf(src), src) - user.put_in_hands(DH) + ..() + if(user.a_intent == INTENT_HELP) + mob_try_pickup(user) /mob/living/simple_animal/drone/proc/try_reactivate(mob/living/user) var/mob/dead/observer/G = get_ghost() diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index 28b66c26..3f58ca59 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -18,7 +18,7 @@ response_disarm = "gently pushes aside" response_harm = "kicks" gold_core_spawnable = FRIENDLY_SPAWN - + can_be_held = "fox" do_footstep = TRUE //Captain fox diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index e329dd3f..a7c56cc7 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -23,6 +23,7 @@ obj_damage = 0 environment_smash = ENVIRONMENT_SMASH_NONE var/static/list/edibles = typecacheof(list(/mob/living/simple_animal/butterfly, /mob/living/simple_animal/cockroach)) //list of atoms, however turfs won't affect AI, but will affect consumption. + can_be_held = "lizard" /mob/living/simple_animal/hostile/lizard/CanAttack(atom/the_target)//Can we actually attack a possible target? if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it @@ -39,3 +40,7 @@ return TRUE else return ..() + +/mob/living/simple_animal/hostile/lizard/generate_mob_holder() + var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, "lizard", 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi', TRUE) + return holder \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index bf45c9cc..47e8f4d0 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -25,6 +25,7 @@ var/body_color //brown, gray and white, leave blank for random gold_core_spawnable = FRIENDLY_SPAWN var/chew_probability = 1 + can_be_held = TRUE /mob/living/simple_animal/mouse/Initialize() . = ..() @@ -34,7 +35,7 @@ icon_state = "mouse_[body_color]" icon_living = "mouse_[body_color]" icon_dead = "mouse_[body_color]_dead" - + can_be_held = "mouse_[body_color]" /mob/living/simple_animal/mouse/proc/splat() src.health = 0 @@ -87,14 +88,17 @@ /mob/living/simple_animal/mouse/white body_color = "white" icon_state = "mouse_white" + can_be_held = "mouse_white" /mob/living/simple_animal/mouse/gray body_color = "gray" icon_state = "mouse_gray" + can_be_held = "mouse_gray" /mob/living/simple_animal/mouse/brown body_color = "brown" icon_state = "mouse_brown" + can_be_held = "mouse_brown" //TOM IS ALIVE! SQUEEEEEEEE~K :) /mob/living/simple_animal/mouse/brown/Tom @@ -118,3 +122,8 @@ /obj/item/reagent_containers/food/snacks/deadmouse/on_grind() reagents.clear_reagents() + +/mob/living/simple_animal/mouse/generate_mob_holder() + var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, (istext(can_be_held) ? can_be_held : ""), 'icons/mob/animals_held.dmi', 'icons/mob/animals_held_lh.dmi', 'icons/mob/animals_held_rh.dmi') + holder.w_class = WEIGHT_CLASS_TINY + return holder \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index 324fa107..175cb8e8 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -22,7 +22,7 @@ maxHealth = 50 speed = 10 glide_size = 2 - + can_be_held = "sloth" //finally oranges can be held do_footstep = TRUE diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 871fd32b..1d152f72 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -102,9 +102,17 @@ return ..() /obj/machinery/disposal/proc/place_item_in_disposal(obj/item/I, mob/user) - I.forceMove(src) - user.visible_message("[user.name] places \the [I] into \the [src].", "You place \the [I] into \the [src].") - + if(istype(I, /obj/item/clothing/head/mob_holder)) + var/obj/item/clothing/head/mob_holder/H = I + var/mob/living/m = H.held_mob + H.release() + if(m) + user.start_pulling(m, 1) + stuff_mob_in(m,user) + return//you don't want this going into disposals ever + if(user.temporarilyRemoveItemFromInventory(I)) //double-checks never hurt + I.forceMove(src) + user.visible_message("[user.name] places \the [I] into \the [src].", "You place \the [I] into \the [src].") //mouse drop another mob or self /obj/machinery/disposal/MouseDrop_T(mob/living/target, mob/living/user) if(istype(target)) diff --git a/hyperstation/code/modules/resize/holder_micro.dm b/hyperstation/code/modules/resize/holder_micro.dm new file mode 100644 index 00000000..6f3d7806 --- /dev/null +++ b/hyperstation/code/modules/resize/holder_micro.dm @@ -0,0 +1,8 @@ +// Micro Holders - Extends /obj/item/holder + +/obj/item/clothing/head/mob_holder/micro + name = "micro" + desc = "Another person, small enough to fit in your hand." + icon = null // I forgot how to fix this. + icon_state = "" + slot_flags = ITEM_SLOT_FEET | ITEM_SLOT_HEAD | ITEM_SLOT_ID \ No newline at end of file diff --git a/hyperstation/code/modules/resize/resizing.dm b/hyperstation/code/modules/resize/resizing.dm new file mode 100644 index 00000000..0fc6a9dc --- /dev/null +++ b/hyperstation/code/modules/resize/resizing.dm @@ -0,0 +1,160 @@ +//LETS GET THIS FUCKING SIZECONTENT GAMERS +var/const/RESIZE_MACRO = 6 +var/const/RESIZE_HUGE = 4 +var/const/RESIZE_BIG = 2 +var/const/RESIZE_NORMAL = 1 +var/const/RESIZE_SMALL = 0.75 +var/const/RESIZE_TINY = 0.50 +var/const/RESIZE_MICRO = 0.25 + +//averages +var/const/RESIZE_A_MACROHUGE = (RESIZE_MACRO + RESIZE_HUGE) / 2 +var/const/RESIZE_A_HUGEBIG = (RESIZE_HUGE + RESIZE_BIG) / 2 +var/const/RESIZE_A_BIGNORMAL = (RESIZE_BIG + RESIZE_NORMAL) / 2 +var/const/RESIZE_A_NORMALSMALL = (RESIZE_NORMAL + RESIZE_SMALL) / 2 +var/const/RESIZE_A_SMALLTINY = (RESIZE_SMALL + RESIZE_TINY) / 2 +var/const/RESIZE_A_TINYMICRO = (RESIZE_TINY + RESIZE_MICRO) / 2 + +//Scale up a mob's icon by the size_multiplier +/mob/living/update_transform() + ASSERT(!iscarbon(src)) //the source isnt carbon should always be true + var/matrix/M = matrix() //matrix (M) variable + M.Scale(size_multiplier) + M.Translate(0, 16*(size_multiplier-1)) //translate by 16 * size_multiplier - 1 on Y axis + src.transform = M //the source of transform is M + + +/mob/proc/get_effective_size() + return 100000 + +mob/living/get_effective_size() + return src.size_multiplier + +/mob/living/proc/resize(var/new_size, var/animate = TRUE) + if(size_multiplier == new_size) + return 1 + + size_multiplier = new_size //Change size_multiplier so that other items can interact with them + src.update_transform() //WORK DAMN YOU + +//handle the big steppy, except nice +/mob/living/proc/handle_micro_bump_helping(var/mob/living/tmob) + + //Both small. + if(src.get_effective_size() <= RESIZE_A_SMALLTINY && tmob.get_effective_size() <= RESIZE_A_SMALLTINY) + now_pushing = 0 + src.forceMove(tmob.loc) + return 1 + + //Doing messages + if(abs(get_effective_size()/tmob.get_effective_size()) >= 2) //if the initiator is twice the size of the micro + now_pushing = 0 + src.forceMove(tmob.loc) + + //Smaller person being stepped on + if(get_effective_size() > tmob.get_effective_size() && iscarbon(src)) + if(istype(tmob) && istype(tmob, /datum/sprite_accessory/taur/naga)) + to_chat(src,"You carefully slither around [tmob].") + to_chat(tmob,"[src]'s huge tail slithers beside you!") + else + to_chat(src,"You carefully step over [tmob].") + to_chat(tmob,"[src] steps over you carefully!") + return 1 + + //Smaller person stepping under a larger person + if(tmob.get_effective_size() > get_effective_size()) + micro_step_under(tmob) + src.forceMove(tmob.loc) + now_pushing = 0 + return 1 + +//Stepping on disarm intent +/mob/living/proc/handle_micro_bump_other(var/mob/living/tmob) + ASSERT(isliving(tmob)) + + //Both small + if(src.get_effective_size() <= RESIZE_A_SMALLTINY && tmob.get_effective_size() <= RESIZE_A_SMALLTINY) + now_pushing = 0 + src.forceMove(tmob.loc) + return 1 + + if(abs(get_effective_size()/tmob.get_effective_size()) >= 2) + if(src.a_intent == "disarm" && src.canmove && !src.buckled) + now_pushing = 0 + src.forceMove(tmob.loc) + if(get_effective_size() > tmob.get_effective_size() && iscarbon(src)) + if(istype(tmob) && istype(tmob, /datum/sprite_accessory/taur/naga)) + to_chat(src,"You carefully roll over [tmob] with your tail!") + to_chat(tmob,"[src]'s huge tail rolls over you!") + sizediffStamLoss(tmob) + else + to_chat(src,"You painfully but harmlessly step on [tmob]!") + to_chat(tmob,"[src] steps onto you with force!") + sizediffStamLoss(tmob) + return 1 + + if(src.a_intent == "harm" && src.canmove && !src.buckled) + now_pushing = 0 + src.forceMove(tmob.loc) + if(get_effective_size() > tmob.get_effective_size() && iscarbon(src)) + if(istype(tmob) && istype(tmob, /datum/sprite_accessory/taur/naga)) + to_chat(src,"You grind [tmob] into the floor with your tail!") + to_chat(tmob,"[src]'s massive tail plows you into the floor!") + sizediffStamLoss(tmob) + sizediffBruteloss(tmob) + else + to_chat(src,"You pound [tmob] into the floor underfoot!") + to_chat(tmob,"[src] slams you into the ground, crushing you!") + sizediffStamLoss(tmob) + sizediffBruteloss(tmob) + return 1 + +// if(src.a_inent == "grab"... goes here + + if(tmob.get_effective_size() > get_effective_size()) + micro_step_under(tmob) + src.forceMove(tmob.loc) + now_pushing = 0 + return 1 + +//smaller person stepping under another person +/mob/living/proc/micro_step_under(var/mob/living/tmob) + if(istype(tmob) && istype(tmob, /datum/sprite_accessory/taur/naga)) + to_chat(src,"You jump over [tmob]'s thick tail.") + to_chat(tmob,"[src] bounds over your tail.") + else + to_chat(src,"You run between [tmob]'s legs.") + to_chat(tmob,"[src] runs between your legs.") + +//Proc for scaling stamina damage on size difference +/mob/living/proc/sizediffStamLoss(var/mob/living/tmob) + var/S = (get_effective_size()/tmob.get_effective_size()*25) //macro divided by micro, times 25 + tmob.Knockdown(S) //final result in stamina knockdown + +//Proc for scaling brute damage on size difference +/mob/living/proc/sizediffBruteloss(var/mob/living/tmob) + var/B = (get_effective_size()/tmob.get_effective_size()*2) //macro divided by micro, times 2 + tmob.adjustBruteLoss(B) //final result in brute loss + +//Proc for picking up people. WIP. +/* +/mob/living/proc/attempt_to_scoop(var/mob/living/M) + var/sizediv = get_effective_size()/M.get_effective_size() + if(!holder_default && holder_type) + holder_default = holder_type + if(!istype(M)) + return 0 + if(buckled) + to_chat(src,"You have to unbuckle them before you can pick them up.") + return 0 + if(sizediv >= 2) + holder_type = /obj/item/holder/micro + var/obj/item/holder/m_holder = get_scooped(M) + holder_type = holder_default + if (m_holder) + return 1 + else + return 0 +*/ + +//Clothes coming off at different sizes, and health/speed/stam changes as well diff --git a/hyperstation/code/modules/resize/sizechems.dm b/hyperstation/code/modules/resize/sizechems.dm new file mode 100644 index 00000000..90deb236 --- /dev/null +++ b/hyperstation/code/modules/resize/sizechems.dm @@ -0,0 +1,40 @@ +//Size Chemicals, now with better and less cringy names. + +/datum/reagent/dahl/growthchem + name = "Prospacillin" + id = "growthchem" + description = "A stabilized altercation of size-altering liquids, this one appears to increase cell volume." + color = "#E70C0C" + taste_description = "a sharp, fiery and intoxicating flavour." + overdose_threshold = 10 + metabolization_rate = 0.25 + can_synth = FALSE //DO NOT MAKE THIS SNYTHESIZABLE, THESE CHEMS ARE SUPPOSED TO NOT BE USED COMMONLY + +/datum/reagent/dahl/growthchem/on_mob_add(mob/living/carbon/M) + log_game("SIZECODE: [M] ckey: [M.key] has ingested growthchem.") + +/datum/reagent/dahl/growthchem/on_mob_life(mob/living/carbon/M) + if(M.size_multiplier < RESIZE_MACRO) + M.resize(M.size_multiplier+0.01) + + return + +/datum/reagent/dahl/shrinkchem + name = "Diminicillin" + id = "shrinkchem" + description = "A stabilized altercation of size-altering liquids, this one appears to decrease cell volume." + color = "#0C26E7" + taste_description = "a pungent, acidic and jittery flavour." + overdose_threshold = 10 + metabolization_rate = 0.25 + can_synth = FALSE //SAME STORY AS ABOVE + +/datum/reagent/dahl/shrinkchem/on_mob_add(mob/living/carbon/M) + log_game("SIZECODE: [M] ckey: [M.key] has ingested shrinkchem.") + +/datum/reagent/dahl/shrinkchem/on_mob_life(mob/living/carbon/M) + if(M.size_multiplier > RESIZE_MICRO) + M.resize(M.size_multiplier-0.01) + + return + diff --git a/hyperstation/code/modules/resize/sizegun.dm b/hyperstation/code/modules/resize/sizegun.dm new file mode 100644 index 00000000..d1154838 --- /dev/null +++ b/hyperstation/code/modules/resize/sizegun.dm @@ -0,0 +1,86 @@ +/obj/item/projectile/sizelaser + name = "sizeray laser" + icon_state = "omnilaser" + hitsound = null + damage = 5 + damage_type = STAMINA + flag = "laser" + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + +/obj/item/projectile/sizelaser/shrinkray + icon_state="bluelaser" + +/obj/item/projectile/sizelaser/growthray + icon_state="laser" + +/obj/item/projectile/sizelaser/shrinkray/on_hit(var/atom/target, var/blocked = 0) + if(istype(target, /mob/living/carbon/human)) + var/mob/living/M = target + switch(M.size_multiplier) + if(RESIZE_MACRO to INFINITY) + M.resize(RESIZE_HUGE) + if(RESIZE_HUGE to RESIZE_MACRO) + M.resize(RESIZE_BIG) + if(RESIZE_BIG to RESIZE_HUGE) + M.resize(RESIZE_NORMAL) + if(RESIZE_NORMAL to RESIZE_BIG) + M.resize(RESIZE_SMALL) + if(RESIZE_SMALL to RESIZE_NORMAL) + M.resize(RESIZE_TINY) + if(RESIZE_TINY to RESIZE_SMALL) + M.resize(RESIZE_MICRO) + if((0 - INFINITY) to RESIZE_NORMAL) + M.resize(RESIZE_MICRO) + M.update_transform() + return 1 + +/obj/item/projectile/sizelaser/growthray/on_hit(var/atom/target, var/blocked = 0 ) + if(istype(target, /mob/living/carbon/human)) + var/mob/living/M = target + switch(M.size_multiplier) + if(RESIZE_HUGE to RESIZE_MACRO) + M.resize(RESIZE_MACRO) + if(RESIZE_BIG to RESIZE_HUGE) + M.resize(RESIZE_HUGE) + if(RESIZE_NORMAL to RESIZE_BIG) + M.resize(RESIZE_BIG) + if(RESIZE_SMALL to RESIZE_NORMAL) + M.resize(RESIZE_NORMAL) + if(RESIZE_TINY to RESIZE_SMALL) + M.resize(RESIZE_SMALL) + if(RESIZE_MICRO to RESIZE_TINY) + M.resize(RESIZE_TINY) + if((0 - INFINITY) to RESIZE_MICRO) + M.resize(RESIZE_MICRO) + M.update_transform() + return 1 + +/obj/item/ammo_casing/energy/laser/growthray + projectile_type = /obj/item/projectile/sizelaser/growthray + select_name = "Growth" + +/obj/item/ammo_casing/energy/laser/shrinkray + projectile_type = /obj/item/projectile/sizelaser/shrinkray + select_name = "Shrink" + +//Gun +/obj/item/gun/energy/laser/sizeray + name = "size ray" + icon_state = "bluetag" + desc = "Debug size manipulator. You probably shouldn't have this!" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray, /obj/item/ammo_casing/energy/laser/growthray) + selfcharge = EGUN_SELFCHARGE + charge_delay = 5 + ammo_x_offset = 2 + clumsy_check = 1 + + attackby(obj/item/W, mob/user) + if(W==src) + if(icon_state=="bluetag") + icon_state="redtag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/growthray) + else + icon_state="bluetag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/shrinkray) + return ..() diff --git a/icons/mob/animals_held.dmi b/icons/mob/animals_held.dmi new file mode 100644 index 0000000000000000000000000000000000000000..82a065d93a562a9264ccee2022d6a4beed1ad5f2 GIT binary patch literal 675 zcmV;U0$lxxP)GC)ttc@! z6~s0~D9g#LN-RpjrA%4D)z1ZNJpg$hCCCy0*o^=H0i;PpK~z|U?Uui8!Y~lVomx=2 z>Of>sR#f@`4i-EBeE|?cU0X$BZdFxc??6UYD@*r2LCVqx=+H;1!@t~}Lr|(NmHdkX z`t19Bjw8-6wjl^Z1|d4Yqn6%vuZy(SK%dE8ix}kS7IjIjfe!j3YG~LCX+40lJ%7OQ ze0T3@vgx=9bT}ayhH887iREl6?~5hP1{@n`hCpi_0U3pGx-tS7pZjHXyj{cusWA;` zZCXJmSpt2YEU7^=uJmDa}3nO9|IoGuQ5kO8N!$kP}{`2acd(YO|$i9 zAurcy=G1qTAQhw-M2zb*`~?62002ov JPDHLkV1lkOAg2HT literal 0 HcmV?d00001 diff --git a/icons/mob/animals_held_lh.dmi b/icons/mob/animals_held_lh.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6c407c850bf37be8127f689001dfb64fb8a30237 GIT binary patch literal 10036 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 ngunN+p8WS0v;HqO&mXUmH>@J73FE