Cain & Abel (new mining loot) (#89455)

## About The Pull Request
adds the Cain & Abel to the lootpool of the colossus!

![daggerpic](https://github.com/user-attachments/assets/d0e0c5f9-bace-4010-854a-3ea65e764499)

these are a set of angelic twinblades bound together by some chains. The
long chains allow u to attack mobs from a distance (2 tiles max) and at
very FAST speed, and come with a few new mechanics:

-Attacking a mob with the cain and abel grants you a special whisp that
follows your character. these whisps empower ur next melee attacks u can
collect a maximum of up to 6 whisps, (their bonuses stack), after which
they reset. If u get hit by a mob once, you'll lose ur whisps (and ur
melee bonus), so ull have to regain them by rebuilding up ur combo. u
can also choose to sacrifice ur whisps by firing them at mobs (by right
clicking them) for some hefty damage (this again means u'll lose them)

https://github.com/user-attachments/assets/0a1738db-9fa4-4226-ac80-334f5e97cfa5

-u can also choose to hurl one of ur daggers at enemies, there's 2 throw
modes u can toggle between by pressing Z while holding ur weapon.
1- On launch mode, u can throw one of ur daggers at a tile, afterwhich
the chains will rapidly pull u towards it, making for some cool getaways
in tense situations. this puts throw mode on a 7 second cooldown
2- On crystal mode, u can hurl a dagger at an enemy or at a tile. Spiked
crystals will errupt on nearby floors, dealing some damage to nearby
mobs and stunning them for 2 seconds (bosses dont get stunned tho). puts
throw mode on a 15 second cooldown

https://github.com/user-attachments/assets/665b9cf4-c5a1-4263-a36b-86e3f35d0ae5

-Lastly is the swing ability. This will swing ur daggers around u,
dealing AOE damage to nearby mobs, and makes u block all melee attacks,
tentacle attacks, and deflect incoming projectile attacks (could for
example be used to deflect the colossus' shotgun blast back to it). ull
only block attacks while the animation is active, which lasts a good
1.75 seconds, and is at a 20 second cooldown.

https://github.com/user-attachments/assets/073e5324-af5b-45ab-912e-5bcaa13fc728

Here's a short clip of me using them to fight a colossus and a bubblegum
https://www.youtube.com/watch?v=kp5Hu16dHPQ&ab_channel=Kobsa

## Why It's Good For The Game
adds a new fun weapon with a few deep mechanics to the game. also makes
taking down colossi alot more rewarding.

## Changelog
🆑
add: adds the cain and abel to the colossus lootpool!
/🆑
# Conflicts:
#	icons/mob/inhands/equipment/kitchen_lefthand.dmi
This commit is contained in:
Ben10Omintrix
2025-03-29 05:28:51 +02:00
committed by The Sharkening
parent 5667648f8a
commit abb8f539bd
33 changed files with 589 additions and 19 deletions

View File

@@ -88,8 +88,8 @@
visuals.layer = beam_layer
visuals.update_appearance()
Draw()
RegisterSignal(origin, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(redrawing))
RegisterSignals(origin, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(redrawing))
RegisterSignals(target, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING), PROC_REF(redrawing))
/**
* Triggered by signals set up when the beam is set up. If it's still sane to create a beam, it removes the old beam, creates a new one. Otherwise it kills the beam.
@@ -101,7 +101,9 @@
*/
/datum/beam/proc/redrawing(atom/movable/mover, atom/oldloc, direction)
SIGNAL_HANDLER
if(origin && target && get_dist(origin,target)<max_distance && origin.z == target.z)
if(QDELING(src))
return
if(!QDELETED(origin) && !QDELETED(target) && get_dist(origin,target)<max_distance && origin.z == target.z)
QDEL_LIST(elements)
INVOKE_ASYNC(src, PROC_REF(Draw))
else
@@ -110,8 +112,8 @@
/datum/beam/Destroy()
QDEL_LIST(elements)
QDEL_NULL(visuals)
UnregisterSignal(origin, COMSIG_MOVABLE_MOVED)
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
UnregisterSignal(origin, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
UnregisterSignal(target, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
target = null
origin = null
return ..()

View File

@@ -1347,7 +1347,7 @@
return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, gentle)
///If this returns FALSE then callback will not be called.
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE, throw_datum_typepath = /datum/thrownthing)
. = FALSE
if(QDELETED(src))
@@ -1394,7 +1394,7 @@
else
target_zone = thrower.zone_selected
var/datum/thrownthing/thrown_thing = new(src, target, get_dir(src, target), range, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
var/datum/thrownthing/thrown_thing = new throw_datum_typepath(src, target, get_dir(src, target), range, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)

View File

@@ -24,6 +24,10 @@
/// Angle of the icon, used for piercing and slashing attack animations, clockwise from *east-facing* sprites
var/icon_angle = 0
///icon file for an alternate attack icon
var/attack_icon
///icon state for an alternate attack icon
var/attack_icon_state
///Icon file for mob worn overlays.
var/icon/worn_icon
@@ -909,7 +913,7 @@
else
playsound(hit_atom, 'sound/items/weapons/throwtap.ogg', volume, TRUE, -1)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE, throw_type_path = /datum/thrownthing)
if(HAS_TRAIT(src, TRAIT_NODROP))
return
thrownby = WEAKREF(thrower)
@@ -1687,7 +1691,7 @@
if (isnull(used_item))
return
var/image/attack_image = image(icon = used_item)
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)
attack_image.plane = attacked_atom.plane + 1
// Scale the icon.
attack_image.transform *= 0.5

View File

@@ -44,7 +44,7 @@
span_notice("You hear repeated smashing!"),
)
/obj/item/table_clock/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, gentle, quickstart)
/obj/item/table_clock/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, gentle, quickstart, throw_type_path = /datum/thrownthing)
. = ..()
if(!.)
return

View File

@@ -548,7 +548,7 @@
///Reference of the mob we will attempt to snare
var/datum/weakref/ensnare_mob_ref
/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle = FALSE, quickstart = TRUE)
/obj/item/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle = FALSE, quickstart = TRUE, throw_type_path = /datum/thrownthing)
if(!..())
return
playsound(src.loc,'sound/items/weapons/bolathrow.ogg', 75, TRUE)

View File

@@ -18,7 +18,7 @@
. = ..()
. += span_notice("Throw this at objects or creatures to freeze them, it will boomerang back so be cautious!")
/obj/item/freeze_cube/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle, quickstart = TRUE)
/obj/item/freeze_cube/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle, quickstart = TRUE, throw_type_path = /datum/thrownthing)
. = ..()
if(!.)
return

View File

@@ -0,0 +1,153 @@
#define THROW_MODE_CRYSTALS "throw_mode_crystals"
#define THROW_MODE_LAUNCH "throw_mode_launch"
/obj/item/cain_and_abel
name = "Cain & Abel"
desc = "I cry I pray mon Dieu."
icon = 'icons/obj/mining_zones/artefacts.dmi'
lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi' //same sprite as lefthand
icon_state = "cain_and_abel"
inhand_icon_state = "cain_and_abel"
attack_verb_continuous = list("attacks", "saws", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "saw", "slice", "tear", "lacerate", "rip", "dice", "cut")
force = 15
attack_speed = 6
actions_types = list(/datum/action/cooldown/dagger_swing)
hitsound = 'sound/items/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_SMALL
sharpness = SHARP_EDGED
light_range = 3
light_power = 2
light_color = "#3db9db"
reach = 2
attack_icon = 'icons/effects/effects.dmi'
attack_icon_state = "cain_abel_attack"
///our current combo count
var/combo_count = 0
///the maximum combo we can reach
var/max_combo = 6
///percentage boost we get on every combo
var/damage_boost = 1.15
///pixel offsets of our wisps
var/static/list/wisp_offsets = list(
list(9, 12),
list(14, 0),
list(9, -12),
list(-9, 12),
list(-14, 0),
list(-9, -12),
)
///what throw mode we're using
var/throw_mode = THROW_MODE_CRYSTALS
///flames we have up!
var/list/current_wisps = list()
///cooldown till we can throw blades again
COOLDOWN_DECLARE(throw_cooldown)
/obj/item/cain_and_abel/Initialize(mapload)
. = ..()
AddComponent(/datum/component/two_handed, require_twohands = TRUE, force_unwielded = force, force_wielded = force)
/obj/item/cain_and_abel/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
if(!isliving(old_loc))
return
unset_user(old_loc)
/obj/item/cain_and_abel/proc/unset_user(mob/living/source)
if(HAS_TRAIT(source, TRAIT_RELAYING_ATTACKER))
source.RemoveElement(/datum/element/relay_attackers)
set_combo(new_value = 0, user = source)
UnregisterSignal(source, list(COMSIG_ATOM_WAS_ATTACKED))
/obj/item/cain_and_abel/equipped(mob/user, slot)
. = ..()
if(!(slot & ITEM_SLOT_HANDS))
unset_user(user)
return
if(!HAS_TRAIT(user, TRAIT_RELAYING_ATTACKER))
user.AddElement(/datum/element/relay_attackers)
RegisterSignal(user, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked))
/obj/item/cain_and_abel/proc/on_attacked(datum/source, atom/attacker, attack_flags)
SIGNAL_HANDLER
set_combo(new_value = 0, user = source)
/obj/item/cain_and_abel/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(get_dist(interacting_with, user) > 9 || interacting_with.z != user.z)
return NONE
if(!length(current_wisps))
user.balloon_alert(user, "no wisps!")
return ITEM_INTERACT_BLOCKING
for(var/index in 0 to (length(current_wisps) - 1))
addtimer(CALLBACK(src, PROC_REF(fire_wisp), user, interacting_with), index * 0.15 SECONDS)
set_combo(new_value = 0, user = user)
return ITEM_INTERACT_SUCCESS
/obj/item/cain_and_abel/attack(mob/living/target, mob/living/carbon/human/user)
if(!istype(target) || target.mob_size < MOB_SIZE_LARGE || target.stat == DEAD)
attack_speed = CLICK_CD_MELEE
return ..()
attack_speed = initial(attack_speed)
var/old_force = force
var/bonus_value = combo_count || 1
force = CEILING((bonus_value * damage_boost) * force, 1)
. = ..()
force = old_force
set_combo(new_value = combo_count + 1, user = user)
/obj/item/cain_and_abel/attack_self(mob/user)
. = ..()
if(.)
return TRUE
throw_mode = (throw_mode == THROW_MODE_CRYSTALS) ? THROW_MODE_LAUNCH : THROW_MODE_CRYSTALS
user.balloon_alert(user, "crystals [throw_mode == THROW_MODE_CRYSTALS ? "activated" : "deactivated"]")
return TRUE
/obj/item/cain_and_abel/proc/set_combo(new_value, mob/living/user)
combo_count = (new_value <= max_combo) ? new_value : 0
handle_wisps(user)
/obj/item/cain_and_abel/proc/handle_wisps(mob/living/user)
var/should_remove = length(current_wisps) > combo_count
var/wisps_to_alter = abs(combo_count - length(current_wisps))
for(var/i = 1, i <= wisps_to_alter, i++)
if(!should_remove)
add_wisp(user)
continue
var/obj/my_wisp = current_wisps[i]
remove_wisp(my_wisp)
/obj/item/cain_and_abel/proc/add_wisp(mob/living/user)
var/obj/effect/overlay/blood_wisp/new_wisp = new(src)
current_wisps += new_wisp
var/list/position = wisp_offsets[length(current_wisps)]
user.vis_contents += new_wisp
new_wisp.pixel_x = position[1]
new_wisp.pixel_y = position[2]
RegisterSignal(new_wisp, COMSIG_QDELETING, PROC_REF(on_wisp_delete))
/obj/item/cain_and_abel/proc/on_wisp_delete(datum/source)
SIGNAL_HANDLER
current_wisps -= source
UnregisterSignal(source, COMSIG_QDELETING)
/obj/item/cain_and_abel/proc/fire_wisp(atom/user, atom/target)
user.fire_projectile(/obj/projectile/dagger_wisp, target)
/obj/item/cain_and_abel/proc/remove_wisp(obj/wisp_to_remove)
animate(wisp_to_remove, alpha = 0, time = 0.2 SECONDS)
QDEL_IN(wisp_to_remove, 0.2 SECONDS)

View File

@@ -0,0 +1,26 @@
/datum/action/cooldown/dagger_swing
name = "Dagger swing"
desc = "Swing your daggers around."
button_icon = 'icons/obj/mining_zones/artefacts.dmi'
button_icon_state = "cain_and_abel"
background_icon_state = "bg_default"
overlay_icon_state = "bg_default_border"
cooldown_time = 20 SECONDS
/datum/action/cooldown/dagger_swing/Activate(atom/target_atom)
. = ..()
var/mob/living/living_owner = owner
living_owner.apply_status_effect(/datum/status_effect/dagger_swinging)
var/static/list/possible_sounds = list(
'sound/items/weapons/cain_and_abel/dagger_slash_1.ogg',
'sound/items/weapons/cain_and_abel/dagger_slash_2.ogg',
'sound/items/weapons/cain_and_abel/dagger_slash_3.ogg',
'sound/items/weapons/cain_and_abel/dagger_slash_4.ogg',
'sound/items/weapons/cain_and_abel/dagger_slash_5.ogg',
'sound/items/weapons/cain_and_abel/dagger_slash_6.ogg',
)
var/list/sounds_to_pick_from = possible_sounds.Copy()
for(var/index in 0 to 5)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), owner, pick_n_take(sounds_to_pick_from), 65, TRUE), 0.15 SECONDS * index)

View File

@@ -0,0 +1,210 @@
//effect when we're swinging wildly around
/obj/effect/temp_visual/dagger_slash
name = "Blood Wisp"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE
layer = ABOVE_HUD_PLANE
icon = 'icons/effects/160x160.dmi'
icon_state = "dagger_slash"
pixel_y = -64
base_pixel_y = -64
pixel_x = -64
base_pixel_x = -64
duration = 1.75 SECONDS
/obj/effect/temp_visual/dagger_slash/Initialize(mapload)
. = ..()
animate(src, alpha = 0, time = 1.75 SECONDS)
//flames we collect around our body
/obj/effect/overlay/blood_wisp
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE
layer = ABOVE_HUD_PLANE
icon = 'icons/effects/effects.dmi'
icon_state = "blood_wisp"
light_power = 2
light_range = 2
light_color = "#d74e63"
//blade we hurl
/obj/projectile/dagger
name = "Cain"
icon = 'icons/effects/effects.dmi'
icon_state = "cain_abel_attack"
damage = 10
range = 9
light_power = 2
light_range = 2
light_color = "#589ac9"
speed = 3
can_hit_turfs = TRUE
hitsound = 'sound/items/weapons/zipline_hit.ogg'
///effect we leave by after hit
var/effect_left = /obj/effect/temp_visual/dagger_engraved
/obj/projectile/dagger/proc/dagger_effects(atom/target)
if(QDELETED(target))
return null
var/turf/target_turf = get_turf(target)
if(isgroundlessturf(target_turf))
return null
var/obj/effect/temp_visual/dagger_engraved/engraved = new effect_left(target_turf)
firer.Beam(engraved, icon_state = "chain", icon = 'icons/obj/mining_zones/artefacts.dmi', maxdistance = 9, layer = BELOW_MOB_LAYER)
return engraved
/obj/projectile/dagger/crystal
effect_left = /obj/effect/temp_visual/dagger_engraved/crystals
/obj/projectile/dagger/launch
effect_left = /obj/effect/temp_visual/dagger_engraved/launch
/obj/projectile/dagger/launch/dagger_effects(atom/target)
. = ..()
if(isnull(.))
return
var/obj/effect/temp_visual/dagger_engraved/launch/launching_dagger = .
launching_dagger.launch(firer)
//effect when monsters step on our crystals
/obj/effect/temp_visual/dagger_lightning
icon = 'icons/effects/effects.dmi'
icon_state = "lightning"
light_color = "#3d50db"
duration = 1.25 SECONDS
//dagger engraved to the floor
/obj/effect/temp_visual/dagger_engraved
icon = 'icons/effects/effects.dmi'
icon_state = "cain_abel_engraved"
light_color = "#5767e1"
light_power = 2
light_range = 2
duration = 3 SECONDS
//the dagger thatll launch us toward it
/obj/effect/temp_visual/dagger_engraved/launch
/obj/effect/temp_visual/dagger_engraved/launch/proc/launch(mob/living/firer)
firer.throw_at(target = src, range = 9, speed = 1, spin = FALSE, gentle = TRUE, throw_type_path = /datum/thrownthing/dagger_launch)
//throw datum the cain and abel applies
/datum/thrownthing/dagger_launch
///traits we apply to the user when being launched
var/static/list/traits_on_launch = list(
TRAIT_IMMOBILIZED,
TRAIT_MOVE_FLOATING,
)
/datum/thrownthing/dagger_launch/New(thrownthing, target, init_dir, maxrange, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
. = ..()
if(isnull(thrownthing))
return
var/atom/thrown_atom = thrownthing
thrown_atom.add_traits(traits_on_launch, REF(src))
new /obj/effect/temp_visual/mook_dust(get_turf(thrownthing))
/datum/thrownthing/dagger_launch/finalize(hit, target)
if(thrownthing)
new /obj/effect/temp_visual/mook_dust(get_turf(thrownthing))
return ..()
/datum/thrownthing/dagger_launch/Destroy()
if(thrownthing)
thrownthing.remove_traits(traits_on_launch, REF(src))
var/obj/effect/temp_visual/dagger_engraved/launch/target_dagger = initial_target?.resolve()
if(istype(target_dagger))
qdel(target_dagger)
return ..()
//dagger thatll spring up crystals
/obj/effect/temp_visual/dagger_engraved/crystals
light_power = 1
light_range = 1
/obj/effect/temp_visual/dagger_engraved/crystals/Initialize(mapload)
. = ..()
for(var/index in 0 to 2)
addtimer(CALLBACK(src, PROC_REF(generate_crystals), index), index * 0.5 SECONDS)
/obj/effect/temp_visual/dagger_engraved/crystals/proc/generate_crystals(range)
if(range == 0)
new /obj/effect/temp_visual/dagger_crystal(get_turf(src))
return
playsound(src, 'sound/items/weapons/crystal_dagger_sound.ogg', 60, vary = TRUE, pressure_affected = FALSE)
var/list/turfs_to_crystalize = border_diamond_range_turfs(src, range)
for(var/turf/turf_to_crystalize as anything in turfs_to_crystalize)
new /obj/effect/temp_visual/dagger_crystal(turf_to_crystalize)
//effect when our whisps hit something
/obj/effect/temp_visual/wisp_explosion
icon = 'icons/effects/effects.dmi'
icon_state = "wisp_hit"
layer = ABOVE_ALL_MOB_LAYER
light_power = 2
light_range = 2
light_color = "#d74e63"
duration = 0.5 SECONDS
/obj/effect/temp_visual/wisp_explosion/Initialize(mapload)
. = ..()
playsound(get_turf(src), 'sound/items/weapons/effects/blood_wisp_explode.ogg', 60, vary = TRUE, pressure_affected = FALSE)
//painful crystals to step on
/obj/effect/temp_visual/dagger_crystal
icon = 'icons/effects/effects.dmi'
icon_state = "cain_abel_crystal"
duration = 3 SECONDS
light_range = 3
light_power = 2
light_color = "#3db9db"
///damage we apply to mobs who step on us
var/applied_damage = 50
/obj/effect/temp_visual/dagger_crystal/Initialize(mapload)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
for(var/mob/living/victim in get_turf(src))
if(victim.mob_size < MOB_SIZE_LARGE)
continue
apply_crystal_effects(victim)
addtimer(CALLBACK(src, PROC_REF(dissappear_gracefully)), duration - 1 SECONDS)
/obj/effect/temp_visual/dagger_crystal/proc/on_entered(datum/source, mob/living/entered_living)
SIGNAL_HANDLER
if(istype(entered_living))
apply_crystal_effects(entered_living)
/obj/effect/temp_visual/dagger_crystal/proc/apply_crystal_effects(mob/living/victim)
victim.apply_status_effect(/datum/status_effect/dagger_stun)
playsound(victim, 'sound/items/weapons/bladeslice.ogg', 50, FALSE)
victim.apply_damage(victim.mob_size >= MOB_SIZE_LARGE ? applied_damage : applied_damage / 10, BRUTE)
/obj/effect/temp_visual/dagger_crystal/proc/dissappear_gracefully()
animate(src, alpha = 0, time = 0.9 SECONDS)
//wisp we hurl at monsters
/obj/projectile/dagger_wisp
name = "dagger wisp"
damage = 25
armor_flag = BOMB
light_power = 2
light_range = 2
light_color = "#d74e63"
icon = 'icons/effects/effects.dmi'
icon_state = "blood_wisp"
/obj/projectile/dagger_wisp/Initialize(mapload)
. = ..()
transform = transform.Scale(1, -1)
/obj/projectile/dagger_wisp/on_hit(atom/target, blocked, pierce_hit)
. = ..()
new /obj/effect/temp_visual/wisp_explosion(get_turf(target))

View File

@@ -0,0 +1,98 @@
///status effect applied to us when we're wildly swinging
/datum/status_effect/dagger_swinging
id = "dagger swinging"
tick_interval = 0.25 SECONDS
duration = 1.75 SECONDS
alert_type = null
///base damage we apply to mobs near us
var/base_damage = 5
/datum/status_effect/dagger_swinging/on_apply()
. = ..()
if(!.)
return
var/obj/effect/temp_visual/dagger_slash/slash_effect = new
owner.vis_contents += slash_effect
ADD_TRAIT(owner, TRAIT_TENTACLE_IMMUNE, REF(src))
RegisterSignal(owner, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(hit_by_projectile))
RegisterSignal(owner, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(block_attack))
/datum/status_effect/dagger_swinging/tick(seconds_between_ticks)
if(!isturf(owner.loc))
return
var/mob_hit = FALSE
for(var/mob/living/target_mob in oview(owner, 1))
target_mob.apply_damage(target_mob.mob_size < MOB_SIZE_LARGE ? base_damage : base_damage * 5, BRUTE)
mob_hit = TRUE
if(mob_hit)
playsound(owner, 'sound/items/weapons/bladeslice.ogg', 75, FALSE) //just play it once
/datum/status_effect/dagger_swinging/proc/hit_by_projectile(mob/living/swinger, obj/projectile/projectile, hit_area)
SIGNAL_HANDLER
if(!isturf(owner.loc))
return NONE
playsound(swinger, 'sound/items/weapons/parry.ogg', 75, TRUE)
var/obj/effect/temp_visual/guardian/phase/out/parry_effect = new
parry_effect.pixel_x = rand(-4, 4)
parry_effect.pixel_y = rand(-10, 10)
owner.vis_contents += parry_effect
projectile.firer = swinger
projectile.set_angle(-projectile.angle)
return COMPONENT_BULLET_PIERCED
/datum/status_effect/dagger_swinging/proc/block_attack(
mob/living/source,
atom/hitby,
damage,
attack_text,
attack_type,
armour_penetration,
damage_type,
attack_flag,
)
SIGNAL_HANDLER
if(attack_type == PROJECTILE_ATTACK || damage >= 75 || damage <= 0 || damage_type == STAMINA)
return NONE
playsound(owner, 'sound/items/weapons/parry.ogg', 75, TRUE)
new /obj/effect/temp_visual/guardian/phase/out(get_turf(owner))
return SUCCESSFUL_BLOCK
/datum/status_effect/dagger_swinging/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_TENTACLE_IMMUNE, REF(src))
///status effect applied to enemies who step on crystals
/datum/status_effect/dagger_stun
id = "dagger stun"
tick_interval = STATUS_EFFECT_NO_TICK
duration = 2 SECONDS
alert_type = null
///overlay we apply to stunned enemies
var/static/mutable_appearance/stun_lightning = mutable_appearance('icons/effects/effects.dmi', "lightning", layer = ABOVE_ALL_MOB_LAYER)
/datum/status_effect/dagger_stun/on_apply()
. = ..()
if(!.)
return
RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_overlays_updated))
ADD_TRAIT(owner, TRAIT_AI_PAUSED, REF(src))
owner.update_appearance()
/datum/status_effect/dagger_stun/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_AI_PAUSED, REF(src))
UnregisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS)
owner.update_appearance()
/datum/status_effect/dagger_stun/proc/on_overlays_updated(atom/parent_atom, list/overlays)
SIGNAL_HANDLER
overlays += stun_lightning

View File

@@ -0,0 +1,63 @@
#define THROW_CRYSTALS_COOLDOWN 15 SECONDS
#define THROW_LAUNCH_COOLDOWN 7 SECONDS
/obj/item/cain_and_abel/on_thrown(mob/living/carbon/user, atom/target)
. = null
if(!COOLDOWN_FINISHED(src, throw_cooldown))
user.balloon_alert(user, "on cooldown!")
return
if(user.incapacitated || HAS_TRAIT(user, TRAIT_NO_THROWING) || !isturf(user.loc) || user.buckled)
user.balloon_alert(user, "unable!")
return
if(get_dist(target, user) > 9)
user.balloon_alert(user, "too far away!")
return
var/static/list/throw_options = list(
THROW_MODE_LAUNCH = list(
"cooldown" = THROW_LAUNCH_COOLDOWN,
"projectile" = /obj/projectile/dagger/launch,
),
THROW_MODE_CRYSTALS = list(
"cooldown" = THROW_CRYSTALS_COOLDOWN,
"projectile" = /obj/projectile/dagger/crystal,
),
)
var/list/throw_settings = throw_options[throw_mode]
COOLDOWN_START(src, throw_cooldown, throw_settings["cooldown"])
var/atom/dagger = user.fire_projectile(throw_settings["projectile"], target, 'sound/items/weapons/fwoosh.ogg', user)
if(isnull(dagger))
return
set_dagger_icon(thrown = TRUE) //when we throw a dagger, we'll only be holding 1
user.Beam(dagger, icon_state = "chain", icon = 'icons/obj/mining_zones/artefacts.dmi', maxdistance = 9, layer = BELOW_MOB_LAYER)
RegisterSignal(dagger, COMSIG_QDELETING, PROC_REF(reset_dagger_icon))
RegisterSignal(dagger, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_dagger_hit))
/obj/item/cain_and_abel/proc/on_dagger_hit(obj/projectile/dagger/source, atom/movable/firer, atom/target, Angle)
SIGNAL_HANDLER
UnregisterSignal(source, list(COMSIG_QDELETING, COMSIG_PROJECTILE_SELF_ON_HIT))
if(!ismob(loc))
return
var/atom/dagger_visual = source.dagger_effects(target)
if(!QDELETED(dagger_visual))
RegisterSignal(dagger_visual, COMSIG_QDELETING, PROC_REF(reset_dagger_icon))
return
set_dagger_icon(thrown = FALSE)
/obj/item/cain_and_abel/proc/set_dagger_icon(thrown = FALSE)
inhand_icon_state = "[src::inhand_icon_state][thrown ? "_thrown" : ""]"
update_inhand_icon()
/obj/item/cain_and_abel/proc/reset_dagger_icon(datum/source)
SIGNAL_HANDLER
set_dagger_icon(thrown = FALSE)
#undef THROW_CRYSTALS_COOLDOWN
#undef THROW_LAUNCH_COOLDOWN

View File

@@ -157,6 +157,7 @@
var/random_crystal = pick(choices)
new random_crystal(src)
new /obj/item/organ/vocal_cords/colossus(src)
new /obj/item/cain_and_abel(src)
/obj/structure/closet/crate/necropolis/colossus/crusher
name = "angelic colossus chest"

View File

@@ -92,7 +92,7 @@
if(living_overlay)
. += living_overlay
/mob/living/basic/leaper/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
/mob/living/basic/leaper/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE, throw_type_path = /datum/thrownthing)
ADD_TRAIT(src, TRAIT_IMMOBILIZED, LEAPING_TRAIT)
return ..()

View File

@@ -150,7 +150,7 @@
. += ore_overlay
/mob/living/basic/mining/mook/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
/mob/living/basic/mining/mook/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE, throw_type_path = /datum/thrownthing)
change_combatant_state(state = MOOK_ATTACK_ACTIVE)
return ..()

View File

@@ -109,7 +109,7 @@
if(CanHug(AM) && Adjacent(AM))
return Leap(AM)
/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle, quickstart = TRUE)
/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, gentle, quickstart = TRUE, throw_type_path = /datum/thrownthing)
. = ..()
if(!.)
return

View File

@@ -1559,7 +1559,7 @@
/mob/living/carbon/alien/update_stamina()
return
/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE, throw_type_path = /datum/thrownthing)
stop_pulling()
. = ..()

View File

@@ -120,5 +120,5 @@
hit_human.Paralyze(4 SECONDS)
hit_human.emote("scream")
/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback, gentle, quickstart = TRUE)
/obj/item/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback, gentle, quickstart = TRUE, throw_type_path = /datum/thrownthing)
return ..(target, range, speed, thrower, FALSE, diagonals_first, callback, quickstart = quickstart)

View File

@@ -210,7 +210,10 @@
taser = null
firer = null
QDEL_NULL(tase_line)
if(!QDELING(tase_line))
QDEL_NULL(tase_line)
else
tase_line = null
/datum/status_effect/tased/tick(seconds_between_ticks)
if(!do_tase_with(taser, seconds_between_ticks))

View File

@@ -33,7 +33,7 @@
hit_living.electrocute_act(80, src, flags = SHOCK_ILLUSION | SHOCK_NOGLOVES)
qdel(src)
/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, gentle, quickstart = TRUE)
/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, gentle, quickstart = TRUE, throw_type_path = /datum/thrownthing)
. = ..()
if(ishuman(thrower))
var/mob/living/carbon/human/human_thrower = thrower

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 KiB

After

Width:  |  Height:  |  Size: 851 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 KiB

After

Width:  |  Height:  |  Size: 917 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -223,4 +223,9 @@ sound/items/handling/materials/snow_drop.ogg and sound/items/handling/materials/
sound/items/hair-clippers.ogg -- https://freesound.org/people/VIPERSTRONG/sounds/655747/ -- by VIPERSTRONG (CC0)
sound/items/weapons/cain_and_abel/dagger_slash_1 to dagger_slash_6 -- https://pixabay.com/sound-effects/whooshslash-sounds-different-pitches-vol2-187377/ by Ponjisk
sound/items/weapons/crystal_dagger_sound.ogg -- https://pixabay.com/sound-effects/breaking-glass-with-feet-45176/ freesound community
'sound/items/weapons/effects/blood_wisp_explode.ogg' -- https://pixabay.com/sound-effects/fire-sound-effects-224089/ by Alice-soundz
sound/effect/plasticflaps.ogg -- door_plastic_tapes.wav by estupe -- https://freesound.org/s/186357/ -- License: Creative Commons 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -4932,6 +4932,11 @@
#include "code\modules\mining\laborcamp\laborstacker.dm"
#include "code\modules\mining\lavaland\ash_flora.dm"
#include "code\modules\mining\lavaland\necropolis_chests.dm"
#include "code\modules\mining\lavaland\cain_and_abel\_cain_and_abel.dm"
#include "code\modules\mining\lavaland\cain_and_abel\dagger_abilities.dm"
#include "code\modules\mining\lavaland\cain_and_abel\dagger_effects.dm"
#include "code\modules\mining\lavaland\cain_and_abel\dagger_status_effects.dm"
#include "code\modules\mining\lavaland\cain_and_abel\dagger_throw.dm"
#include "code\modules\mining\lavaland\mining_loot\berserker.dm"
#include "code\modules\mining\lavaland\mining_loot\clothing.dm"
#include "code\modules\mining\lavaland\mining_loot\consumables.dm"