diff --git a/code/modules/food_and_drinks/food/snacks_burgers.dm b/code/modules/food_and_drinks/food/snacks_burgers.dm index 814875d2..f9169ca3 100644 --- a/code/modules/food_and_drinks/food/snacks_burgers.dm +++ b/code/modules/food_and_drinks/food/snacks_burgers.dm @@ -281,3 +281,11 @@ bonus_reagents = list("nutriment" = 8, "vitamin" = 1) tastes = list("bun" = 4, "bacon" = 2) foodtype = GRAIN | MEAT + +/obj/item/reagent_containers/food/snacks/burger/cumburger + name = "Cumburger" + desc = "A sticky and salty burger." + icon_state = "cumburger" + tastes = list("bun" = 4, "salt" = 5) + bonus_reagents = list("nutriment" = 3, "vitamin" = 10, "semen" = 10, "aphro" = 5) + foodtype = GRAIN \ No newline at end of file diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm index d7beebf4..733c6c48 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm @@ -176,7 +176,7 @@ ) result = /obj/item/reagent_containers/food/snacks/burger/purple subcategory = CAT_BURGER - + /datum/crafting_recipe/food/blackburger name = "Black burger" reqs = list( @@ -186,7 +186,7 @@ ) result = /obj/item/reagent_containers/food/snacks/burger/black subcategory = CAT_BURGER - + /datum/crafting_recipe/food/whiteburger name = "White burger" reqs = list( @@ -293,3 +293,12 @@ result = /obj/item/reagent_containers/food/snacks/burger/baconburger subcategory = CAT_BURGER + +/datum/crafting_recipe/food/burger + name = "Cumburger" + reqs = list( + /datum/reagent/consumable/semen = 10, + /obj/item/reagent_containers/food/snacks/bun = 1 + ) + result = /obj/item/reagent_containers/food/snacks/burger/cumburger + subcategory = CAT_BURGER \ No newline at end of file diff --git a/hyperstation/code/obj/leash.dm b/hyperstation/code/obj/leash.dm index a4f94c98..75ce8c00 100644 --- a/hyperstation/code/obj/leash.dm +++ b/hyperstation/code/obj/leash.dm @@ -1,403 +1,403 @@ -//Jay Sparrow -//TODO -/* -Icons, maybe? -*/ - -#define STATUS_EFFECT_LEASH_PET /datum/status_effect/leash_pet -#define STATUS_EFFECT_LEASH_DOM /datum/status_effect/leash_dom -#define STATUS_EFFECT_LEASH_FREEPET /datum/status_effect/leash_freepet -#define MOVESPEED_ID_LEASH "LEASH" - -/////STATUS EFFECTS///// -//These are mostly used as flags for the states each member can be in - -/datum/status_effect/leash_dom - status_type = STATUS_EFFECT_UNIQUE - alert_type = /obj/screen/alert/status_effect/leash_dom - -/obj/screen/alert/status_effect/leash_dom - name = "Leash Master" - desc = "You've got a leash, and a cute pet on the other end." - icon_state = "leash_master" //These call icons that don't exist, so no icon comes up. Which is good. - //As a result, the descriptions also don't proc, which is fine. - -/datum/status_effect/leash_freepet - status_type = STATUS_EFFECT_UNIQUE - alert_type = /obj/screen/alert/status_effect/leash_freepet - -/obj/screen/alert/status_effect/leash_freepet - name = "Escaped Pet" - desc = "You're on a leash, but you've no master. If anyone grabs the leash they'll gain control!" - icon_state = "leash_freepet" - - -/datum/status_effect/leash_pet - id = "leashed" - status_type = STATUS_EFFECT_UNIQUE - var/datum/weakref/redirect_component - alert_type = /obj/screen/alert/status_effect/leash_pet - -/obj/screen/alert/status_effect/leash_pet - name = "Leashed Pet" - desc = "You're on the hook now! Be good for your master." - icon_state = "leash_pet" - - -/datum/status_effect/leash_pet/on_apply() - redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist)))) - if(!owner.stat) - to_chat(owner, "You have been leashed!") - return ..() - -//This lets the pet resist their leash -/datum/status_effect/leash_pet/proc/owner_resist() - to_chat(owner, "You reach for the hook on your collar...") - //Determine how long it takes to remove the leash - var/deleash = 15 - //if(owner.get_item_by_slot(SLOT_HANDCUFFED)) //Commented out because there is no clear way to make this proc BEFORE decuff on resist. - //deleash = 100 - if(do_mob(owner, owner, deleash))//do_mob creates a progress bar and then enacts the code after. Owner, owner, because it's an act on themself - if(!QDELETED(src)) - to_chat(owner, "[owner] has removed their leash!") - owner.remove_status_effect(/datum/status_effect/leash_pet) - -///// OBJECT ///// -//The leash object itself -//The component variables are used for hooks, used later. - -/obj/item/leash - name = "leash" - desc = "A simple tether that can easily be hooked onto a collar. Perfect for your pet." - icon = 'hyperstation/icons/obj/leash.dmi' - icon_state = "leash" - item_state = "leash" - throw_range = 4 - slot_flags = ITEM_SLOT_BELT - force = 1 - throwforce = 1 - w_class = WEIGHT_CLASS_SMALL - var/leash_used = 0 //A flag to see if the leash has been used yet, because for some reason picking up an unused leash is weird - var/mob/living/leash_pet = "null" //Variable to store our pet later - var/mob/living/leash_master = "null" //And our master too - var/datum/component/mobhook_leash_pet - var/datum/component/mobhook_leash_master //Needed to watch for these entities to move - var/datum/component/mobhook_leash_freepet - var/leash_location[3] //Three digit list for us to store coordinates later - -//Called when someone is clicked with the leash -/obj/item/leash/attack(mob/living/carbon/C, mob/living/user) //C is the target, user is the one with the leash - if(istype(C.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/petcollar) || istype(C.get_item_by_slot(SLOT_NECK), /obj/item/electropack/shockcollar)) - var/leashtime = 50 - if(C.handcuffed) - leashtime = 5 - if(do_mob(user, C, leashtime)) //do_mob adds a progress bar, but then we also check to see if they have a collar - log_combat(user, C, "leashed", addition="playfully") - //TODO: Figure out how to make an easy breakout for leashed leash_pets - C.apply_status_effect(/datum/status_effect/leash_pet)//Has now been leashed - user.apply_status_effect(/datum/status_effect/leash_dom) //Is the leasher - leash_pet = C //Save pet reference for later - leash_master = user //Save dom reference for later - mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_pet_move))) - mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move))) - leash_used = 1 - if(!leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Add slowdown if the pet didn't leash themselves - leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5) - for(var/mob/viewing in viewers(user, null)) - if(viewing == leash_master) - to_chat(leash_master, "You have hooked a leash onto [leash_pet]!") - else - viewing.show_message("[leash_pet] has been leashed by [leash_master]!", 1) - if(leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Pet leashed themself. They are not the dom - leash_pet.apply_status_effect(/datum/status_effect/leash_freepet) - leash_pet.remove_status_effect(/datum/status_effect/leash_dom) - while(1) //While true loop. The mark of a genius coder. ##MAINLOOP START - sleep(2) //Check every other tick - if(leash_pet == "null") //No pet, break loop - return - if(!(leash_pet.get_item_by_slot(SLOT_NECK))) //The pet has slipped their collar and is not the pet anymore. - for(var/mob/viewing in viewers(user, null)) - viewing.show_message("[leash_pet] has slipped out of their collar!!", 1) - to_chat(leash_pet, "You have slipped out of your collar!") - to_chat(loc, "[leash_pet] has slipped out of their collar!") - leash_pet.remove_status_effect(/datum/status_effect/leash_pet) - - if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks. - QDEL_NULL(mobhook_leash_master) - QDEL_NULL(mobhook_leash_pet) - QDEL_NULL(mobhook_leash_freepet) - if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet)) - leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) - if(leash_pet.has_movespeed_modifier(MOVESPEED_ID_LEASH)) - leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) - if(!leash_master == "null") - leash_master.remove_status_effect(/datum/status_effect/leash_dom) - leash_used = 0 //reset the leash to neutral - leash_pet = "null" - return - - else //No collar, no fun - var/leash_message = pick("Your pet needs a collar") - to_chat(user, "[leash_message]") - -//Called when the leash is used in hand -//Tugs the pet closer -/obj/item/leash/attack_self(mob/living/user) - if(!leash_pet == "null") //No pet, no tug. - return - //Yank the pet. Yank em in close. - if(leash_pet.x > leash_master.x + 1) - step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. - if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master - step(leash_pet, SOUTH, 1) - if(leash_pet.y < leash_master.y) - step(leash_pet, NORTH, 1) - if(leash_pet.x < leash_master.x - 1) - step(leash_pet, EAST, 1) - if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master - step(leash_pet, SOUTH, 1) - if(leash_pet.y < leash_master.y) - step(leash_pet, NORTH, 1) - if(leash_pet.y > leash_master.y + 1) - step(leash_pet, SOUTH, 1) - if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master - step(leash_pet, WEST, 1) - if(leash_pet.x < leash_master.x) - step(leash_pet, EAST, 1) - if(leash_pet.y < leash_master.y - 1) - step(leash_pet, NORTH, 1) - if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master - step(leash_pet, WEST, 1) - if(leash_pet.x < leash_master.x) - step(leash_pet, EAST, 1) - -/obj/item/leash/proc/on_master_move() - //Make sure the dom still has a pet - if(leash_master == "null") //There must be a master - return - if(leash_pet == "null") //There must be a pet - return - if(leash_pet == leash_master) //Pet is the master - return - if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) - QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe - leash_master.remove_status_effect(/datum/status_effect/leash_dom) - return - - //If the master moves, pull the pet in behind - sleep(2) //A small sleep so the pet kind of bounces back after they make the step - //Also, the sleep means that the distance check for master happens before the pet, to prevent both from proccing. - - if(leash_master == "null") //Just to stop error messages - return - if(leash_pet == "null") - return - if(leash_pet.x > leash_master.x + 2) - step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. - if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master - step(leash_pet, SOUTH, 1) - if(leash_pet.y < leash_master.y) - step(leash_pet, NORTH, 1) - if(leash_pet.x < leash_master.x - 2) - step(leash_pet, EAST, 1) - if(leash_pet.y > leash_master.y) - step(leash_pet, SOUTH, 1) - if(leash_pet.y < leash_master.y) - step(leash_pet, NORTH, 1) - if(leash_pet.y > leash_master.y + 2) - step(leash_pet, SOUTH, 1) - if(leash_pet.x > leash_master.x) - step(leash_pet, WEST, 1) - if(leash_pet.x < leash_master.x) - step(leash_pet, EAST, 1) - if(leash_pet.y < leash_master.y - 2) - step(leash_pet, NORTH, 1) - if(leash_pet.x > leash_master.x) - step(leash_pet, WEST, 1) - if(leash_pet.x < leash_master.x) - step(leash_pet, EAST, 1) - - //Knock the pet over if they get further behind. Shouldn't happen too often. - sleep(3) //This way running normally won't just yank the pet to the ground. - if(leash_master == "null") //Just to stop error messages. Break the loop early if something removed the master - return - if(leash_pet == "null") - return - if(leash_pet.x > leash_master.x + 3 || leash_pet.x < leash_master.x - 3 || leash_pet.y > leash_master.y + 3 || leash_pet.y < leash_master.y - 3) - //var/leash_knockdown_message = "[leash_pet] got pulled to the ground by their leash!" - //to_chat(leash_master, "[leash_knockdown_message]") - //to_chat(leash_pet, "[leash_knockdown_message]") - leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) - - //This code is to check if the pet has gotten too far away, and then break the leash. - sleep(3) //Wait to snap the leash - if(leash_master == "null") //Just to stop error messages - return - if(leash_pet == "null") - return - if(leash_pet.x > leash_master.x + 5 || leash_pet.x < leash_master.x - 5 || leash_pet.y > leash_master.y + 5 || leash_pet.y < leash_master.y - 5) - var/leash_break_message = "The leash snapped free from [leash_pet]!" - for(var/mob/viewing in viewers(leash_pet, null)) - if(viewing == leash_master) - to_chat(leash_master, "The leash snapped free from your pet!") - if(viewing == leash_pet) - to_chat(leash_pet, "Your leash has popped from your collar!") - else - viewing.show_message("[leash_break_message]", 1) - leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) - leash_pet.adjustOxyLoss(5) - leash_pet.remove_status_effect(/datum/status_effect/leash_pet) - leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) - leash_master.remove_status_effect(/datum/status_effect/leash_dom) - QDEL_NULL(mobhook_leash_master) - QDEL_NULL(mobhook_leash_pet) - leash_pet = "null" - leash_master = "null" - leash_used = 0 - -/obj/item/leash/proc/on_pet_move() - //This should only work if there is a pet and a master. - //This is here pretty much just to stop the console from flooding with errors - if(leash_master == "null") - return - if(leash_pet == "null") - return - //Make sure the pet is still a pet - if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) - QDEL_NULL(mobhook_leash_pet) //Probably redundant, but it's nice to be safe - return - - //The pet has escaped. There is no DOM. GO PET RUN. - if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))//If the pet is free, break - return - - //If the pet gets too far away, they get tugged back - sleep(3)//A small sleep so the pet kind of bounces back after they make the step - if(leash_master == "null") - return - if(leash_pet == "null") - return - //West tug - if(leash_pet.x > leash_master.x + 2) - step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. - //East tug - if(leash_pet.x < leash_master.x - 2) - step(leash_pet, EAST, 1) - //South tug - if(leash_pet.y > leash_master.y + 2) - step(leash_pet, SOUTH, 1) - //North tug - if(leash_pet.y < leash_master.y - 2) - step(leash_pet, NORTH, 1) - -/obj/item/leash/proc/on_freepet_move() - . = ..() - //Pet is on the run. Let's drag the leash behind them. - if(!leash_master == "null") //If there is a master, don't do this - return - if(leash_pet == "null") //If there is no pet, don't do this - return - if(leash_pet.is_holding_item_of_type(/obj/item/leash)) //If the pet is holding the leash, don't do this - return - - sleep(2) - if(leash_pet == "null") - return - //Double move to catch the leash up to the pet - if(src.x > leash_pet.x + 2) - . = step(src, WEST, 1) - if(src.x < leash_pet.x - 2) - . = step(src, EAST, 1) - if(src.y > leash_pet.y + 2) - . = step(src, SOUTH, 1) - if(src.y < leash_pet.y - 2) - . = step(src, NORTH, 1) - //Primary dragging code - if(src.x > leash_pet.x + 1) - . = step(src, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. - if(src.y > leash_pet.y)//Check the other axis, and tug them into alignment so they are behind the pet - . = step(src, SOUTH, 1) - if(src.y < leash_pet.y) - . = step(src, NORTH, 1) - if(src.x < leash_pet.x - 1) - . = step(src, EAST, 1) - if(src.y > leash_pet.y) - . = step(src, SOUTH, 1) - if(src.y < leash_pet.y) - . = step(src, NORTH, 1) - if(src.y > leash_pet.y + 1) - . = step(src, SOUTH, 1) - if(src.x > leash_pet.x) - . = step(src, WEST, 1) - if(src.x < leash_pet.x) - . = step(src, EAST, 1) - if(src.y < leash_pet.y - 1) - . = step(src, NORTH, 1) - if(src.x > leash_pet.x) - . = step(src, WEST, 1) - if(src.x < leash_pet.x) - . = step(src, EAST, 1) - - sleep(1) - //Just to prevent error messages - if(leash_pet == "null") - return - if(src.x > leash_pet.x + 5 || src.x < leash_pet.x - 5 || src.y > leash_pet.y + 5 || src.y < leash_pet.y - 5) - var/leash_break_message = "The leash snapped free from [leash_pet]!" - for(var/mob/viewing in viewers(leash_pet, null)) - if(viewing == leash_pet) - to_chat(leash_pet, "Your leash has popped from your collar!") - else - viewing.show_message("[leash_break_message]", 1) - leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) - leash_pet.adjustOxyLoss(5) - leash_pet.remove_status_effect(/datum/status_effect/leash_pet) - leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) - QDEL_NULL(mobhook_leash_pet) - QDEL_NULL(mobhook_leash_freepet) - leash_pet = "null" - leash_used = 0 - -/obj/item/leash/dropped() //Drop the leash, and the leash effects stop - . = ..() - if(leash_pet == "null") //There is no pet. Stop this silliness - return - if(leash_master == "null") - return - //Dropping procs any time the leash changes slots. So, we will wait a tick and see if the leash was actually dropped - sleep(1) - if(leash_master.is_holding_item_of_type(/obj/item/leash) || istype(leash_master.get_item_by_slot(SLOT_BELT), /obj/item/leash)) - return //Dom still has the leash as it turns out. Cancel the proc. - for(var/mob/viewing in viewers(leash_master, null)) - viewing.show_message("[leash_master] has dropped the leash.", 1) - //DOM HAS DROPPED LEASH. PET IS FREE. SCP HAS BREACHED CONTAINMENT. - leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) - mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_freepet_move))) - leash_master.remove_status_effect(/datum/status_effect/leash_dom) //No dom with no leash. We will get a new dom if the leash is picked back up. - leash_master = "null" - QDEL_NULL(mobhook_leash_master) - -/obj/item/leash/equipped(mob/user) - . = ..() - if(leash_used == 0) //Don't apply statuses with a fresh leash. Keeps things clean on the backend. - return - sleep(2) - if(leash_pet == "null") - return - leash_master = user - if(leash_master.has_status_effect(/datum/status_effect/leash_freepet) || leash_master.has_status_effect(/datum/status_effect/leash_pet)) //Pet picked up their own leash. - leash_master = "null" - return - leash_master.apply_status_effect(/datum/status_effect/leash_dom) - mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move))) - leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) - QDEL_NULL(mobhook_leash_freepet) - leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5) - -/datum/crafting_recipe/leash - name = "Leash" - result = /obj/item/leash - time = 40 - reqs = list(/obj/item/stack/sheet/metal = 1, - /obj/item/stack/sheet/cloth = 3) +//Jay Sparrow +//TODO +/* +Icons, maybe? +*/ + +#define STATUS_EFFECT_LEASH_PET /datum/status_effect/leash_pet +#define STATUS_EFFECT_LEASH_DOM /datum/status_effect/leash_dom +#define STATUS_EFFECT_LEASH_FREEPET /datum/status_effect/leash_freepet +#define MOVESPEED_ID_LEASH "LEASH" + +/////STATUS EFFECTS///// +//These are mostly used as flags for the states each member can be in + +/datum/status_effect/leash_dom + status_type = STATUS_EFFECT_UNIQUE + alert_type = /obj/screen/alert/status_effect/leash_dom + +/obj/screen/alert/status_effect/leash_dom + name = "Leash Master" + desc = "You've got a leash, and a cute pet on the other end." + icon_state = "leash_master" //These call icons that don't exist, so no icon comes up. Which is good. + //As a result, the descriptions also don't proc, which is fine. + +/datum/status_effect/leash_freepet + status_type = STATUS_EFFECT_UNIQUE + alert_type = /obj/screen/alert/status_effect/leash_freepet + +/obj/screen/alert/status_effect/leash_freepet + name = "Escaped Pet" + desc = "You're on a leash, but you've no master. If anyone grabs the leash they'll gain control!" + icon_state = "leash_freepet" + + +/datum/status_effect/leash_pet + id = "leashed" + status_type = STATUS_EFFECT_UNIQUE + var/datum/weakref/redirect_component + alert_type = /obj/screen/alert/status_effect/leash_pet + +/obj/screen/alert/status_effect/leash_pet + name = "Leashed Pet" + desc = "You're on the hook now! Be good for your master." + icon_state = "leash_pet" + + +/datum/status_effect/leash_pet/on_apply() + redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist)))) + if(!owner.stat) + to_chat(owner, "You have been leashed!") + return ..() + +//This lets the pet resist their leash +/datum/status_effect/leash_pet/proc/owner_resist() + to_chat(owner, "You reach for the hook on your collar...") + //Determine how long it takes to remove the leash + var/deleash = 15 + //if(owner.get_item_by_slot(SLOT_HANDCUFFED)) //Commented out because there is no clear way to make this proc BEFORE decuff on resist. + //deleash = 100 + if(do_mob(owner, owner, deleash))//do_mob creates a progress bar and then enacts the code after. Owner, owner, because it's an act on themself + if(!QDELETED(src)) + to_chat(owner, "[owner] has removed their leash!") + owner.remove_status_effect(/datum/status_effect/leash_pet) + +///// OBJECT ///// +//The leash object itself +//The component variables are used for hooks, used later. + +/obj/item/leash + name = "leash" + desc = "A simple tether that can easily be hooked onto a collar. Perfect for your pet." + icon = 'hyperstation/icons/obj/leash.dmi' + icon_state = "leash" + item_state = "leash" + throw_range = 4 + slot_flags = ITEM_SLOT_BELT + force = 1 + throwforce = 1 + w_class = WEIGHT_CLASS_SMALL + var/leash_used = 0 //A flag to see if the leash has been used yet, because for some reason picking up an unused leash is weird + var/mob/living/leash_pet = "null" //Variable to store our pet later + var/mob/living/leash_master = "null" //And our master too + var/datum/component/mobhook_leash_pet + var/datum/component/mobhook_leash_master //Needed to watch for these entities to move + var/datum/component/mobhook_leash_freepet + var/leash_location[3] //Three digit list for us to store coordinates later + +//Called when someone is clicked with the leash +/obj/item/leash/attack(mob/living/carbon/C, mob/living/user) //C is the target, user is the one with the leash + if(istype(C.get_item_by_slot(SLOT_NECK), /obj/item/clothing/neck/petcollar) || istype(C.get_item_by_slot(SLOT_NECK), /obj/item/electropack/shockcollar)) + var/leashtime = 50 + if(C.handcuffed) + leashtime = 5 + if(do_mob(user, C, leashtime)) //do_mob adds a progress bar, but then we also check to see if they have a collar + log_combat(user, C, "leashed", addition="playfully") + //TODO: Figure out how to make an easy breakout for leashed leash_pets + C.apply_status_effect(/datum/status_effect/leash_pet)//Has now been leashed + user.apply_status_effect(/datum/status_effect/leash_dom) //Is the leasher + leash_pet = C //Save pet reference for later + leash_master = user //Save dom reference for later + mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_pet_move))) + mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move))) + leash_used = 1 + if(!leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Add slowdown if the pet didn't leash themselves + leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5) + for(var/mob/viewing in viewers(user, null)) + if(viewing == leash_master) + to_chat(leash_master, "You have hooked a leash onto [leash_pet]!") + else + viewing.show_message("[leash_pet] has been leashed by [leash_master]!", 1) + if(leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Pet leashed themself. They are not the dom + leash_pet.apply_status_effect(/datum/status_effect/leash_freepet) + leash_pet.remove_status_effect(/datum/status_effect/leash_dom) + while(1) //While true loop. The mark of a genius coder. ##MAINLOOP START + sleep(2) //Check every other tick + if(leash_pet == "null") //No pet, break loop + return + if(!(leash_pet.get_item_by_slot(SLOT_NECK))) //The pet has slipped their collar and is not the pet anymore. + for(var/mob/viewing in viewers(user, null)) + viewing.show_message("[leash_pet] has slipped out of their collar!!", 1) + to_chat(leash_pet, "You have slipped out of your collar!") + to_chat(loc, "[leash_pet] has slipped out of their collar!") + leash_pet.remove_status_effect(/datum/status_effect/leash_pet) + + if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks. + QDEL_NULL(mobhook_leash_master) + QDEL_NULL(mobhook_leash_pet) + QDEL_NULL(mobhook_leash_freepet) + if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet)) + leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) + if(leash_pet.has_movespeed_modifier(MOVESPEED_ID_LEASH)) + leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) + if(!leash_master == "null") + leash_master.remove_status_effect(/datum/status_effect/leash_dom) + leash_used = 0 //reset the leash to neutral + leash_pet = "null" + return + + else //No collar, no fun + var/leash_message = pick("Your pet needs a collar") + to_chat(user, "[leash_message]") + +//Called when the leash is used in hand +//Tugs the pet closer +/obj/item/leash/attack_self(mob/living/user) + if(!leash_pet == "null") //No pet, no tug. + return + //Yank the pet. Yank em in close. + if(leash_pet.x > leash_master.x + 1) + step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. + if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master + step(leash_pet, SOUTH, 1) + if(leash_pet.y < leash_master.y) + step(leash_pet, NORTH, 1) + if(leash_pet.x < leash_master.x - 1) + step(leash_pet, EAST, 1) + if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master + step(leash_pet, SOUTH, 1) + if(leash_pet.y < leash_master.y) + step(leash_pet, NORTH, 1) + if(leash_pet.y > leash_master.y + 1) + step(leash_pet, SOUTH, 1) + if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master + step(leash_pet, WEST, 1) + if(leash_pet.x < leash_master.x) + step(leash_pet, EAST, 1) + if(leash_pet.y < leash_master.y - 1) + step(leash_pet, NORTH, 1) + if(leash_pet.x > leash_master.x)//Check the other axis, and tug them into alignment so they are behind the master + step(leash_pet, WEST, 1) + if(leash_pet.x < leash_master.x) + step(leash_pet, EAST, 1) + +/obj/item/leash/proc/on_master_move() + //Make sure the dom still has a pet + if(leash_master == "null") //There must be a master + return + if(leash_pet == "null") //There must be a pet + return + if(leash_pet == leash_master) //Pet is the master + return + if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) + QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe + leash_master.remove_status_effect(/datum/status_effect/leash_dom) + return + + //If the master moves, pull the pet in behind + sleep(2) //A small sleep so the pet kind of bounces back after they make the step + //Also, the sleep means that the distance check for master happens before the pet, to prevent both from proccing. + + if(leash_master == "null") //Just to stop error messages + return + if(leash_pet == "null") + return + if(leash_pet.x > leash_master.x + 2) + step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. + if(leash_pet.y > leash_master.y)//Check the other axis, and tug them into alignment so they are behind the master + step(leash_pet, SOUTH, 1) + if(leash_pet.y < leash_master.y) + step(leash_pet, NORTH, 1) + if(leash_pet.x < leash_master.x - 2) + step(leash_pet, EAST, 1) + if(leash_pet.y > leash_master.y) + step(leash_pet, SOUTH, 1) + if(leash_pet.y < leash_master.y) + step(leash_pet, NORTH, 1) + if(leash_pet.y > leash_master.y + 2) + step(leash_pet, SOUTH, 1) + if(leash_pet.x > leash_master.x) + step(leash_pet, WEST, 1) + if(leash_pet.x < leash_master.x) + step(leash_pet, EAST, 1) + if(leash_pet.y < leash_master.y - 2) + step(leash_pet, NORTH, 1) + if(leash_pet.x > leash_master.x) + step(leash_pet, WEST, 1) + if(leash_pet.x < leash_master.x) + step(leash_pet, EAST, 1) + + //Knock the pet over if they get further behind. Shouldn't happen too often. + sleep(3) //This way running normally won't just yank the pet to the ground. + if(leash_master == "null") //Just to stop error messages. Break the loop early if something removed the master + return + if(leash_pet == "null") + return + if(leash_pet.x > leash_master.x + 3 || leash_pet.x < leash_master.x - 3 || leash_pet.y > leash_master.y + 3 || leash_pet.y < leash_master.y - 3) + //var/leash_knockdown_message = "[leash_pet] got pulled to the ground by their leash!" + //to_chat(leash_master, "[leash_knockdown_message]") + //to_chat(leash_pet, "[leash_knockdown_message]") + leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) + + //This code is to check if the pet has gotten too far away, and then break the leash. + sleep(3) //Wait to snap the leash + if(leash_master == "null") //Just to stop error messages + return + if(leash_pet == "null") + return + if(leash_pet.x > leash_master.x + 5 || leash_pet.x < leash_master.x - 5 || leash_pet.y > leash_master.y + 5 || leash_pet.y < leash_master.y - 5) + var/leash_break_message = "The leash snapped free from [leash_pet]!" + for(var/mob/viewing in viewers(leash_pet, null)) + if(viewing == leash_master) + to_chat(leash_master, "The leash snapped free from your pet!") + if(viewing == leash_pet) + to_chat(leash_pet, "Your leash has popped from your collar!") + else + viewing.show_message("[leash_break_message]", 1) + leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) + leash_pet.adjustOxyLoss(5) + leash_pet.remove_status_effect(/datum/status_effect/leash_pet) + leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) + leash_master.remove_status_effect(/datum/status_effect/leash_dom) + QDEL_NULL(mobhook_leash_master) + QDEL_NULL(mobhook_leash_pet) + leash_pet = "null" + leash_master = "null" + leash_used = 0 + +/obj/item/leash/proc/on_pet_move() + //This should only work if there is a pet and a master. + //This is here pretty much just to stop the console from flooding with errors + if(leash_master == "null") + return + if(leash_pet == "null") + return + //Make sure the pet is still a pet + if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) + QDEL_NULL(mobhook_leash_pet) //Probably redundant, but it's nice to be safe + return + + //The pet has escaped. There is no DOM. GO PET RUN. + if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))//If the pet is free, break + return + + //If the pet gets too far away, they get tugged back + sleep(3)//A small sleep so the pet kind of bounces back after they make the step + if(leash_master == "null") + return + if(leash_pet == "null") + return + //West tug + if(leash_pet.x > leash_master.x + 2) + step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. + //East tug + if(leash_pet.x < leash_master.x - 2) + step(leash_pet, EAST, 1) + //South tug + if(leash_pet.y > leash_master.y + 2) + step(leash_pet, SOUTH, 1) + //North tug + if(leash_pet.y < leash_master.y - 2) + step(leash_pet, NORTH, 1) + +/obj/item/leash/proc/on_freepet_move() + . = ..() + //Pet is on the run. Let's drag the leash behind them. + if(!leash_master == "null") //If there is a master, don't do this + return + if(leash_pet == "null") //If there is no pet, don't do this + return + if(leash_pet.is_holding_item_of_type(/obj/item/leash)) //If the pet is holding the leash, don't do this + return + + sleep(2) + if(leash_pet == "null") + return + //Double move to catch the leash up to the pet + if(src.x > leash_pet.x + 2) + . = step(src, WEST, 1) + if(src.x < leash_pet.x - 2) + . = step(src, EAST, 1) + if(src.y > leash_pet.y + 2) + . = step(src, SOUTH, 1) + if(src.y < leash_pet.y - 2) + . = step(src, NORTH, 1) + //Primary dragging code + if(src.x > leash_pet.x + 1) + . = step(src, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed. + if(src.y > leash_pet.y)//Check the other axis, and tug them into alignment so they are behind the pet + . = step(src, SOUTH, 1) + if(src.y < leash_pet.y) + . = step(src, NORTH, 1) + if(src.x < leash_pet.x - 1) + . = step(src, EAST, 1) + if(src.y > leash_pet.y) + . = step(src, SOUTH, 1) + if(src.y < leash_pet.y) + . = step(src, NORTH, 1) + if(src.y > leash_pet.y + 1) + . = step(src, SOUTH, 1) + if(src.x > leash_pet.x) + . = step(src, WEST, 1) + if(src.x < leash_pet.x) + . = step(src, EAST, 1) + if(src.y < leash_pet.y - 1) + . = step(src, NORTH, 1) + if(src.x > leash_pet.x) + . = step(src, WEST, 1) + if(src.x < leash_pet.x) + . = step(src, EAST, 1) + + sleep(1) + //Just to prevent error messages + if(leash_pet == "null") + return + if(src.x > leash_pet.x + 5 || src.x < leash_pet.x - 5 || src.y > leash_pet.y + 5 || src.y < leash_pet.y - 5) + var/leash_break_message = "The leash snapped free from [leash_pet]!" + for(var/mob/viewing in viewers(leash_pet, null)) + if(viewing == leash_pet) + to_chat(leash_pet, "Your leash has popped from your collar!") + else + viewing.show_message("[leash_break_message]", 1) + leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0) + leash_pet.adjustOxyLoss(5) + leash_pet.remove_status_effect(/datum/status_effect/leash_pet) + leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) + QDEL_NULL(mobhook_leash_pet) + QDEL_NULL(mobhook_leash_freepet) + leash_pet = "null" + leash_used = 0 + +/obj/item/leash/dropped() //Drop the leash, and the leash effects stop + . = ..() + if(leash_pet == "null") //There is no pet. Stop this silliness + return + if(leash_master == "null") + return + //Dropping procs any time the leash changes slots. So, we will wait a tick and see if the leash was actually dropped + sleep(1) + if(leash_master.is_holding_item_of_type(/obj/item/leash) || istype(leash_master.get_item_by_slot(SLOT_BELT), /obj/item/leash)) + return //Dom still has the leash as it turns out. Cancel the proc. + for(var/mob/viewing in viewers(leash_master, null)) + viewing.show_message("[leash_master] has dropped the leash.", 1) + //DOM HAS DROPPED LEASH. PET IS FREE. SCP HAS BREACHED CONTAINMENT. + leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH) + mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_freepet_move))) + leash_master.remove_status_effect(/datum/status_effect/leash_dom) //No dom with no leash. We will get a new dom if the leash is picked back up. + leash_master = "null" + QDEL_NULL(mobhook_leash_master) + +/obj/item/leash/equipped(mob/user) + . = ..() + if(leash_used == 0) //Don't apply statuses with a fresh leash. Keeps things clean on the backend. + return + sleep(2) + if(leash_pet == "null") + return + leash_master = user + if(leash_master.has_status_effect(/datum/status_effect/leash_freepet) || leash_master.has_status_effect(/datum/status_effect/leash_pet)) //Pet picked up their own leash. + leash_master = "null" + return + leash_master.apply_status_effect(/datum/status_effect/leash_dom) + mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move))) + leash_pet.remove_status_effect(/datum/status_effect/leash_freepet) + QDEL_NULL(mobhook_leash_freepet) + leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5) + +/datum/crafting_recipe/leash + name = "Leash" + result = /obj/item/leash + time = 40 + reqs = list(/obj/item/stack/sheet/metal = 1, + /obj/item/stack/sheet/cloth = 3) category = CAT_MISC \ No newline at end of file diff --git a/icons/obj/food/burgerbread.dmi b/icons/obj/food/burgerbread.dmi index 9eafd6c2..6e6d9555 100644 Binary files a/icons/obj/food/burgerbread.dmi and b/icons/obj/food/burgerbread.dmi differ