Moves a great many things from attackby() to item_interaction() (#96642)

## About The Pull Request

21 things.
Additionally, an actual refactor(a real one) of refunding of item-based
spellbook entries, as in the three summons, which hasn't worked for four
years and now does.
Lastly, the holopayment stand IDs can project can now accept payment.


## Why It's Good For The Game

They want you to think this is worth 210 gbp, don't believe the lies

## Changelog
🆑

fix: Wizards can now successfully refund summoning items
fix: Holopayment stands can now take payment

code: 21 things have been moved from attackby() to item_interaction()

/🆑
This commit is contained in:
Leland Kemble
2026-07-05 10:02:06 +02:00
committed by GitHub
parent ae315a22a1
commit e816dfebeb
23 changed files with 332 additions and 321 deletions
@@ -41,12 +41,13 @@
///Cooldown for printing the bounty sheet, and not breaking people's eardrums.
COOLDOWN_DECLARE(sheet_printer_cooldown)
/obj/machinery/computer/piratepad_control/civilian/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
if(isidcard(I))
if(id_insert(user, I, inserted_scan_id))
inserted_scan_id = I
return TRUE
return ..()
/obj/machinery/computer/piratepad_control/civilian/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!isidcard(tool))
return NONE
if(!id_insert(user, tool, inserted_scan_id))
return ITEM_INTERACT_BLOCKING
inserted_scan_id = tool
return ITEM_INTERACT_SUCCESS
/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I)
if(istype(I) && istype(I.buffer,/obj/machinery/piratepad/civilian))
+12 -11
View File
@@ -26,20 +26,21 @@
QDEL_NULL(current_plate)
. = ..()
/obj/machinery/plate_press/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
/obj/machinery/plate_press/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stack/license_plates/empty))
return NONE
if(!is_operational)
to_chat(user, span_warning("[src] has to be on to do this!"))
return FALSE
to_chat(user, span_warning("[src] has to be on to be loaded!"))
return ITEM_INTERACT_BLOCKING
if(current_plate)
to_chat(user, span_warning("[src] already has a plate in it!"))
return FALSE
if(istype(I, /obj/item/stack/license_plates/empty))
var/obj/item/stack/license_plates/empty/plate = I
plate.use(1)
current_plate = new plate.type(src, 1) //Spawn a new single sheet in the machine
update_appearance()
else
return ..()
return ITEM_INTERACT_BLOCKING
var/obj/item/stack/license_plates/empty/plate = tool
plate.use(1)
current_plate = new plate.type(src, 1) //Spawn a new single sheet in the machine
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/plate_press/attack_hand(mob/living/user, list/modifiers)
. = ..()
+9 -8
View File
@@ -56,18 +56,19 @@
. = ..()
. += span_notice("You can booby-trap the poster by using a glass shard on it before you put it up.")
/obj/item/poster/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
if(!istype(I, /obj/item/shard))
return ..()
/obj/item/poster/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/shard))
return NONE
if (locate(/obj/item/shard) in (poster_structure?.contents || contents))
if(locate(/obj/item/shard) in (poster_structure?.contents || contents))
balloon_alert(user, "already trapped!")
return
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(I, src))
return
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You conceal \the [I] inside the rolled up poster."))
to_chat(user, span_notice("You conceal \the [tool] inside the rolled up poster."))
return ITEM_INTERACT_SUCCESS
/obj/item/poster/interact_with_atom(turf/closed/wall_structure, mob/living/user, list/modifiers)
if(!isclosedturf(wall_structure))
+5 -6
View File
@@ -80,12 +80,11 @@
pushed_over = FALSE
tacticool = AddComponent(/datum/component/tactical)
/obj/item/cardboard_cutout/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
if(istype(I, /obj/item/toy/crayon))
change_appearance(I, user)
return TRUE
return ..()
/obj/item/cardboard_cutout/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/toy/crayon))
return NONE
change_appearance(tool, user)
return ITEM_INTERACT_SUCCESS
/obj/item/cardboard_cutout/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
. = ..()
+6 -6
View File
@@ -124,13 +124,13 @@
desc = "This door only opens when a keycard is swiped. It looks virtually indestructible."
uses_queuelinks = FALSE
/obj/machinery/door/puzzle/keycard/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
. = ..()
if(!istype(attacking_item, /obj/item/keycard))
return
var/obj/item/keycard/key = attacking_item
if(!try_puzzle_open(key.puzzle_id))
/obj/machinery/door/puzzle/keycard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/keycard))
return NONE
if(!try_puzzle_open(astype(tool, /obj/item/keycard).puzzle_id))
to_chat(user, span_notice("[src] buzzes. This must not be the right key."))
return ITEM_INTERACT_BLOCKING
return ITEM_INTERACT_SUCCESS
//Test doors. Gives admins a few doors to use quickly should they so choose for events.
/obj/machinery/door/puzzle/keycard/yellow_required
+14 -11
View File
@@ -66,14 +66,17 @@
var/obj/item/documents/photocopy/C = copy
copy_type = C.copy_type
/obj/item/documents/photocopy/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers)
if(istype(O, /obj/item/toy/crayon/red) || istype(O, /obj/item/toy/crayon/blue))
if (forgedseal)
to_chat(user, span_warning("You have already forged a seal on [src]!"))
else
var/obj/item/toy/crayon/C = O
name = "[C.crayon_color] secret documents"
icon_state = "docs_[C.crayon_color]"
forgedseal = C.crayon_color
to_chat(user, span_notice("You forge the official seal with a [C.crayon_color] crayon. No one will notice... right?"))
update_appearance()
/obj/item/documents/photocopy/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/toy/crayon/red) && !istype(tool, /obj/item/toy/crayon/blue))
return NONE
if (forgedseal)
to_chat(user, span_warning("You have already forged a seal on [src]!"))
return ITEM_INTERACT_BLOCKING
var/obj/item/toy/crayon/C = tool
name = "[C.crayon_color] secret documents"
icon_state = "docs_[C.crayon_color]"
forgedseal = C.crayon_color
to_chat(user, span_notice("You forge the official seal with a [C.crayon_color] crayon. No one will notice... right?"))
update_appearance()
return ITEM_INTERACT_SUCCESS
@@ -228,6 +228,16 @@
var/was_put_in_hands = user.put_in_hands(to_equip)
to_chat(user, span_notice("\A [to_equip.name] has been summoned [was_put_in_hands ? "in your hands" : "at your feet"]."))
/datum/spellbook_entry/item/can_refund(mob/living/carbon/human/user, obj/item/spellbook/book, obj/item/refunding_item)
if(HAS_TRAIT(user, TRAIT_SPELLS_LOTTERY))
to_chat(user, span_notice("No refund."))
return FALSE
if(!book.refunds_allowed) // We're not doing a check for refundable here because they're not refundable in-book.
return FALSE
if(refunding_item.type != item_path)
return FALSE
return TRUE
/// Ritual, these cause station wide effects and are (pretty much) a blank slate to implement stuff in
/datum/spellbook_entry/summon
category = "Rituals"
@@ -64,7 +64,6 @@
desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side."
item_path = /obj/item/antag_spawner/contract
category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
/datum/spellbook_entry/item/guardian
name = "Guardian Deck"
@@ -82,7 +81,6 @@
item_path = /obj/item/antag_spawner/slaughter_demon
limit = 3
category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
/datum/spellbook_entry/item/hugbottle
name = "Bottle of Tickles"
@@ -97,7 +95,6 @@
cost = 1 //non-destructive; it's just a jape, sibling!
limit = 3
category = SPELLBOOK_CATEGORY_ASSISTANCE
refundable = TRUE
/datum/spellbook_entry/item/vendormancer
name = "Scepter of Vendormancy"
@@ -74,55 +74,39 @@
return ..()
/obj/item/spellbook/attackby(obj/item/O, mob/user, list/modifiers, list/attack_modifiers)
/obj/item/spellbook/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
// This can be generalized in the future, but for now it stays
if(istype(O, /obj/item/antag_spawner/contract))
var/datum/spellbook_entry/item/contract/contract_entry = locate() in entries
if(!istype(contract_entry))
to_chat(user, span_warning("[src] doesn't seem to want to refund [O]."))
return
if(!contract_entry.can_refund(user, src))
to_chat(user, span_warning("You can't refund [src]."))
return
var/obj/item/antag_spawner/contract/contract = O
if(contract.used)
var/datum/spellbook_entry/item/spawner_entry
var/success_string
if(istype(tool, /obj/item/antag_spawner/contract))
if(astype(tool, /obj/item/antag_spawner/contract).used)
to_chat(user, span_warning("The contract has been used, you can't get your points back now!"))
return
return ITEM_INTERACT_BLOCKING
spawner_entry = locate(/datum/spellbook_entry/item/contract) in entries
success_string = "You feed the contract back into the spellbook, refunding your points."
to_chat(user, span_notice("You feed the contract back into the spellbook, refunding your points."))
uses += contract_entry.cost
contract_entry.times--
qdel(O)
if(istype(tool, /obj/item/antag_spawner/slaughter_demon/laughter))
spawner_entry = locate(/datum/spellbook_entry/item/hugbottle) in entries
success_string = "On second thought, maybe summoning a demon isn't a funny idea. You refund your points."
else if(istype(O, /obj/item/antag_spawner/slaughter_demon/laughter))
var/datum/spellbook_entry/item/hugbottle/demon_entry = locate() in entries
if(!istype(demon_entry))
to_chat(user, span_warning("[src] doesn't seem to want to refund [O]."))
return
if(!demon_entry.can_refund(user, src))
to_chat(user, span_warning("You can't refund [O]."))
return
else if(istype(tool, /obj/item/antag_spawner/slaughter_demon))
spawner_entry = locate(/datum/spellbook_entry/item/bloodbottle) in entries
success_string = "On second thought, maybe summoning a demon is a bad idea. You refund your points."
to_chat(user, span_notice("On second thought, maybe summoning a demon isn't a funny idea. You refund your points."))
uses += demon_entry.cost
demon_entry.times--
qdel(O)
if(isnull(success_string))
return NONE
if(!istype(spawner_entry)) // No success_string means it isn't a valid item, no spawner entry means the book doesn't have it(somehow)(they had this check before I got here)
to_chat(user, span_warning("[src] doesn't seem to want to refund [tool]."))
return ITEM_INTERACT_BLOCKING
if(!spawner_entry.can_refund(user, src, tool))
to_chat(user, span_warning("You can't refund [src]."))
return ITEM_INTERACT_BLOCKING
else if(istype(O, /obj/item/antag_spawner/slaughter_demon))
var/datum/spellbook_entry/item/bloodbottle/demon_entry = locate() in entries
if(!istype(demon_entry))
to_chat(user, span_warning("[src] doesn't seem to want to refund [O]."))
return
if(!demon_entry.can_refund(user, src))
to_chat(user, span_warning("You can't refund [O]."))
return
to_chat(user, span_notice("On second thought, maybe summoning a demon is a bad idea. You refund your points."))
uses += demon_entry.cost
demon_entry.times--
qdel(O)
return ..()
to_chat(user, span_notice(success_string))
uses += spawner_entry.cost
spawner_entry.times--
qdel(tool)
return ITEM_INTERACT_SUCCESS
/// Instantiates our list of spellbook entries.
/obj/item/spellbook/proc/prepare_spells()
+27 -30
View File
@@ -74,60 +74,57 @@
linked_card = null
return ..()
/obj/structure/holopay/attackby(obj/item/held_item, mob/item_holder, list/modifiers, list/attack_modifiers)
var/mob/living/user = item_holder
if(!isliving(user))
return ..()
/obj/structure/holopay/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
/// Users can pay with an ID to skip the UI
if(isidcard(held_item))
if(istype(held_item, /obj/item/card/id/departmental_budget))
if(isidcard(tool))
if(istype(tool, /obj/item/card/id/departmental_budget))
balloon_alert(user, "invalid payment card")
to_chat(user, span_warning("You cannot use a departamental card for this."))
return FALSE
if(force_fee && tgui_alert(item_holder, "This holopay has a [force_fee] [MONEY_SYMBOL] fee. Confirm?", "Holopay Fee", list("Pay", "Cancel")) != "Pay")
return TRUE
return ITEM_INTERACT_BLOCKING
if(force_fee && tgui_alert(user, "This holopay has a [force_fee] [MONEY_SYMBOL] fee. Confirm?", "Holopay Fee", list("Pay", "Cancel")) != "Pay")
return ITEM_INTERACT_BLOCKING
process_payment(user)
return TRUE
return ITEM_INTERACT_SUCCESS
/// Users can also pay by holochip
if(istype(held_item, /obj/item/holochip))
if(istype(tool, /obj/item/holochip))
/// Account checks
var/obj/item/holochip/chip = held_item
var/obj/item/holochip/chip = tool
if(!chip.credits)
balloon_alert(user, "holochip is empty")
to_chat(user, span_warning("There doesn't seem to be any [MONEY_NAME] here."))
return FALSE
return ITEM_INTERACT_BLOCKING
/// Charges force fee or uses pay what you want
var/cash_deposit = force_fee || tgui_input_number(user, "How much? (Max: [chip.credits])", "Patronage", max_value = chip.credits)
/// Exit sanity checks
if(!cash_deposit)
return TRUE
if(QDELETED(held_item) || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return FALSE
return ITEM_INTERACT_BLOCKING
if(QDELETED(tool) || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return ITEM_INTERACT_BLOCKING
if(!chip.spend(cash_deposit, FALSE))
balloon_alert(user, "insufficient [MONEY_NAME]")
to_chat(user, span_warning("You don't have enough [MONEY_NAME] to pay with this chip."))
return FALSE
return ITEM_INTERACT_BLOCKING
/// Success: Alert buyer
alert_buyer(user, cash_deposit)
return TRUE
return ITEM_INTERACT_SUCCESS
/// Throws errors if they try to use space cash
if(istype(held_item, /obj/item/stack/spacecash))
if(istype(tool, /obj/item/stack/spacecash))
to_chat(user, "What is this, the 2000s? We only take card here.")
return TRUE
if(istype(held_item, /obj/item/coin))
return ITEM_INTERACT_BLOCKING
if(istype(tool, /obj/item/coin))
to_chat(user, "What is this, the 1800s? We only take card here.")
return TRUE
return ..()
return ITEM_INTERACT_BLOCKING
return NONE
/obj/structure/holopay/attackby_secondary(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
/obj/structure/holopay/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers)
/// Can kill it by right-clicking with ID because it seems useful and intuitive, to me, at least
if(!isidcard(weapon))
return ..()
var/obj/item/card/id/attacking_id = weapon
if(!isidcard(tool))
return NONE
var/obj/item/card/id/attacking_id = tool
if(!attacking_id.my_store || attacking_id.my_store != src)
return ..()
return ITEM_INTERACT_BLOCKING
dissipate()
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return ITEM_INTERACT_SUCCESS
/obj/structure/holopay/ui_interact(mob/user, datum/tgui/ui)
. = ..()
@@ -259,7 +256,7 @@
/// Account checks
var/obj/item/card/id/id_card
id_card = user.get_idcard(TRUE)
if(isnull(id_card) || id_card.can_be_used_in_payment(user))
if(isnull(id_card) || !id_card.can_be_used_in_payment(user))
balloon_alert(user, "invalid account")
to_chat(user, span_warning("You don't have a valid account."))
return FALSE
@@ -83,16 +83,16 @@
else
toggle_open()
/obj/machinery/skill_station/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
if(istype(I,/obj/item/skillchip))
if(inserted_skillchip)
to_chat(user,span_notice("There's already a skillchip inside."))
return
if(!user.transferItemToLoc(I, src))
return
inserted_skillchip = I
return
return ..()
/obj/machinery/skill_station/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool,/obj/item/skillchip))
return NONE
if(inserted_skillchip)
to_chat(user,span_notice("There's already a skillchip inside."))
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
inserted_skillchip = tool
return ITEM_INTERACT_SUCCESS
/obj/machinery/skill_station/dump_contents()
. = ..()
@@ -119,12 +119,11 @@
for(var/obj/item/crusher_trophy/crusher_trophy as anything in trophies)
. += span_notice("It has \a [crusher_trophy] attached, which causes [crusher_trophy.effect_desc()].")
/obj/item/kinetic_crusher/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(istype(attacking_item, /obj/item/crusher_trophy))
var/obj/item/crusher_trophy/crusher_trophy = attacking_item
crusher_trophy.add_to(src, user)
return
return ..()
/obj/item/kinetic_crusher/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(istype(tool, /obj/item/crusher_trophy))
astype(tool, /obj/item/crusher_trophy).add_to(src, user)
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/kinetic_crusher/crowbar_act(mob/living/user, obj/item/tool)
. = ..()
+14 -16
View File
@@ -381,24 +381,22 @@
qdel(src)
return LAZARUS_INJECTOR_USED
/obj/item/food/deadmouse/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
var/mob/living/living_user = user
if(istype(living_user) && attacking_item.get_sharpness() && living_user.combat_mode)
if(!isturf(loc))
balloon_alert(user, "can't butcher here!")
return
/obj/item/food/deadmouse/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!tool.get_sharpness() || !user.combat_mode)
return NONE
if(!isturf(loc))
balloon_alert(user, "can't butcher here!")
return ITEM_INTERACT_BLOCKING
balloon_alert(user, "butchering...")
if(!do_after(user, 0.75 SECONDS, src))
balloon_alert(user, "interrupted!")
return
balloon_alert(user, "butchering...")
if(!do_after(user, 0.75 SECONDS, src))
balloon_alert(user, "interrupted!")
return ITEM_INTERACT_BLOCKING
loc.balloon_alert(user, "butchered")
new /obj/item/food/meat/slab/mouse(loc)
qdel(src)
return
return ..()
loc.balloon_alert(user, "butchered")
new /obj/item/food/meat/slab/mouse(loc)
qdel(src)
return ITEM_INTERACT_SUCCESS
/obj/item/food/deadmouse/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(isnull(reagents) || !interacting_with.is_open_container())
+25 -15
View File
@@ -121,25 +121,35 @@
return TRUE
. = ..()
/obj/item/clipboard/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
if(istype(weapon, /obj/item/paper))
/obj/item/clipboard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(istype(tool, /obj/item/paper))
//Add paper into the clipboard
if(!user.transferItemToLoc(weapon, src))
return
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
if(top_paper)
UnregisterSignal(top_paper, COMSIG_ATOM_UPDATED_ICON)
RegisterSignal(weapon, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_top_paper_change))
top_paper = weapon
to_chat(user, span_notice("You clip [weapon] onto [src]."))
else if(istype(weapon, /obj/item/pen) && !pen)
RegisterSignal(tool, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_top_paper_change))
top_paper = tool
to_chat(user, span_notice("You clip [tool] onto [src]."))
update_appearance()
return ITEM_INTERACT_SUCCESS
if(istype(tool, /obj/item/pen) && !pen)
//Add a pen into the clipboard, attack (write) if there is already one
if(!usr.transferItemToLoc(weapon, src))
return
pen = weapon
to_chat(usr, span_notice("You slot [weapon] into [src]."))
else if(top_paper)
top_paper.attackby(user.get_active_held_item(), user)
update_appearance()
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
pen = tool
to_chat(user, span_notice("You slot [tool] into [src]."))
update_appearance()
return ITEM_INTERACT_SUCCESS
if(top_paper)
top_paper.item_interaction(user, user.get_active_held_item())
update_appearance()
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/clipboard/attack_self(mob/user)
add_fingerprint(usr)
+18 -14
View File
@@ -43,23 +43,27 @@
for(var/obj/item/obj in src)
obj.forceMove(loc)
/obj/structure/filingcabinet/attackby(obj/item/P, mob/living/user, list/modifiers, list/attack_modifiers)
if(P.tool_behaviour == TOOL_WRENCH && LAZYACCESS(modifiers, RIGHT_CLICK))
to_chat(user, span_notice("You begin to [anchored ? "unwrench" : "wrench"] [src]."))
if(P.use_tool(src, user, 20, volume=50))
to_chat(user, span_notice("You successfully [anchored ? "unwrench" : "wrench"] [src]."))
set_anchored(!anchored)
else if(P.w_class < WEIGHT_CLASS_NORMAL)
if(!user.transferItemToLoc(P, src))
return
to_chat(user, span_notice("You put [P] in [src]."))
/obj/structure/filingcabinet/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(tool.w_class < WEIGHT_CLASS_NORMAL)
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You put [tool] in [src]."))
icon_state = "[initial(icon_state)]-open"
sleep(0.5 SECONDS)
icon_state = initial(icon_state)
else if(!user.combat_mode || (P.item_flags & NOBLUDGEON))
to_chat(user, span_warning("You can't put [P] in [src]!"))
else
return ..()
return ITEM_INTERACT_SUCCESS
if(!user.combat_mode || (tool.item_flags & NOBLUDGEON))
to_chat(user, span_warning("You can't put [tool] in [src]!"))
return ITEM_INTERACT_BLOCKING
return NONE
/obj/structure/filingcabinet/wrench_act_secondary(mob/living/user, obj/item/tool)
to_chat(user, span_notice("You begin to [anchored ? "unwrench" : "wrench"] [src]."))
if(!tool.use_tool(src, user, 20, volume=50))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You successfully [anchored ? "unwrench" : "wrench"] [src]."))
set_anchored(!anchored)
return ITEM_INTERACT_SUCCESS
/obj/structure/filingcabinet/attack_hand(mob/living/carbon/user, list/modifiers)
. = ..()
+14 -14
View File
@@ -415,44 +415,44 @@ GAME_VERB_SRC(/obj/item/paper, rename, usr, "Rename paper", null)
user.put_in_hands(new_plane)
return new_plane
/obj/item/paper/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
/obj/item/paper/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
// Enable picking paper up by clicking on it with the clipboard or paper bin
if(istype(attacking_item, /obj/item/clipboard) || istype(attacking_item, /obj/item/paper_bin))
attacking_item.attackby(src, user)
return
if(istype(tool, /obj/item/clipboard) || istype(tool, /obj/item/paper_bin))
tool.item_interaction(user, src)
return ITEM_INTERACT_SUCCESS
// Handle writing items.
var/writing_stats = attacking_item.get_writing_implement_details()
var/writing_stats = tool.get_writing_implement_details()
if(!writing_stats)
ui_interact(user)
return ..()
return NONE
if(writing_stats["interaction_mode"] == MODE_WRITING)
if(!user.can_write(attacking_item))
return
if(!user.can_write(tool))
return ITEM_INTERACT_BLOCKING
if(get_total_length() >= MAX_PAPER_LENGTH)
to_chat(user, span_warning("This sheet of paper is full!"))
return
return ITEM_INTERACT_BLOCKING
ui_interact(user)
return
return ITEM_INTERACT_SUCCESS
// Handle stamping items.
if(writing_stats["interaction_mode"] == MODE_STAMPING)
if(!user.can_read(src) || user.is_blind())
//The paper's stampable window area is assumed approx 300x400
add_stamp(writing_stats["stamp_class"], rand(0, 300), rand(0, 400), rand(0, 360), writing_stats["stamp_icon_state"], stamp_icon = writing_stats["stamp_icon"])
user.visible_message(span_notice("[user] blindly stamps [src] with \the [attacking_item]!"))
to_chat(user, span_notice("You stamp [src] with \the [attacking_item] the best you can!"))
user.visible_message(span_notice("[user] blindly stamps [src] with \the [tool]!"))
to_chat(user, span_notice("You stamp [src] with \the [tool] the best you can!"))
playsound(src, 'sound/items/handling/standard_stamp.ogg', 50, vary = TRUE)
else
to_chat(user, span_notice("You ready your stamp over the paper! "))
ui_interact(user)
return
return ITEM_INTERACT_SUCCESS
ui_interact(user)
return ..()
return NONE
/// Secondary right click interaction to quickly stamp things
/obj/item/paper/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers)
+31 -26
View File
@@ -118,27 +118,29 @@
add_fingerprint(user)
return ..()
/obj/item/paper_bin/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
/obj/item/paper_bin/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(at_overlay_limit())
dump_contents(drop_location(), TRUE)
return
if(istype(I, /obj/item/paper))
var/obj/item/paper/paper = I
if(!user.transferItemToLoc(paper, src, silent = FALSE))
return
to_chat(user, span_notice("You put [paper] in [src]."))
paper_stack += paper
return ITEM_INTERACT_SUCCESS
if(istype(tool, /obj/item/paper))
if(!user.transferItemToLoc(tool, src, silent = FALSE))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You put [tool] in [src]."))
paper_stack += tool
total_paper += 1
update_appearance()
else if(istype(I, /obj/item/pen) && !bin_pen)
var/obj/item/pen/pen = I
if(!user.transferItemToLoc(pen, src, silent = FALSE))
return
to_chat(user, span_notice("You put [pen] in [src]."))
bin_pen = pen
return ITEM_INTERACT_SUCCESS
if(istype(tool, /obj/item/pen) && !bin_pen)
if(!user.transferItemToLoc(tool, src, silent = FALSE))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You put [tool] in [src]."))
bin_pen = tool
update_appearance()
else
return ..()
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/paper_bin/proc/at_overlay_limit()
return overlays.len >= MAX_ATOM_OVERLAYS - 1
@@ -253,16 +255,19 @@
/obj/item/paper_bin/bundlenatural/fire_act(exposed_temperature, exposed_volume)
qdel(src)
/obj/item/paper_bin/bundlenatural/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/paper/carbon))
to_chat(user, span_warning("[W] won't fit into [src]."))
return
if(W.get_sharpness())
if(W.use_tool(src, user, 1 SECONDS))
to_chat(user, span_notice("You slice the cable from [src]."))
deconstruct(TRUE)
else
..()
/obj/item/paper_bin/bundlenatural/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(istype(tool, /obj/item/paper/carbon))
to_chat(user, span_warning("[tool] won't fit into [src]."))
return ITEM_INTERACT_BLOCKING
if(tool.get_sharpness())
if(!tool.use_tool(src, user, 1 SECONDS))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You slice the cable from [src]."))
deconstruct(TRUE)
return ITEM_INTERACT_SUCCESS
return ..()
/obj/item/paper_bin/carbon
name = "carbon paper bin"
+7 -7
View File
@@ -80,16 +80,16 @@
user.put_in_hands(released_paper)
/obj/item/paperplane/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(IS_WRITING_UTENSIL(attacking_item))
/obj/item/paperplane/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(IS_WRITING_UTENSIL(tool))
to_chat(user, span_warning("You should unfold [src] before changing it!"))
return
else if(istype(attacking_item, /obj/item/stamp)) //we don't randomize stamps on a paperplane
internal_paper.attackby(attacking_item, user) //spoofed attack to update internal paper.
return ITEM_INTERACT_BLOCKING
if(istype(tool, /obj/item/stamp)) //we don't randomize stamps on a paperplane
internal_paper.item_interaction(user, tool) //spoofed attack to update internal paper.
update_appearance()
add_fingerprint(user)
return
return ..()
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/paperplane/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(iscarbon(hit_atom) && HAS_TRAIT(hit_atom, TRAIT_PAPER_MASTER))
+23 -24
View File
@@ -39,27 +39,27 @@
detailed_desc = span_notice("<i>As you sift through the papers, you slowly start to piece together what you're reading.</i>")
/obj/item/paperwork/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
. = ..()
if(.)
return
/obj/item/paperwork/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stamp))
return NONE
if(stamped || !istype(attacking_item, /obj/item/stamp))
return
if(stamped)
return ITEM_INTERACT_BLOCKING
if(istype(attacking_item, stamp_requested))
if(istype(tool, stamp_requested))
add_stamp()
to_chat(user, span_notice("You skim through the papers until you find a field reading 'STAMP HERE', and complete the paperwork."))
return TRUE
var/datum/action/item_action/chameleon/change/stamp/stamp_action = locate() in attacking_item.actions
if(isnull(stamp_action))
to_chat(user, span_warning("You hunt through the papers for somewhere to use [attacking_item], but can't find anything."))
return TRUE
return ITEM_INTERACT_SUCCESS
to_chat(user, span_notice("[attacking_item] morphs into the appropriate stamp, which you use to complete the paperwork."))
var/datum/action/item_action/chameleon/change/stamp/stamp_action = locate() in tool.actions
if(isnull(stamp_action))
to_chat(user, span_warning("You hunt through the papers for somewhere to use [tool], but can't find anything."))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("[tool] morphs into the appropriate stamp, which you use to complete the paperwork."))
stamp_action.update_look(stamp_requested)
add_stamp()
return TRUE
return ITEM_INTERACT_SUCCESS
/obj/item/paperwork/examine_more(mob/user)
. = ..()
@@ -233,16 +233,15 @@
else
. += span_notice("These appear to just be a photocopy of the original documents.")
/obj/item/paperwork/photocopy/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
if(istype(attacking_item, /obj/item/stamp/void) && !stamped && !voided)
to_chat(user, span_notice("You plant the [attacking_item] firmly onto the front of the documents."))
stamp_overlay = mutable_appearance('icons/obj/service/bureaucracy.dmi', "paper_stamp-void")
add_overlay(stamp_overlay)
voided = TRUE
stamped = TRUE //It won't get you any money, but it also can't LOSE you money now.
return
return ..()
/obj/item/paperwork/photocopy/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stamp/void) || stamped || voided)
return ..()
to_chat(user, span_notice("You plant the [tool] firmly onto the front of the documents."))
stamp_overlay = mutable_appearance('icons/obj/service/bureaucracy.dmi', "paper_stamp-void")
add_overlay(stamp_overlay)
voided = TRUE
stamped = TRUE //It won't get you any money, but it also can't LOSE you money now.
return ITEM_INTERACT_SUCCESS
//Ancient paperwork is a subtype of paperwork, meant to be used for any paperwork not spawned by the event.
//It doesn't have any of the flavor text that the event ones spawn with.
+24 -20
View File
@@ -178,26 +178,30 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
maptext_x = 4
maptext = MAPTEXT(current_number) //Finally, apply the maptext
/obj/machinery/ticket_machine/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
..()
if(istype(I, /obj/item/hand_labeler_refill))
if(!(ticket_number >= max_number))
to_chat(user, span_notice("[src] refuses [I]! There [max_number - ticket_number == 1 ? "is" : "are"] still [max_number - ticket_number] ticket\s left!"))
return
to_chat(user, span_notice("You start to refill [src]'s ticket holder (doing this will reset its ticket count!)."))
if(do_after(user, 3 SECONDS, target = src))
to_chat(user, span_notice("You insert [I] into [src] as it whirs nondescriptly."))
qdel(I)
ticket_number = 0
current_number = 0
if(tickets.len)
for(var/obj/item/ticket_machine_ticket/ticket in tickets)
ticket.audible_message(span_notice("\the [ticket] disperses!"), hearing_distance = SAMETILE_MESSAGE_RANGE)
qdel(ticket)
tickets.Cut()
max_number = initial(max_number)
update_appearance()
return
/obj/machinery/ticket_machine/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/hand_labeler_refill))
return NONE
if(!(ticket_number >= max_number))
to_chat(user, span_notice("[src] refuses [tool]! There [max_number - ticket_number == 1 ? "is" : "are"] still [max_number - ticket_number] ticket\s left!"))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You start to refill [src]'s ticket holder (doing this will reset its ticket count!)."))
if(!do_after(user, 3 SECONDS, target = src))
return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You insert [tool] into [src] as it whirs nondescriptly."))
qdel(tool)
ticket_number = 0
current_number = 0
if(tickets.len)
for(var/obj/item/ticket_machine_ticket/ticket in tickets)
ticket.audible_message(span_notice("\the [ticket] disperses!"), hearing_distance = SAMETILE_MESSAGE_RANGE)
qdel(ticket)
tickets.Cut()
max_number = initial(max_number)
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/machinery/ticket_machine/proc/reset_cooldown()
ready = TRUE
+22 -23
View File
@@ -12,16 +12,17 @@
var/obj/item/photo/displayed
pixel_shift = 30
/obj/item/wallframe/picture/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/photo))
if(!displayed)
if(!user.transferItemToLoc(I, src))
return
displayed = I
update_appearance()
else
to_chat(user, span_warning("\The [src] already contains a photo."))
..()
/obj/item/wallframe/picture/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/photo))
return NONE
if(displayed)
to_chat(user, span_warning("\The [src] already contains a photo."))
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
displayed = tool
update_appearance()
return ITEM_INTERACT_SUCCESS
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/wallframe/picture/attack_hand(mob/user, list/modifiers)
@@ -145,19 +146,17 @@
return ITEM_INTERACT_SUCCESS
/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
if(istype(I, /obj/item/photo))
if(framed)
to_chat(user, span_warning("\The [src] already contains a photo."))
return TRUE
var/obj/item/photo/P = I
if(!user.transferItemToLoc(P, src))
return
set_and_save_framed(P)
update_appearance()
return TRUE
..()
/obj/structure/sign/picture_frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/photo))
return NONE
if(framed)
to_chat(user, span_warning("\The [src] already contains a photo."))
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
return ITEM_INTERACT_BLOCKING
set_and_save_framed(tool)
update_appearance()
return ITEM_INTERACT_SUCCESS
/obj/structure/sign/picture_frame/attack_hand(mob/user, list/modifiers)
. = ..()
+11 -10
View File
@@ -79,16 +79,17 @@
/obj/item/photo/attack_self(mob/user)
user.examinate(src)
/obj/item/photo/attackby(obj/item/P, mob/user, list/modifiers, list/attack_modifiers)
if(IS_WRITING_UTENSIL(P))
if(!user.can_write(P))
return
var/txt = tgui_input_text(user, "What would you like to write on the back?", "Photo Writing", max_length = 128)
if(txt && user.can_perform_action(src))
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
scribble = txt
else
return ..()
/obj/item/photo/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!IS_WRITING_UTENSIL(tool))
return NONE
if(!user.can_write(tool))
return ITEM_INTERACT_BLOCKING
var/txt = tgui_input_text(user, "What would you like to write on the back?", "Photo Writing", max_length = 128)
if(!txt || !user.can_perform_action(src))
return ITEM_INTERACT_BLOCKING
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
scribble = txt
return ITEM_INTERACT_SUCCESS
/obj/item/photo/examine(mob/user)
. = ..()
+11 -12
View File
@@ -324,18 +324,17 @@
// attach a wire to a power machine - leads from the turf you are standing on
//almost never called, overwritten by all power machines but terminal and generator
/obj/machinery/power/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/coil = W
var/turf/T = user.loc
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !isfloorturf(T))
return
if(get_dist(src, user) > 1)
return
coil.place_turf(T, user)
else
return ..()
/obj/machinery/power/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(!istype(tool, /obj/item/stack/cable_coil))
return NONE
var/obj/item/stack/cable_coil/coil = tool
var/turf/userturf = user.loc
if(userturf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE || !isfloorturf(userturf))
return ITEM_INTERACT_BLOCKING
if(get_dist(src, user) > 1)
return ITEM_INTERACT_BLOCKING
coil.place_turf(userturf, user)
return ITEM_INTERACT_SUCCESS
///////////////////////////////////////////
// Powernet handling helpers