diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 588f315cfef..a11aa288557 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -541,6 +541,9 @@ /// Sent from /obj/item/update_weight_class(), to its loc. (obj/item/changed_item, old_w_class, new_w_class) #define COMSIG_ATOM_CONTENTS_WEIGHT_CLASS_CHANGED "atom_contents_weight_class_changed" +/// Sent from /obj/item/proc/animate_attack() : (atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override) +#define COMSIG_ITEM_ATTACK_ANIMATION "item_attack_animation" + ///Sent from /obj/item/skillchip/on_implant() #define COMSIG_SKILLCHIP_IMPLANTED "skillchip_implanted" @@ -588,3 +591,6 @@ /// Sent from /datum/wires/attach_assembly() : (atom/holder) #define COMSIG_ASSEMBLY_PRE_ATTACH "assembly_pre_attach" #define COMPONENT_CANCEL_ATTACH (1<<0) + +/// Sent from /obj/item/kinetic_crusher/proc/fire_kinetic_blast() : (atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer) +#define COMSIG_CRUSHER_FIRED_BLAST "crusher_fired_blast" diff --git a/code/datums/components/nuclear_bomb_operator.dm b/code/datums/components/nuclear_bomb_operator.dm index 6ab9330e5ca..0b623478da8 100644 --- a/code/datums/components/nuclear_bomb_operator.dm +++ b/code/datums/components/nuclear_bomb_operator.dm @@ -113,7 +113,7 @@ var/mob/mob_parent = parent if(!isopenturf(attacked_target)) INVOKE_ASYNC(held_disk, TYPE_PROC_REF(/obj/item, melee_attack_chain), mob_parent, attacked_target) - mob_parent.do_item_attack_animation(attacked_target, used_item = held_disk) + mob_parent.do_item_attack_animation(attacked_target, used_item = held_disk, animation_type = ATTACK_ANIMATION_BLUNT) return COMPONENT_CANCEL_ATTACK_CHAIN held_disk.forceMove(attacked_target) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 3f79b1568a0..4e4a405001b 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1499,14 +1499,7 @@ /atom/movable/proc/do_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, no_effect, fov_effect = TRUE, item_animation_override = null) if(!no_effect && (visual_effect_icon || used_item)) - var/animation_type = item_animation_override || ATTACK_ANIMATION_BLUNT - if (used_item && !item_animation_override) - switch(used_item.get_sharpness()) - if (SHARP_EDGED) - animation_type = ATTACK_ANIMATION_SLASH - if (SHARP_POINTY) - animation_type = ATTACK_ANIMATION_PIERCE - do_item_attack_animation(attacked_atom, visual_effect_icon, used_item, animation_type = animation_type) + do_item_attack_animation(attacked_atom, visual_effect_icon, used_item, animation_type = item_animation_override) if(attacked_atom == src) return //don't do an animation if attacking self diff --git a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm index 72fca5f3ccd..e0630e4ef4b 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm @@ -38,18 +38,6 @@ purchase_path = /obj/item/crusher_trophy/retool_kit cost_per_order = 150 -/datum/orderable_item/mining/crusher_retool_kit_harpoon - purchase_path = /obj/item/crusher_trophy/retool_kit/harpoon - cost_per_order = 150 - -/datum/orderable_item/mining/crusher_retool_kit_dagger - purchase_path = /obj/item/crusher_trophy/retool_kit/dagger - cost_per_order = 150 - -/datum/orderable_item/mining/crusher_retool_kit_glaive - purchase_path = /obj/item/crusher_trophy/retool_kit/glaive - cost_per_order = 150 - /datum/orderable_item/mining/resonator purchase_path = /obj/item/resonator cost_per_order = 710 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 4356264925e..dc0152b69e9 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1544,38 +1544,63 @@ // This is instant on byond's end, but to our clients this looks like a quick drop animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = old_transform, time = 3, easing = CUBIC_EASING) -/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, animation_type = ATTACK_ANIMATION_BLUNT) - if (visual_effect_icon) - var/image/attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon) - attack_image.plane = attacked_atom.plane + 1 - // Scale the icon. - attack_image.transform *= 0.4 - // The icon should not rotate. - attack_image.appearance_flags = APPEARANCE_UI - var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS) - var/matrix/copy_transform = new(transform) - animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), time = 0.3 SECONDS) - animate(time = 0.1 SECONDS) - animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT) +/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, animation_type) + if (!visual_effect_icon) + if (used_item) + used_item.animate_attack(src, attacked_atom, animation_type) return - if (isnull(used_item)) - return - - var/image/attack_image = isnull(used_item.attack_icon) ? image(icon = used_item) : image(icon = used_item.attack_icon, icon_state = used_item.attack_icon_state) + var/image/attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon) attack_image.plane = attacked_atom.plane + 1 - attack_image.pixel_w = used_item.base_pixel_x + used_item.base_pixel_w - attack_image.pixel_z = used_item.base_pixel_y + used_item.base_pixel_z + // Scale the icon. + attack_image.transform *= 0.4 + // The icon should not rotate. + attack_image.appearance_flags = APPEARANCE_UI + var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS) + var/matrix/copy_transform = new(transform) + animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), time = 0.3 SECONDS) + animate(time = 0.1 SECONDS) + animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT) + +/obj/item/proc/animate_attack(atom/movable/attacker, atom/attacked_atom, animation_type) + var/list/image_override = list() + var/list/animation_override = list() + var/used_icon_angle = icon_angle + var/list/angle_override = list() + SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_ANIMATION, attacker, attacked_atom, animation_type, image_override, animation_override, angle_override) + var/image/attack_image = null + if (!length(image_override)) + attack_image = isnull(attack_icon) ? image(icon = src) : image(icon = attack_icon, icon_state = attack_icon_state) + else + attack_image = image_override[1] + + if (length(animation_override)) + animation_type = animation_override[1] + else if (!animation_type) + switch (get_sharpness()) + if (SHARP_EDGED) + animation_type = ATTACK_ANIMATION_SLASH + if (SHARP_POINTY) + animation_type = ATTACK_ANIMATION_PIERCE + else + animation_type = ATTACK_ANIMATION_BLUNT + + if (length(angle_override)) + used_icon_angle = angle_override[1] + + attack_image.plane = attacked_atom.plane + 1 + attack_image.pixel_w = attacker.base_pixel_x + attacker.base_pixel_w - attacked_atom.base_pixel_x - attacked_atom.base_pixel_w + attack_image.pixel_z = attacker.base_pixel_y + attacker.base_pixel_z - attacked_atom.base_pixel_y - attacked_atom.base_pixel_z // Scale the icon. attack_image.transform *= 0.5 // The icon should not rotate. attack_image.appearance_flags = APPEARANCE_UI var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS) - var/matrix/copy_transform = new(transform) + var/matrix/copy_transform = new(attacker.transform) var/x_sign = 0 var/y_sign = 0 - var/direction = get_dir(src, attacked_atom) + var/direction = get_dir(attacker, attacked_atom) if (direction & NORTH) y_sign = -1 else if (direction & SOUTH) @@ -1606,7 +1631,7 @@ if (ATTACK_ANIMATION_PIERCE) var/attack_angle = dir2angle(direction) + rand(-7, 7) // Deducting 90 because we're assuming that icon_angle of 0 means an east-facing sprite - var/anim_angle = attack_angle - 90 - used_item.icon_angle + var/anim_angle = attack_angle - 90 - used_icon_angle var/angle_mult = 1 if (x_sign && y_sign) angle_mult = 1.4 @@ -1644,7 +1669,7 @@ var/x_rot_sign = 0 var/y_rot_sign = 0 var/attack_dir = (prob(50) ? 1 : -1) - var/anim_angle = dir2angle(direction) - 90 - used_item.icon_angle + var/anim_angle = dir2angle(direction) - 90 - used_icon_angle if (x_sign) y_rot_sign = attack_dir diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm index 1bdb29f82cf..2d5c3bcc476 100644 --- a/code/game/objects/items/hand_items.dm +++ b/code/game/objects/items/hand_items.dm @@ -509,7 +509,7 @@ to_chat(taker, span_nicegreen("[offerer] gives you \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!")) offerer.face_atom(taker) taker.face_atom(offerer) - offerer.do_item_attack_animation(taker, used_item = src) + offerer.do_item_attack_animation(taker, used_item = src, animation_type = ATTACK_ANIMATION_BLUNT) //We're still firing a shot here because I don't want to deal with some weird edgecase where direct impacting them with the projectile causes it to freak out because there's no angle or something blown_kiss.original = taker blown_kiss.fired_from = offerer diff --git a/code/modules/antagonists/heretic/structures/mawed_crucible.dm b/code/modules/antagonists/heretic/structures/mawed_crucible.dm index 47eedfb48db..67dfc47b7df 100644 --- a/code/modules/antagonists/heretic/structures/mawed_crucible.dm +++ b/code/modules/antagonists/heretic/structures/mawed_crucible.dm @@ -124,7 +124,7 @@ balloon_alert(user, "flask is full!") return ITEM_INTERACT_SUCCESS to_fill.reagents.add_reagent(/datum/reagent/eldritch, 50) - do_item_attack_animation(src, used_item = tool) + do_item_attack_animation(src, used_item = tool, animation_type = ATTACK_ANIMATION_BLUNT) current_mass-- balloon_alert(user, "refilled flask") return ITEM_INTERACT_SUCCESS diff --git a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm index 6493a096429..635ceb1d164 100644 --- a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm @@ -219,7 +219,8 @@ if(!isturf(proj_turf)) return var/obj/projectile/destabilizer/destabilizer = new(proj_turf) - destabilizer.icon_state = "[projectile_icon]" + SEND_SIGNAL(src, COMSIG_CRUSHER_FIRED_BLAST, target, user, destabilizer) + destabilizer.icon_state = projectile_icon for(var/obj/item/crusher_trophy/attached_trophy as anything in trophies) attached_trophy.on_projectile_fire(destabilizer, user) destabilizer.aim_projectile(target, user, modifiers) diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm index f5817bcaed5..a4ceb5a425e 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm @@ -1,52 +1,64 @@ /*! * Contains crusher trophies that are not obtained from fauna */ - -//cosmetic items for changing the crusher's look - +/// Cosmetic items for changing the crusher's look /obj/item/crusher_trophy/retool_kit - name = "crusher sword retool kit" - desc = "A toolkit for changing the crusher's appearance without affecting the device's function. This one will make it look like a sword." + name = "crusher retool kit" + desc = "A toolkit for changing the crusher's appearance without affecting the device's function." icon = 'icons/obj/mining.dmi' icon_state = "retool_kit" denied_type = /obj/item/crusher_trophy/retool_kit - /// Specifies the icon file in which the crusher's new state is stored. - var/retool_icon = 'icons/obj/mining.dmi' - ///Specifies the sprite/icon state which the crusher is changed to as an item. Should appear in the icons/obj/mining.dmi file with accompanying "lit" and "recharging" sprites - var/retool_icon_state = "crusher_sword" - ///Specifies the icon state for the crusher's appearance in hand. Should appear in both retool_lefthand_file and retool_righthand_file. - var/retool_inhand_icon = "crusher_sword" - ///For if the retool kit changes the projectile's appearance. The sprite should be in icons/obj/weapons/guns/projectiles.dmi. - var/retool_projectile_icon = "pulse1" - /// Specifies the left hand inhand icon file. Don't forget to set the right hand file as well. - var/retool_lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi' - /// Specifies the right hand inhand icon file. Don't forget to set the left hand file as well. - var/retool_righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi' - /// Specifies the X dimensions of the new inhand, only relevant with different inhand files. - var/retool_inhand_x = 32 - /// Specifies the Y dimensions of the new inhand, only relevant with different inhand files. - var/retool_inhand_y = 32 + + /// Currently picked crusher reskin + var/datum/crusher_skin/active_skin = /datum/crusher_skin/sword + /// If this kit forces some specific skin, or can pick between subtypes + var/forced_skin + +/obj/item/crusher_trophy/retool_kit/Destroy(force) + if (istype(active_skin)) + QDEL_NULL(active_skin) + return ..() /obj/item/crusher_trophy/retool_kit/effect_desc() - return "the crusher to have the appearance of a sword" + return "the crusher to have the appearance of \a [active_skin::name]" /obj/item/crusher_trophy/retool_kit/add_to(obj/item/kinetic_crusher/pkc, mob/user) + if (!forced_skin) + var/list/choices = list() + for (var/datum/crusher_skin/skin as anything in subtypesof(/datum/crusher_skin)) + if (skin::normal_skin) + choices[skin] = icon(skin::retool_icon || 'icons/obj/mining.dmi', skin::retool_icon_state) + var/datum/crusher_skin/chosen_skin = show_radial_menu(user, src, choices, tooltips = TRUE, require_near = TRUE) + if (!chosen_skin) + return + active_skin = chosen_skin + else + active_skin = forced_skin . = ..() if(!.) return - - pkc.icon = retool_icon - pkc.icon_state = retool_icon_state - pkc.current_inhand_icon_state = retool_inhand_icon - pkc.projectile_icon = retool_projectile_icon - pkc.lefthand_file = retool_lefthand_file - pkc.righthand_file = retool_righthand_file - pkc.inhand_x_dimension = retool_inhand_x - pkc.inhand_y_dimension = retool_inhand_y + active_skin = new active_skin(pkc) + if (active_skin.retool_icon) + pkc.icon = active_skin.retool_icon + pkc.icon_state = active_skin.retool_icon_state + pkc.current_inhand_icon_state = active_skin.retool_inhand_icon + if (active_skin.retool_projectile_icon) + pkc.projectile_icon = active_skin.retool_projectile_icon + // Should either have both, or neither + if (active_skin.retool_lefthand_file) + pkc.lefthand_file = active_skin.retool_lefthand_file + pkc.righthand_file = active_skin.retool_righthand_file + if (active_skin.retool_inhand_x) + pkc.inhand_x_dimension = active_skin.retool_inhand_x + if (active_skin.retool_inhand_y) + pkc.inhand_y_dimension = active_skin.retool_inhand_y pkc.update_appearance() pkc.update_slot_icon() /obj/item/crusher_trophy/retool_kit/remove_from(obj/item/kinetic_crusher/pkc) + var/skin_type = active_skin.type + qdel(active_skin) + active_skin = skin_type pkc.icon = initial(pkc.icon) pkc.icon_state = initial(pkc.icon_state) pkc.current_inhand_icon_state = initial(pkc.current_inhand_icon_state) @@ -59,28 +71,111 @@ pkc.update_slot_icon() return ..() -/obj/item/crusher_trophy/retool_kit/harpoon - name = "crusher harpoon retool kit" - desc = "A toolkit for changing the crusher's appearance without affecting the device's function. This one will make it look like a harpoon." +/// Alternate PKC skins +/datum/crusher_skin + /// Name of the modification + var/name = "error that should be reported to coders" + /// Specifies the icon file in which the crusher's new state is stored. + var/retool_icon = null + ///Specifies the sprite/icon state which the crusher is changed to as an item. Should appear in the icons/obj/mining.dmi file with accompanying "lit" and "recharging" sprites + var/retool_icon_state = "ipickaxe" + ///Specifies the icon state for the crusher's appearance in hand. Should appear in both retool_lefthand_file and retool_righthand_file. + var/retool_inhand_icon = "ipickaxe" + ///For if the retool kit changes the projectile's appearance. The sprite should be in icons/obj/weapons/guns/projectiles.dmi. + var/retool_projectile_icon = null + /// Specifies the left hand inhand icon file. Don't forget to set the right hand file as well. + var/retool_lefthand_file = null + /// Specifies the right hand inhand icon file. Don't forget to set the left hand file as well. + var/retool_righthand_file = null + /// Specifies the X dimensions of the new inhand, only relevant with different inhand files. + var/retool_inhand_x = null + /// Specifies the Y dimensions of the new inhand, only relevant with different inhand files. + var/retool_inhand_y = null + /// Can this skin be normally selected by a generic retool kit? + var/normal_skin = TRUE + /// Crusher this skin is attached to + var/obj/item/kinetic_crusher/crusher + +/datum/crusher_skin/New(obj/item/kinetic_crusher/new_crusher) + . = ..() + crusher = new_crusher + +/datum/crusher_skin/Destroy(force) + crusher = null + return ..() + +/datum/crusher_skin/sword + name = "sword" + retool_icon_state = "crusher_sword" + retool_inhand_icon = "crusher_sword" + +/datum/crusher_skin/harpoon + name = "harpoon" retool_icon_state = "crusher_harpoon" retool_inhand_icon = "crusher_harpoon" retool_projectile_icon = "pulse_harpoon" -/obj/item/crusher_trophy/retool_kit/harpoon/effect_desc() - return "the crusher to have the appearance of a harpoon" +/datum/crusher_skin/harpoon/New(obj/item/kinetic_crusher/new_crusher) + . = ..() + RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation)) -/obj/item/crusher_trophy/retool_kit/dagger - name = "crusher dagger retool kit" - desc = "A toolkit for changing the crusher's appearance without affecting the device's function. This one will make it look like a dual dagger and mini-blaster on a chain." +/datum/crusher_skin/harpoon/Destroy(force) + UnregisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION) + return ..() + +/datum/crusher_skin/harpoon/proc/on_attack_animation(obj/item/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override) + SIGNAL_HANDLER + + // If nothing is forcing an animation type, attack with a piercing animation because we're a harpoon + if (!animation_type) + animation_override += ATTACK_ANIMATION_PIERCE + +/datum/crusher_skin/dagger + name = "dual dagger and blaster" retool_icon_state = "crusher_dagger" retool_inhand_icon = "crusher_dagger" + /// Are we doing a blaster animation right now? + var/blaster_strike = FALSE -/obj/item/crusher_trophy/retool_kit/dagger/effect_desc() - return "the crusher to have the appearance of a dual dagger and blaster" +/datum/crusher_skin/dagger/New(obj/item/kinetic_crusher/new_crusher) + . = ..() + RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation)) + RegisterSignal(crusher, COMSIG_CRUSHER_FIRED_BLAST, PROC_REF(on_fired_blast)) -/obj/item/crusher_trophy/retool_kit/glaive - name = "crusher glaive retool kit" - desc = "A toolkit for changing the crusher's appearance without affecting the device's function. This one will make it look like a glaive, with a longer, thinner blade." +/datum/crusher_skin/dagger/Destroy(force) + UnregisterSignal(crusher, list(COMSIG_ITEM_ATTACK_ANIMATION, COMSIG_CRUSHER_FIRED_BLAST)) + return ..() + +/datum/crusher_skin/dagger/proc/on_attack_animation(obj/item/kinetic_crusher/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override) + SIGNAL_HANDLER + + // If we've been forcefully assigned an animation type already, we shouldn't do the custom attack animation logic + if (animation_type) + return + + if (isliving(attacked_atom)) + var/mob/living/target = attacked_atom + if (blaster_strike) + image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_blaster") + angle_override += 0 + animation_override += ATTACK_ANIMATION_PIERCE + blaster_strike = FALSE + return + + if (target.has_status_effect(/datum/status_effect/crusher_mark)) + animation_override += ATTACK_ANIMATION_PIERCE + + image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_melee") + +/datum/crusher_skin/dagger/proc/on_fired_blast(obj/item/kinetic_crusher/source, atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer) + SIGNAL_HANDLER + + if (isliving(target) && get_dist(target, user) <= 1) + blaster_strike = TRUE + user.do_item_attack_animation(target, used_item = source) + +/datum/crusher_skin/glaive + name = "glaive" retool_icon_state = "crusher_glaive" retool_inhand_icon = "crusher_glaive" retool_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' @@ -88,16 +183,17 @@ retool_inhand_x = 64 retool_inhand_y = 64 -/obj/item/crusher_trophy/retool_kit/glaive/effect_desc() - return "the crusher to have the appearance of a glaive" - /obj/item/crusher_trophy/retool_kit/ashenskull name = "ashen skull" desc = "It burns with the flame of the necropolis, whispering in your ear. It demands to be bound to a suitable weapon." icon_state = "retool_kit_skull" - retool_icon_state = "crusher_skull" - retool_inhand_icon = "crusher_skull" - retool_projectile_icon = "pulse_skull" + forced_skin = /datum/crusher_skin/ashen_skull /obj/item/crusher_trophy/retool_kit/ashenskull/effect_desc() return "the crusher to appear corrupted by infernal powers" + +/datum/crusher_skin/ashen_skull + retool_icon_state = "crusher_skull" + retool_inhand_icon = "crusher_skull" + retool_projectile_icon = "pulse_skull" + normal_skin = FALSE diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi index 5a933d9d468..7a83f5d05b1 100644 Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ