Files
Bubberstation/code/modules/food_and_drinks/pizzabox.dm
SkyratBot 81f3db19f8 [MIRROR] Adds pen clicking, changes most pen typechecks into writing implement checks (#28356)
* Adds pen clicking, changes most pen typechecks into writing implement checks (#84186)

## About The Pull Request

Fixes #84170
Adds pen clicking and changes some of edagger and pendriver code to use
it instead.
Also replaces most pen typechecks to writing implement checks where it
makes sense, so now you can rename things with everything you can write
with (crayons)

![3d6NwcATNp](https://github.com/tgstation/tgstation/assets/44720187/f6b3ba2f-f3de-4e40-827f-2bad153a92f4)

Twisting pen caps (for traitor uplinks) has been moved to ctrl + click
instead.

* Adds pen clicking, changes most pen typechecks into writing implement checks

---------

Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
2024-06-25 16:19:05 +05:30

455 lines
14 KiB
Plaintext

/obj/item/bombcore/miniature/pizza
name = "pizza bomb"
desc = "Special delivery!"
icon_state = "pizzabomb_inactive"
inhand_icon_state = "eshield"
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
/obj/item/pizzabox
name = "pizza box"
desc = "A box suited for pizzas."
icon = 'icons/obj/food/containers.dmi'
icon_state = "pizzabox"
base_icon_state = "pizzabox"
inhand_icon_state = "pizzabox"
lefthand_file = 'icons/mob/inhands/items/food_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/food_righthand.dmi'
custom_materials = list(/datum/material/cardboard =SHEET_MATERIAL_AMOUNT)
var/open = FALSE
var/can_open_on_fall = TRUE //if FALSE, this pizza box will never open if it falls from a stack
/// Used so that you can not destroy the infinite pizza box
var/foldable = TRUE
var/boxtag = ""
///Used to make sure artisinal box tags aren't overwritten.
var/boxtag_set = FALSE
var/list/boxes = list()
var/obj/item/food/pizza/pizza
var/obj/item/bombcore/miniature/pizza/bomb
var/bomb_active = FALSE // If the bomb is counting down.
var/bomb_defused = TRUE // If the bomb is inert.
var/bomb_timer = 1 // How long before blowing the bomb, in seconds.
/// Min bomb timer allowed in seconds
var/bomb_timer_min = 1
/// Max bomb timer allower in seconds
var/bomb_timer_max = 20
/obj/item/pizzabox/Initialize(mapload)
. = ..()
if(pizza)
pizza = new pizza
update_appearance()
register_context()
/obj/item/pizzabox/proc/register_bomb(new_bomb)
bomb = new_bomb
if(istype(bomb))
RegisterSignal(bomb, COMSIG_QDELETING, PROC_REF(clear_bomb))
/obj/item/pizzabox/proc/clear_bomb(datum/source)
SIGNAL_HANDLER
if(isnull(bomb))
return
UnregisterSignal(bomb, COMSIG_QDELETING)
bomb = null
/obj/item/pizzabox/Destroy()
unprocess()
clear_bomb()
return ..()
/obj/item/pizzabox/update_desc()
desc = initial(desc)
. = ..()
if(pizza && pizza.boxtag && !boxtag_set)
boxtag = pizza.boxtag
boxtag_set = TRUE
if(open)
if(pizza)
desc = "[desc] It appears to have \a [pizza] inside. Use your other hand to take it out."
if(bomb)
desc = "[desc] Wait, what?! It has \a [bomb] inside!"
if(bomb_defused)
desc = "[desc] The bomb seems inert. Use your other hand to activate it."
if(bomb_active)
desc = "[desc] It looks like it's about to go off!"
else
var/obj/item/pizzabox/box = length(boxes) ? boxes[length(boxes)] : src
if(length(boxes))
desc = "A pile of boxes suited for pizzas. There appear to be [length(boxes) + 1] boxes in the pile."
if(box.boxtag != "")
desc = "[desc] The [length(boxes) ? "top box" : "box"]'s tag reads: [box.boxtag]"
/obj/item/pizzabox/update_icon_state()
if(!open)
icon_state = "[base_icon_state]"
return ..()
icon_state = pizza ? "[base_icon_state]_messy" : "[base_icon_state]_open"
bomb?.icon_state = "pizzabomb_[bomb_active ? "active" : "inactive"]"
return ..()
/obj/item/pizzabox/update_overlays()
. = ..()
if(open)
if(pizza)
var/mutable_appearance/pizza_overlay = mutable_appearance(pizza.icon, pizza.icon_state)
pizza_overlay.pixel_y = -2
. += pizza_overlay
if(bomb)
var/mutable_appearance/bomb_overlay = mutable_appearance(bomb.icon, bomb.icon_state, layer = layer + 0.01)
bomb_overlay.pixel_y = 8
. += bomb_overlay
return
var/box_offset = 0
for(var/stacked_box in boxes)
box_offset += 3
var/obj/item/pizzabox/box = stacked_box
var/mutable_appearance/box_overlay = mutable_appearance(box.icon, box.icon_state, layer = layer + (box_offset * 0.01))
box_overlay.pixel_y = box_offset
. += box_overlay
var/obj/item/pizzabox/box = LAZYLEN(length(boxes)) ? boxes[length(boxes)] : src
if(box.boxtag != "")
var/mutable_appearance/tag_overlay = mutable_appearance(icon, "pizzabox_tag", layer = layer + (box_offset * 0.02))
tag_overlay.pixel_y = box_offset
. += tag_overlay
/obj/item/pizzabox/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
var/current_offset = 2
if(!isinhands)
return
for(var/V in boxes) //add EXTRA BOX per box
var/mutable_appearance/M = mutable_appearance(icon_file, inhand_icon_state)
M.pixel_y = current_offset
current_offset += 2
. += M
/obj/item/pizzabox/attack_self(mob/user)
if(length(boxes) > 0)
return
open = !open
if(open && !bomb_defused)
audible_message(span_warning("[icon2html(src, hearers(src))] *beep*"))
bomb_active = TRUE
START_PROCESSING(SSobj, src)
update_appearance()
/obj/item/pizzabox/attack_self_secondary(mob/user)
if(length(boxes) > 0)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(pizza || bomb || !foldable)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/obj/item/stack/sheet/cardboard/cardboard = new(user.drop_location())
user.put_in_active_hand(cardboard)
qdel(src)
return SECONDARY_ATTACK_CONTINUE_CHAIN
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/pizzabox/attack_hand(mob/user, list/modifiers)
if(user.get_inactive_held_item() != src)
return ..()
if(open)
if(pizza)
user.put_in_hands(pizza)
pizza = null
update_appearance()
else if(bomb)
if(wires.is_all_cut() && bomb_defused)
user.put_in_hands(bomb)
balloon_alert(user, "removed bomb")
clear_bomb()
update_appearance()
return
else
bomb_timer = tgui_input_number(user, "Set the bomb timer", "Pizza Bomb", bomb_timer, bomb_timer_max, bomb_timer_min)
if(!bomb_timer || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return
bomb_defused = FALSE
log_bomber(user, "has trapped a", src, "with [bomb] set to [bomb_timer] seconds")
bomb.adminlog = "The [bomb.name] in [src.name] that [key_name(user)] activated has detonated!"
balloon_alert(user, "bomb set")
update_appearance()
else if(length(boxes))
var/obj/item/pizzabox/topbox = boxes[length(boxes)]
boxes -= topbox
user.put_in_hands(topbox)
topbox.update_appearance()
update_appearance()
user.regenerate_icons()
/obj/item/pizzabox/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/pizzabox))
var/obj/item/pizzabox/newbox = I
if(!open && !newbox.open)
var/list/add = list()
add += newbox
add += newbox.boxes
if(!user.transferItemToLoc(newbox, src))
return
boxes += add
newbox.boxes.Cut()
newbox.update_appearance()
update_appearance()
user.regenerate_icons()
if(length(boxes) >= 5)
if(prob(10 * length(boxes)))
user.balloon_alert_to_viewers("oops!")
disperse_pizzas()
else
balloon_alert(user, "looks unstable...")
return
else
balloon_alert(user, "close it first!")
else if(istype(I, /obj/item/food/pizza))
if(open)
if(pizza)
balloon_alert(user, "it's full!")
return
if(!user.transferItemToLoc(I, src))
return
pizza = I
update_appearance()
return
else if(istype(I, /obj/item/bombcore/miniature/pizza))
if(open && !bomb)
if(!user.transferItemToLoc(I, src))
return
set_wires(new /datum/wires/explosive/pizza(src))
register_bomb(I)
balloon_alert(user, "bomb placed")
update_appearance()
return
else if(bomb)
balloon_alert(user, "already rigged!")
else if(IS_WRITING_UTENSIL(I))
if(!open)
if(!user.can_write(I))
return
var/obj/item/pizzabox/box = length(boxes) ? boxes[length(boxes)] : src
box.boxtag += tgui_input_text(user, "Write on [box]'s tag:", box, max_length = 30)
if(!user.can_perform_action(src))
return
balloon_alert(user, "writing box tag...")
boxtag_set = TRUE
update_appearance()
return
else if(is_wire_tool(I))
if(wires && bomb)
wires.interact(user)
..()
/obj/item/pizzabox/process(seconds_per_tick)
if(bomb_active && !bomb_defused && (bomb_timer > 0))
playsound(loc, 'sound/items/timer.ogg', 50, FALSE)
bomb_timer -= seconds_per_tick
if(bomb_active && !bomb_defused && (bomb_timer <= 0))
if(bomb in src)
bomb.detonate()
unprocess()
qdel(src)
if(!bomb_active || bomb_defused)
if(bomb_defused && (bomb in src))
bomb.defuse()
bomb_active = FALSE
unprocess()
return
/obj/item/pizzabox/attack(mob/living/target, mob/living/user, def_zone)
. = ..()
if(length(boxes) >= 3 && prob(25 * length(boxes)))
disperse_pizzas()
/obj/item/pizzabox/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(length(boxes) >= 2 && prob(20 * length(boxes)))
disperse_pizzas()
/obj/item/pizzabox/examine(mob/user)
. = ..()
if(isobserver(user))
if(bomb)
. += span_deadsay("This pizza box contains [bomb_defused ? "an unarmed bomb" : "an armed bomb"].")
if(pizza && istype(pizza, /obj/item/food/pizza/margherita/robo))
. += span_deadsay("The pizza in this pizza box contains nanomachines.")
/obj/item/pizzabox/proc/disperse_pizzas()
visible_message(span_warning("The pizzas fall everywhere!"))
for(var/V in boxes)
var/obj/item/pizzabox/P = V
var/fall_dir = pick(GLOB.alldirs)
step(P, fall_dir)
if(P.pizza && P.can_open_on_fall && prob(50)) //rip pizza
P.open = TRUE
P.pizza.forceMove(get_turf(P))
fall_dir = pick(GLOB.alldirs)
step(P.pizza, fall_dir)
P.pizza = null
P.update_appearance()
boxes -= P
update_appearance()
if(isliving(loc))
var/mob/living/L = loc
L.regenerate_icons()
/obj/item/pizzabox/proc/unprocess()
STOP_PROCESSING(SSobj, src)
qdel(wires)
set_wires(null)
update_appearance()
/obj/item/pizzabox/bomb/Initialize(mapload)
. = ..()
if(!pizza)
var/randompizza = pick(subtypesof(/obj/item/food/pizza) - /obj/item/food/pizza/flatbread) //also disincludes another base type
pizza = new randompizza(src)
update_appearance()
register_bomb(new /obj/item/bombcore/miniature/pizza(src))
set_wires(new /datum/wires/explosive/pizza(src))
/obj/item/pizzabox/bomb/armed
bomb_timer = 5
bomb_defused = FALSE
boxtag = "Meat Explosion"
boxtag_set = TRUE
pizza = /obj/item/food/pizza/meat
/obj/item/pizzabox/margherita
pizza = /obj/item/food/pizza/margherita
/obj/item/pizzabox/margherita/robo
pizza = /obj/item/food/pizza/margherita/robo
/obj/item/pizzabox/vegetable
pizza = /obj/item/food/pizza/vegetable
/obj/item/pizzabox/mushroom
pizza = /obj/item/food/pizza/mushroom
/obj/item/pizzabox/meat
pizza = /obj/item/food/pizza/meat
/obj/item/pizzabox/pineapple
pizza = /obj/item/food/pizza/pineapple
//An anomalous pizza box that, when opened, produces the opener's favorite kind of pizza.
/obj/item/pizzabox/infinite
resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF //hard to destroy
can_open_on_fall = FALSE
foldable = FALSE
boxtag = "Your Favourite" //used to give it a tag overlay, shouldn't be seen by players
///List of pizzas this box can spawn. Weighted by chance to be someone's favorite.
var/list/pizza_types = list(
/obj/item/food/pizza/meat = 10,
/obj/item/food/pizza/mushroom = 10,
/obj/item/food/pizza/margherita = 10,
/obj/item/food/pizza/sassysage = 8,
/obj/item/food/pizza/vegetable = 8,
/obj/item/food/pizza/pineapple = 5,
/obj/item/food/pizza/donkpocket = 3,
/obj/item/food/pizza/dank = 1,
)
///List of ckeys and their favourite pizzas. e.g. pizza_preferences[ckey] = /obj/item/food/pizza/meat
var/static/list/pizza_preferences
/obj/item/pizzabox/infinite/Initialize(mapload)
. = ..()
if(!pizza_preferences)
pizza_preferences = list()
/obj/item/pizzabox/infinite/examine(mob/user)
if(!open && ishuman(user))
attune_pizza(user) //pizza tag changes based on examiner
. = ..()
if(isobserver(user))
. += span_deadsay("This pizza box is anomalous, and will produce infinite pizza.")
/obj/item/pizzabox/infinite/attack_self(mob/living/user)
if(ishuman(user))
attune_pizza(user)
to_chat(user, span_notice("Another pizza immediately appears in the box, what the hell?"))
return ..()
/obj/item/pizzabox/infinite/proc/attune_pizza(mob/living/carbon/human/nommer) //tonight on "proc names I never thought I'd type"
if(!nommer.ckey)
return
//list our ckey and assign it a favourite pizza
if(!pizza_preferences[nommer.ckey])
if(nommer.has_quirk(/datum/quirk/pineapple_liker))
pizza_preferences[nommer.ckey] = /obj/item/food/pizza/pineapple
else if(nommer.has_quirk(/datum/quirk/pineapple_hater))
var/list/pineapple_pizza_liker = pizza_types.Copy()
pineapple_pizza_liker -= /obj/item/food/pizza/pineapple
pizza_preferences[nommer.ckey] = pick_weight(pineapple_pizza_liker)
else if(istype(nommer.mind?.assigned_role, /datum/job/botanist))
pizza_preferences[nommer.ckey] = /obj/item/food/pizza/dank
else
pizza_preferences[nommer.ckey] = pick_weight(pizza_types)
if(pizza)
//if the pizza isn't our favourite, delete it
if(pizza.type != pizza_preferences[nommer.ckey])
QDEL_NULL(pizza)
else
pizza.foodtypes = nommer.get_liked_foodtypes() //make sure it's our favourite
return
var/obj/item/food/pizza/favourite_pizza_type = pizza_preferences[nommer.ckey]
pizza = new favourite_pizza_type
boxtag_set = FALSE
update_appearance() //update our boxtag to match our new pizza
pizza.foodtypes = nommer.get_liked_foodtypes() //it's our favorite!
///screentips for pizzaboxes
/obj/item/pizzabox/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(!held_item)
if(user.get_inactive_held_item() != src)
return NONE
if(open)
if(pizza)
context[SCREENTIP_CONTEXT_LMB] = "Remove pizza"
else if(bomb && wires.is_all_cut() && bomb_defused)
context[SCREENTIP_CONTEXT_LMB] = "Remove bomb"
else
if(length(boxes) > 0)
context[SCREENTIP_CONTEXT_LMB] = "Remove pizza box"
return CONTEXTUAL_SCREENTIP_SET
if(held_item == src)
if(length(boxes) > 0)
return NONE
context[SCREENTIP_CONTEXT_LMB] = open ? "Close" : "Open"
if(!pizza && !bomb && foldable)
context[SCREENTIP_CONTEXT_RMB] = "Deconstruct"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/pizzabox))
if(!open)
context[SCREENTIP_CONTEXT_LMB] = "Stack pizza box"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/food/pizza))
if(open && !pizza)
context[SCREENTIP_CONTEXT_LMB] = "Place pizza"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/pen))
if(!open)
context[SCREENTIP_CONTEXT_LMB] = "Write boxtag"
return CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/bombcore/miniature/pizza))
if(open && !bomb)
context[SCREENTIP_CONTEXT_LMB] = "Place bomb"
return CONTEXTUAL_SCREENTIP_SET
if(is_wire_tool(held_item))
if(open && bomb)
context[SCREENTIP_CONTEXT_LMB] = "Access wires"
return CONTEXTUAL_SCREENTIP_SET