Cain & Abel (new mining loot) (#89455)
## About The Pull Request adds the Cain & Abel to the lootpool of the colossus!  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
@@ -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 ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
153
code/modules/mining/lavaland/cain_and_abel/_cain_and_abel.dm
Normal 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)
|
||||
@@ -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)
|
||||
210
code/modules/mining/lavaland/cain_and_abel/dagger_effects.dm
Normal 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))
|
||||
@@ -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
|
||||
63
code/modules/mining/lavaland/cain_and_abel/dagger_throw.dm
Normal 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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 773 KiB After Width: | Height: | Size: 851 KiB |
|
Before Width: | Height: | Size: 901 KiB After Width: | Height: | Size: 917 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 48 KiB |
@@ -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
|
||||
|
||||
BIN
sound/items/weapons/cain_and_abel/dagger_slash_1.ogg
Normal file
BIN
sound/items/weapons/cain_and_abel/dagger_slash_2.ogg
Normal file
BIN
sound/items/weapons/cain_and_abel/dagger_slash_3.ogg
Normal file
BIN
sound/items/weapons/cain_and_abel/dagger_slash_4.ogg
Normal file
BIN
sound/items/weapons/cain_and_abel/dagger_slash_5.ogg
Normal file
BIN
sound/items/weapons/cain_and_abel/dagger_slash_6.ogg
Normal file
BIN
sound/items/weapons/crystal_dagger_sound.ogg
Normal file
BIN
sound/items/weapons/effects/blood_wisp_explode.ogg
Normal 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"
|
||||
|
||||