mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-21 20:13:45 +01:00
Crashed Vulpkanin Ship - Lavaland Ruin (#32110)
* Charon's Revolver * Dog tags * Ash Whelps * Ash walkers and resist adjustment * Some providence stuff * Minor wording adjustment * Desc change * Ruin, salvage, and ruin assets * linters * Mo linters * quick * Various fixes * Variety of fixes * Update * Naming * FIX
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#define PW_CHALLENGE_LOOTROOM "PW_CHALLENGE_LOOTROOM"
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence
|
||||
speaking_name = "Captain Charon"
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence/start_message(mob/living/carbon/human/H)
|
||||
activated = TRUE
|
||||
QDEL_NULL(proximity_monitor)
|
||||
icon_state = "holopad1"
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
var/obj/effect/overlay/hologram = new(get_turf(src))
|
||||
our_holo = hologram
|
||||
hologram.icon = getHologramIcon(icon('icons/mob/simple_human.dmi', "captain_charon"), colour = null, opacity = 0.8, colour_blocking = TRUE) // This is more offputting. Also in colour more and less transparent.
|
||||
hologram.alpha = 166
|
||||
hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
hologram.layer = FLY_LAYER
|
||||
hologram.anchored = TRUE
|
||||
hologram.name = speaking_name
|
||||
hologram.set_light(2)
|
||||
hologram.bubble_icon = "swarmer"
|
||||
hologram.pixel_y = 16
|
||||
for(var/I in things_to_say)
|
||||
hologram.atom_say("[I]")
|
||||
sleep(loop_sleep_time)
|
||||
qdel(hologram)
|
||||
icon_state = initial(icon_state)
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence/bridge
|
||||
things_to_say = list(
|
||||
"Log. This is Captain Charon... Of the independant vessel 'Providence'...",
|
||||
"Hrk... That thing... it got me good. Hah. I got it better.",
|
||||
"Eckart didn't make it. They got him. I haven't heard the screams for hours now...",
|
||||
"This place - it's hell. It's truly hell.",
|
||||
"I'm locking down the ship. That's all I can do now.",
|
||||
"If anyone heard any of my recordings, you can use the device here to disable the lockdown... but it'll probably attract the beasts to you while it works.",
|
||||
"If nobody did... Hah, then why am I still talking?",
|
||||
"Captain Charon, signing off... Please, before this place takes me too."
|
||||
)
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence/last_stand
|
||||
things_to_say = list(
|
||||
"Log. This is Captain Charon of the independant vessel 'Providence...",
|
||||
"The noises are growing closer. We've already seen assailants from some tribal species and other creatures.",
|
||||
"They're probing our defenses - looking for a way in. We've locked down the damaged sections from what's still intact. That'll buy us time.",
|
||||
"Eckart is proving to be a good skirmisher, but he's running low on shells.",
|
||||
"While he buys us time, we've begun to fortify this room. We'll hold out. I promised my crew that much.",
|
||||
"Captain Charon, signing off. If you see miss Protheros, tell her I love her very much."
|
||||
)
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence/crew_quarters
|
||||
things_to_say = list(
|
||||
"Log. This is Captain Charon of the independant vessel 'Providence.",
|
||||
"It's not looking good. Most of the crew were badly injured in the crash.",
|
||||
"We've got what doctors are still mobile running triage, but that's not the worst of it.",
|
||||
"Lieutenant Eckart took an old spacesuit out to look around, and he came back. Said he heard noises.",
|
||||
"Nothing good can come of that, no sir. What crew remain alive have taken up arms. We're not going to die down here.",
|
||||
"Captain Charon, signing off."
|
||||
)
|
||||
|
||||
/obj/structure/environmental_storytelling_holopad/providence/communications
|
||||
things_to_say = list(
|
||||
"Log. This is Captain Charon of the independant vessel 'Providence.",
|
||||
"We've sent out a distress signal. Best we can do now is wait.",
|
||||
"The back half of the ship is gone. That hole in the ground... it doesn't end.",
|
||||
"Sergeant Kenefick shined a targeting laser into the hole, and the rangefinder wouldn't stop ticking.",
|
||||
"We've lost all contact with engineering, and a chunk of our medical deck is gone.",
|
||||
"I believe we can pull through though. Things seem rough, but we've toughed through worse.",
|
||||
"Captain Charon, signing off."
|
||||
)
|
||||
|
||||
/obj/structure/providence_lockdown_controller
|
||||
name = "lockdown controller"
|
||||
desc = "A machine that controls the heavy blast doors all around the Providence. Reversing this should unlock the entire ship for exploration."
|
||||
icon = 'icons/obj/machines/mining_machines.dmi'
|
||||
icon_state = "asteroid_magnet"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
armor = list(MELEE = 50, BULLET = 50, LASER = 50, ENERGY = 50, BOMB = 10, RAD = 100, FIRE = 100, ACID = 100)
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
/// How long does it take to remove the lockdown
|
||||
var/lockdown_time = 8 MINUTES
|
||||
/// Number of waves, minus one for the starting wave
|
||||
var/wave_count = 5
|
||||
/// Minimum mobs per wave
|
||||
var/wave_mob_min = 3
|
||||
/// Max mobs per wave
|
||||
var/wave_mob_max = 6
|
||||
/// Mobs that can spawn in the wave
|
||||
var/list/wave_mobs = list(
|
||||
/mob/living/basic/mining/hivelord/legion = 375,
|
||||
/mob/living/basic/ash_walker = 332,
|
||||
/mob/living/basic/mining/goliath = 297,
|
||||
/mob/living/basic/mining/basilisk/watcher = 296,
|
||||
/mob/living/basic/ash_walker/tough = 120,
|
||||
/mob/living/basic/mining/ash_whelp = 98,
|
||||
/mob/living/basic/ash_walker/veteran = 45,
|
||||
/mob/living/basic/mining/hivelord/legion/dwarf = 25,
|
||||
/mob/living/basic/mining/goliath/ancient = 3,
|
||||
/mob/living/basic/ash_walker/elite = 3,
|
||||
/mob/living/basic/mining/basilisk/watcher/magmawing = 3,
|
||||
/mob/living/basic/mining/ash_whelp/ice = 2,
|
||||
/mob/living/basic/mining/basilisk/watcher/icewing = 1,
|
||||
)
|
||||
/// What faction to give the mobs that spawn
|
||||
var/mob_faction = "providence_attackers"
|
||||
/// Are we active
|
||||
var/started = FALSE
|
||||
|
||||
/obj/structure/providence_lockdown_controller/attack_hand(mob/living/user)
|
||||
. = ..()
|
||||
if(!isliving(user))
|
||||
return
|
||||
if(isanimal_or_basicmob(user))
|
||||
return
|
||||
if(!Adjacent(user))
|
||||
return
|
||||
if(started)
|
||||
return
|
||||
start_challenge()
|
||||
|
||||
/obj/structure/providence_lockdown_controller/proc/start_challenge()
|
||||
icon_state = "asteroid_magnet-on"
|
||||
started = TRUE
|
||||
atom_say("Beginning lockdown lift procedure...")
|
||||
var/wave_delay = lockdown_time / wave_count
|
||||
for(var/i in 1 to wave_count)
|
||||
addtimer(CALLBACK(src, PROC_REF(spawn_wave)), wave_delay * i)
|
||||
addtimer(CALLBACK(src, PROC_REF(finish_challenge)), lockdown_time)
|
||||
sleep(5 SECONDS)
|
||||
spawn_wave()
|
||||
|
||||
/obj/structure/providence_lockdown_controller/proc/finish_challenge()
|
||||
for(var/obj/machinery/door/poddoor/P in GLOB.airlocks)
|
||||
if(P.density && P.id_tag == PW_CHALLENGE_LOOTROOM && P.z == z && !P.operating)
|
||||
P.open()
|
||||
atom_say("Lockdown lifted. Have a secure daayayyyayyy... -ZZZT!")
|
||||
visible_message("<span class='userdanger'>[src] starts beeping ominously!</span>")
|
||||
for(var/i in 1 to 4)
|
||||
playsound(loc, 'sound/items/timer.ogg', 30, 0)
|
||||
sleep(1 SECONDS)
|
||||
explosion(loc, 2, 5, 10, 16, flame_range = 20, cause = name)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/providence_lockdown_controller/proc/spawn_wave()
|
||||
var/list/valid_turfs = orange(8, get_turf(src)) - orange(3, get_turf(src))
|
||||
var/num_to_spawn = rand(wave_mob_min, wave_mob_max)
|
||||
for(var/i in 1 to num_to_spawn)
|
||||
var/turf/valid_spawn
|
||||
while(!valid_spawn)
|
||||
var/turf/T = pick_n_take(valid_turfs)
|
||||
if(!T.is_blocked_turf() && !T.density && !T.contains(/mob) && !istype(T, /turf/simulated/floor/pod))
|
||||
valid_spawn = T
|
||||
var/mob_type = pickweight(wave_mobs)
|
||||
var/mob/living/new_mob = new mob_type(valid_spawn)
|
||||
new /obj/effect/temp_visual/cosmic_explosion(valid_spawn)
|
||||
if(!new_mob.ai_controller)
|
||||
return
|
||||
new_mob.ai_controller.set_blackboard_key(BB_TRAVEL_DESTINATION, get_turf(src))
|
||||
new_mob.ai_controller.queue_behavior(/datum/ai_behavior/travel_towards, BB_TRAVEL_DESTINATION)
|
||||
new_mob.faction |= mob_faction
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/vulpkanin/captain/providence
|
||||
mob_name = "Charon Protheros"
|
||||
mob_gender = MALE
|
||||
|
||||
#undef PW_CHALLENGE_LOOTROOM
|
||||
@@ -710,6 +710,38 @@
|
||||
mob_species = /datum/species/skeleton/brittle
|
||||
mob_gender = NEUTER
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/vulpkanin
|
||||
name = "vulpkanin"
|
||||
mob_species = /datum/species/vulpkanin
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/vulpkanin/merc
|
||||
outfit = /datum/outfit/vulpkanin_merc
|
||||
|
||||
/datum/outfit/vulpkanin_merc
|
||||
name = "vulpkanin mercenary"
|
||||
uniform = /obj/item/clothing/under/syndicate/tacticool
|
||||
suit = /obj/item/clothing/suit/armor/vest
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
gloves = /obj/item/clothing/gloves/color/black
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/vulpkanin/officer
|
||||
outfit = /datum/outfit/vulpkanin_merc/officer
|
||||
|
||||
/datum/outfit/vulpkanin_merc/officer
|
||||
name = "vulpkanin officer"
|
||||
uniform = /obj/item/clothing/under/rank/procedure/blueshield
|
||||
suit = /obj/item/clothing/suit/armor/secjacket
|
||||
head = /obj/item/clothing/head/beret/blue
|
||||
|
||||
/obj/effect/mob_spawn/human/corpse/vulpkanin/captain
|
||||
outfit = /datum/outfit/vulpkanin_merc/captain
|
||||
|
||||
/datum/outfit/vulpkanin_merc/captain
|
||||
name = "vulpkanin captain"
|
||||
uniform = /obj/item/clothing/under/rank/captain
|
||||
suit = /obj/item/clothing/suit/armor/secjacket
|
||||
head = /obj/item/clothing/head/caphat
|
||||
|
||||
/datum/outfit/randomizer
|
||||
name = "randomizer"
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/mob/living/basic/ash_walker
|
||||
name = "ash walker"
|
||||
desc = "These reptillian creatures appear to be related to the Unathi, but seem significantly less evolved."
|
||||
icon = 'icons/mob/simple_human.dmi'
|
||||
icon_state = "ashwalker"
|
||||
icon_living = "ashwalker"
|
||||
icon_dead = "ashwalker_dead" // Does not actually exist. del_on_death.
|
||||
icon_gib = "ashwalker_gib" // Does not actually exist. del_on_death.
|
||||
mob_biotypes = MOB_ORGANIC | MOB_HUMANOID
|
||||
speed = -0.8
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
melee_attack_cooldown_min = 1 SECONDS
|
||||
melee_attack_cooldown_max = 1.5 SECONDS
|
||||
attack_verb_continuous = "stabs"
|
||||
attack_verb_simple = "stab"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
a_intent = INTENT_HARM
|
||||
ai_controller = /datum/ai_controller/basic_controller/simple/ash_walker
|
||||
faction = list("ashwalker")
|
||||
weather_immunities = list("lava","ash")
|
||||
unsuitable_atmos_damage = 0
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = INFINITY
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/iron{layer = ABOVE_MOB_LAYER})
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
sentience_type = SENTIENCE_OTHER
|
||||
step_type = FOOTSTEP_MOB_SHOE
|
||||
|
||||
/mob/living/basic/ash_walker/Initialize(mapload)
|
||||
. = ..()
|
||||
add_language("Sinta'unathi")
|
||||
set_default_language(GLOB.all_languages["Sinta'unathi"])
|
||||
gender = pick(MALE, FEMALE)
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
|
||||
/mob/living/basic/ash_walker/tough
|
||||
name = "ash walker hunter"
|
||||
icon_state = "ashwalker_hunter"
|
||||
icon_living = "ashwalker_hunter"
|
||||
damage_coeff = list(BRUTE = 0.9, BURN = 0.6, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/plasma{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/mob/living/basic/ash_walker/veteran
|
||||
name = "ash walker slayer"
|
||||
icon_state = "ashwalker_veteran"
|
||||
icon_living = "ashwalker_veteran"
|
||||
damage_coeff = list(BRUTE = 0.8, BURN = 0.4, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
armor_penetration_flat = 15
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/silver{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/mob/living/basic/ash_walker/elite
|
||||
name = "ash walker dragonslayer"
|
||||
icon_state = "ashwalker_elite"
|
||||
icon_living = "ashwalker_elite"
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
armor_penetration_flat = 25
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/gold{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/datum/ai_controller/basic_controller/simple/ash_walker
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
|
||||
)
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/generic_resist,
|
||||
/datum/ai_planning_subtree/random_speech/ash_walker,
|
||||
/datum/ai_planning_subtree/target_retaliate,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl/lavaland,
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/random_speech/ash_walker
|
||||
speech_chance = 10
|
||||
emote_see = list("grunts.", "hisses.", "inspects their claws.")
|
||||
speak = list(
|
||||
"As the necropolis wills.",
|
||||
"It's been too long since we've had a good hunt.",
|
||||
"Don't sleep in the shade too long. The cold dreams will find you.",
|
||||
"I heard the dust singing across the rocks today.",
|
||||
"The young ones think the lava fields are a brave place. They will find good hunts there.",
|
||||
"Did you see that star fall?",
|
||||
"Sometimes, I feel like they're watching me.",
|
||||
"I heard someone got a goliath the other day. Delicious.",
|
||||
"Think we'll see an offworlder today?",
|
||||
"Words are cheap.")
|
||||
@@ -0,0 +1,123 @@
|
||||
/mob/living/basic/mining/ash_whelp
|
||||
name = "ash whelp"
|
||||
desc = "The offspring of an ash drake, weak in comparison but still terrifying."
|
||||
icon_state = "ash_whelp"
|
||||
icon_living = "ash_whelp"
|
||||
icon_dead = "ash_whelp_dead"
|
||||
butcher_results = list(
|
||||
/obj/item/stack/ore/gold = 3,
|
||||
/obj/item/stack/sheet/animalhide/ashdrake = 1,
|
||||
/obj/item/stack/sheet/bone = 10,
|
||||
/obj/item/stack/sheet/sinew = 2,
|
||||
)
|
||||
crusher_loot = list(/obj/item/crusher_trophy/tail_spike)
|
||||
speed = 12
|
||||
|
||||
maxHealth = 300
|
||||
health = 300
|
||||
obj_damage = 40
|
||||
armor_penetration_flat = 20
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
attack_verb_continuous = "chomps"
|
||||
attack_verb_simple = "chomp"
|
||||
throw_blocked_message = "does nothing to the scaled hide of the"
|
||||
death_message = "collapses on its side."
|
||||
death_sound = 'sound/misc/demon_dies.ogg'
|
||||
attack_sound = 'sound/misc/demon_attack1.ogg'
|
||||
|
||||
faction = list("mining")
|
||||
|
||||
move_force = MOVE_FORCE_STRONG
|
||||
move_resist = MOVE_FORCE_STRONG
|
||||
pull_force = MOVE_FORCE_STRONG
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/ash_whelp
|
||||
var/list/innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath = BB_WHELP_STRAIGHTLINE_FIRE,
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/fire = BB_WHELP_WIDESPREAD_FIRE,
|
||||
)
|
||||
|
||||
/// How much we will heal when consuming a target
|
||||
var/heal_on_cannibalize = 5
|
||||
|
||||
/mob/living/basic/mining/ash_whelp/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
AddComponent(/datum/component/ai_target_timer)
|
||||
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HEAVY)
|
||||
grant_actions_by_list(innate_actions)
|
||||
|
||||
/mob/living/basic/mining/ash_whelp/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
|
||||
. = ..()
|
||||
|
||||
if(istype(target, /obj/structure/flora/ash/rock))
|
||||
create_sculpture(target)
|
||||
return FALSE
|
||||
|
||||
if(!istype(target, type))
|
||||
return TRUE
|
||||
|
||||
var/mob/living/victim = target
|
||||
if(victim.stat != DEAD)
|
||||
return TRUE
|
||||
|
||||
if(istype(target, /mob/living/basic/mining/ash_whelp))
|
||||
cannibalize_victim(victim)
|
||||
return FALSE
|
||||
return FALSE
|
||||
|
||||
/// Carve a stone into a beautiful self-portrait
|
||||
/mob/living/basic/mining/ash_whelp/proc/create_sculpture(atom/target)
|
||||
visible_message("[src] starts to sculpt [target].")
|
||||
if(!do_after(src, 5 SECONDS, target = target))
|
||||
return
|
||||
var/obj/structure/whelp_statue/dragon_statue = new(get_turf(target))
|
||||
dragon_statue.name = "statue of [src]"
|
||||
dragon_statue.desc = "Let this serve as a warning."
|
||||
qdel(target)
|
||||
|
||||
/// Gib and consume our fellow ash drakes
|
||||
/mob/living/basic/mining/ash_whelp/proc/cannibalize_victim(mob/living/target)
|
||||
start_pulling(target)
|
||||
visible_message("[src] starts to consume [target].")
|
||||
if(!do_after(src, 5 SECONDS, target))
|
||||
return
|
||||
target.gib()
|
||||
adjustBruteLoss(-1 * heal_on_cannibalize)
|
||||
|
||||
/// Ice whelp, the cold variant of ash whelps.
|
||||
/mob/living/basic/mining/ash_whelp/ice
|
||||
name = "ice whelp"
|
||||
desc = "The offspring of an ash drake, weak in comparison but still terrifying. This one exudes an icy aura."
|
||||
icon_state = "ice_whelp"
|
||||
icon_living = "ice_whelp"
|
||||
icon_dead = "ice_whelp_dead"
|
||||
innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice = BB_WHELP_STRAIGHTLINE_FIRE,
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption = BB_WHELP_WIDESPREAD_FIRE,
|
||||
)
|
||||
|
||||
/// Ash whelp statue formed by them carving a rock
|
||||
/obj/structure/whelp_statue
|
||||
name = "statue of an ash whelp"
|
||||
desc = "A basalt sculpture of a proud and regal drake. Its eyes are six glowing gemstones."
|
||||
icon = 'icons/obj/structures/statues.dmi'
|
||||
icon_state = "drake"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
integrity_failure = 100
|
||||
layer = EDGED_TURF_LAYER
|
||||
|
||||
/obj/structure/whelp_statue/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
|
||||
/obj/structure/whelp_statue/update_icon_state()
|
||||
if(broken)
|
||||
icon_state = "drake_headless"
|
||||
return
|
||||
icon_state = "drake"
|
||||
@@ -0,0 +1,209 @@
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath
|
||||
name = "Fire Breath"
|
||||
button_icon_state = "fireball"
|
||||
desc = "Breathe a line of flames towards the target."
|
||||
cooldown_time = 3 SECONDS
|
||||
/// The range of the fire
|
||||
var/fire_range = 15
|
||||
/// The sound played when you use this ability
|
||||
var/fire_sound = 'sound/magic/fireball.ogg'
|
||||
/// Time to wait between spawning each fire turf
|
||||
var/fire_delay = 0.15 SECONDS
|
||||
/// How hot is our fire
|
||||
var/fire_temperature = 1000
|
||||
/// 'How much' fire do we expose the turf to?
|
||||
var/fire_volume = 50
|
||||
/// How much damage do you take when engulfed?
|
||||
var/fire_damage = 20
|
||||
/// How much damage to mechs take when engulfed?
|
||||
var/mech_damage = 45
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/Activate(atom/target_atom)
|
||||
disable_cooldown_actions()
|
||||
attack_sequence(target_atom)
|
||||
StartCooldown()
|
||||
enable_cooldown_actions()
|
||||
return TRUE
|
||||
|
||||
/// Apply our specific fire breathing shape, in proc form so we can override it in subtypes
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/attack_sequence(atom/target)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
fire_line(target)
|
||||
|
||||
/// Breathe fire in a line towards the target, optionally rotated at an offset from the target
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/fire_line(atom/target, offset)
|
||||
if(isnull(target))
|
||||
return
|
||||
var/turf/target_turf = get_ranged_target_turf_direct(owner, target, fire_range, offset)
|
||||
var/list/turfs = get_line(owner, target_turf) - get_turf(owner)
|
||||
INVOKE_ASYNC(src, PROC_REF(progressive_fire_line), turfs)
|
||||
|
||||
/// Creates fire with a delay on the list of targeted turfs
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/progressive_fire_line(list/burn_turfs)
|
||||
if(QDELETED(owner) || owner.stat == DEAD)
|
||||
return
|
||||
// Guys we have already hit, no double dipping
|
||||
var/list/hit_list = list(owner) // also don't burn ourselves
|
||||
for(var/turf/target_turf in burn_turfs)
|
||||
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
|
||||
return
|
||||
burn_turf(target_turf, hit_list, owner)
|
||||
sleep(fire_delay)
|
||||
|
||||
/// Finally spawn the actual fire, spawns the fire hotspot in case you want to recolour it or something
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/burn_turf(turf/fire_turf, list/hit_list, mob/living/source)
|
||||
var/obj/effect/hotspot/hotspot = new /obj/effect/hotspot/fake(fire_turf)
|
||||
hotspot.temperature = fire_temperature
|
||||
hotspot.recolor()
|
||||
fire_turf.hotspot_expose(fire_temperature, fire_volume)
|
||||
|
||||
for(var/mob/living/barbecued in fire_turf.contents)
|
||||
if(barbecued in hit_list)
|
||||
continue
|
||||
hit_list |= barbecued
|
||||
on_burn_mob(barbecued, source)
|
||||
|
||||
for(var/obj/mecha/robotron in fire_turf.contents)
|
||||
if(robotron in hit_list)
|
||||
continue
|
||||
hit_list |= robotron
|
||||
robotron.take_damage(mech_damage, BURN, FIRE)
|
||||
|
||||
return
|
||||
|
||||
/// Do something unpleasant to someone we set on fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/on_burn_mob(mob/living/barbecued, mob/living/source)
|
||||
if(fire_temperature <= TCMB)
|
||||
barbecued.apply_status_effect(/datum/status_effect/ice_block_talisman, 3 SECONDS)
|
||||
to_chat(barbecued, SPAN_USERDANGER("You're frozen solid by [source]'s icy breath!"))
|
||||
else
|
||||
to_chat(barbecued, SPAN_USERDANGER("You are burned by [source]'s fire breath!"))
|
||||
barbecued.adjustFireLoss(fire_damage)
|
||||
|
||||
/// Shoot three lines of fire in a sort of fork pattern approximating a cone
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/cone
|
||||
name = "Fire Cone"
|
||||
desc = "Breathe several lines of fire directed at a target."
|
||||
/// The angles relative to the target that shoot lines of fire
|
||||
var/list/angles = list(-40, 0, 40)
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/cone/attack_sequence(atom/target)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
for(var/offset in angles)
|
||||
fire_line(target, offset)
|
||||
|
||||
/// Shoot fire in a whole bunch of directions
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire
|
||||
name = "Mass Fire"
|
||||
button_icon = 'icons/effects/fire.dmi'
|
||||
button_icon_state = "light"
|
||||
desc = "Breathe flames in all directions."
|
||||
cooldown_time = 10.5 SECONDS
|
||||
click_to_activate = FALSE
|
||||
/// How many fire lines do we produce to turn a full circle?
|
||||
var/sectors = 12
|
||||
/// How long do we wait between each spin?
|
||||
var/breath_delay = 2.5 SECONDS
|
||||
/// How many full circles do we perform?
|
||||
var/total_spins = 3
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/Activate(atom/target_atom)
|
||||
target_atom = get_step(owner, owner.dir) // Just shoot it forwards, we don't need to click on someone forthis one
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/attack_sequence(atom/target)
|
||||
var/queued_spins = 0
|
||||
for(var/i in 1 to total_spins)
|
||||
var/delay = queued_spins * breath_delay
|
||||
queued_spins++
|
||||
addtimer(CALLBACK(src, PROC_REF(fire_spin), target, queued_spins), delay)
|
||||
|
||||
/// Breathe fire in a circle, with a slight angle offset based on which of our several circles it is
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/proc/fire_spin(target, spin_count)
|
||||
if(QDELETED(owner) || owner.stat == DEAD)
|
||||
return // Too dead to spin
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
var/angle_increment = 360 / sectors
|
||||
var/additional_offset = spin_count * angle_increment / 2
|
||||
for(var/i in 1 to sectors)
|
||||
fire_line(target, (angle_increment * i) + (additional_offset))
|
||||
|
||||
/// Breathe "fire" in a line (it's freezing cold)
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice
|
||||
name = "Ice Breath"
|
||||
desc = "Fire a cold line of fire towards the enemy!"
|
||||
cooldown_time = 6 SECONDS
|
||||
fire_range = 7
|
||||
fire_damage = 10
|
||||
fire_delay = 0.85 DECISECONDS
|
||||
fire_temperature = TCMB
|
||||
/// Time to warn people about what we are doing
|
||||
var/forecast_delay = 0.6 SECONDS
|
||||
/// What turf are we aiming at?
|
||||
var/turf/target_turf
|
||||
/// Overlay we show when we're about to fire
|
||||
var/image/forecast_overlay
|
||||
/// Icon state used foroverlay
|
||||
var/forecast_overlay_state = "ice_whelp_telegraph_dir"
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/New(Target, original)
|
||||
. = ..()
|
||||
forecast_overlay = image('icons/mob/lavaland/lavaland_monsters.dmi', forecast_overlay_state)
|
||||
|
||||
/// Apply our specific fire breathing shape, in proc form so we can override it in subtypes
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/attack_sequence(atom/target)
|
||||
target_turf = get_turf(target)
|
||||
INVOKE_ASYNC(src, PROC_REF(attack_forecast))
|
||||
|
||||
/// Charge up before we breathe fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/proc/attack_forecast()
|
||||
owner.face_atom(target_turf)
|
||||
owner.Shake(pixelshiftx = 1, pixelshifty = 0, duration = forecast_delay)
|
||||
forecast_overlay.dir = (get_dir(owner, target_turf))
|
||||
owner.add_overlay(forecast_overlay)
|
||||
var/succeeded = do_after(owner, delay = forecast_delay, target = owner, hidden = TRUE)
|
||||
owner.cut_overlay(forecast_overlay)
|
||||
if(succeeded)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
breath_attack()
|
||||
|
||||
/// Actually breathe fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/proc/breath_attack()
|
||||
owner.face_atom(target_turf)
|
||||
fire_line(target_turf)
|
||||
target_turf = null
|
||||
|
||||
/// Breathe really cold fire an area around them
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption
|
||||
name = "Ice Eruption"
|
||||
desc = "Unleash cold fire in all directions"
|
||||
button_icon = 'icons/effects/fire.dmi'
|
||||
button_icon_state = "light"
|
||||
click_to_activate = FALSE
|
||||
fire_range = 3
|
||||
forecast_delay = 1 SECONDS
|
||||
fire_delay = 1.5 DECISECONDS
|
||||
forecast_overlay_state = "ice_whelp_telegraph_all"
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/breath_attack()
|
||||
target_turf = null
|
||||
|
||||
var/list/hit_list = list(owner)
|
||||
var/list/nearby_turfs = list()
|
||||
for(var/turf/simulated/floor/target_turf in circle_range(owner, fire_range))
|
||||
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
|
||||
continue
|
||||
var/turf_dist = get_dist(owner, target_turf)
|
||||
if(turf_dist == 0)
|
||||
continue
|
||||
LAZYADDASSOCLIST(nearby_turfs, "[turf_dist]", target_turf)
|
||||
|
||||
for(var/i in 1 to fire_range)
|
||||
for(var/turf/simulated/floor/kindling as anything in nearby_turfs["[i]"])
|
||||
burn_turf(kindling, hit_list, owner)
|
||||
sleep(fire_delay)
|
||||
|
||||
///Fire subtype forash whelps
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/fire
|
||||
name = "Eruption"
|
||||
fire_temperature = 1000
|
||||
@@ -0,0 +1,163 @@
|
||||
/// We won't use fire breath unless we have had the same target forthis long
|
||||
#define MIN_TIME_TO_BREATH_WEAPON 2 SECONDS
|
||||
|
||||
/datum/ai_controller/basic_controller/ash_whelp
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
|
||||
BB_AGGRO_RANGE = 5,
|
||||
)
|
||||
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/generic_resist,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/ash_whelp,
|
||||
/datum/ai_planning_subtree/sculpt_statues,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/ash_whelp,
|
||||
/datum/ai_planning_subtree/burn_trees,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl/lavaland,
|
||||
)
|
||||
|
||||
/// Cancel melee attacks when we have our breath weapon
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/ash_whelp
|
||||
melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/ash_whelp
|
||||
|
||||
/// Cancel melee attacks when we have our breath weapon
|
||||
/datum/ai_behavior/basic_melee_attack/ash_whelp
|
||||
|
||||
/datum/ai_behavior/basic_melee_attack/ash_whelp/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
|
||||
var/datum/action/cooldown/breath_weapon = controller.blackboard[BB_TARGETED_ACTION]
|
||||
if(breath_weapon?.IsAvailable())
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
return ..()
|
||||
|
||||
/// Find other tasty dragons
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/ash_whelp
|
||||
target_key = BB_TARGET_CANNIBAL
|
||||
finding_behavior = /datum/ai_behavior/find_hunt_target/corpses/dragon_corpse
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
|
||||
hunt_targets = list(/mob/living/basic/mining/ash_whelp)
|
||||
hunt_range = 10
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse/valid_dinner(mob/living/source, mob/living/dinner, radius)
|
||||
if(dinner.pulledby) // someone already got him before us
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/// Eat other dragons
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
|
||||
var/mob/living/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target) || target.stat != DEAD || target.pulledby) // we were too slow
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/cannibalize/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/// Subtree to find icy rocks and create sculptures out of them
|
||||
/datum/ai_planning_subtree/sculpt_statues
|
||||
|
||||
/datum/ai_planning_subtree/sculpt_statues/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(BB_TARGET_ROCK))
|
||||
controller.queue_behavior(/datum/ai_behavior/sculpt_statue, BB_TARGET_ROCK)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_TARGET_ROCK, /obj/structure/flora/ash/rock)
|
||||
|
||||
/datum/ai_behavior/sculpt_statue
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
action_cooldown = 5 MINUTES
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/obj/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
if(!controller.ai_interact(target = target_key, intent = INTENT_HELP))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
|
||||
/// Select appropriate ability based on range
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp
|
||||
operational_datums = list(/datum/component/ai_target_timer)
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/mob/living/target = controller.blackboard[target_key]
|
||||
if(isnull(target))
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0
|
||||
if(time_on_target < MIN_TIME_TO_BREATH_WEAPON)
|
||||
return // We need to spend some time acquiring our target first
|
||||
|
||||
var/dist_to_target = get_dist(controller.pawn, target)
|
||||
var/datum/action/cooldown/mob_cooldown/fire_breath/short_range_ability = controller.blackboard[BB_WHELP_WIDESPREAD_FIRE]
|
||||
if(isnull(short_range_ability) || dist_to_target > short_range_ability.fire_range)
|
||||
ability_key = BB_WHELP_STRAIGHTLINE_FIRE
|
||||
return ..()
|
||||
|
||||
ability_key = BB_WHELP_WIDESPREAD_FIRE
|
||||
return ..()
|
||||
|
||||
/// subtree to look fortrees and burn them with our flamethrower
|
||||
/datum/ai_planning_subtree/burn_trees
|
||||
|
||||
/datum/ai_planning_subtree/burn_trees/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/datum/action/cooldown/using_action = controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE]
|
||||
if(!using_action?.IsAvailable())
|
||||
return
|
||||
|
||||
if(controller.blackboard_key_exists(BB_TARGET_TREE))
|
||||
controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees, BB_WHELP_STRAIGHTLINE_FIRE, BB_TARGET_TREE)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/set_target_tree, BB_TARGET_TREE)
|
||||
|
||||
/datum/ai_behavior/set_target_tree
|
||||
|
||||
/datum/ai_behavior/set_target_tree/perform(seconds_per_tick, datum/ai_controller/controller, tree_key)
|
||||
. = ..()
|
||||
var/mob/living_pawn = controller.pawn
|
||||
var/list/possible_trees = list()
|
||||
|
||||
for(var/obj/structure/flora/tree/possible_tree in oview(9, living_pawn))
|
||||
possible_trees += possible_tree
|
||||
|
||||
if(!length(possible_trees))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.set_blackboard_key(tree_key, pick(possible_trees))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
required_distance = 2
|
||||
action_cooldown = 2 MINUTES
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees/setup(datum/ai_controller/controller, ability_key, target_key)
|
||||
. = ..()
|
||||
var/obj/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
#undef MIN_TIME_TO_BREATH_WEAPON
|
||||
@@ -14,6 +14,14 @@
|
||||
multi_sprite_step = 1
|
||||
icon_state = "357_box"
|
||||
|
||||
/obj/item/ammo_box/huntsman32
|
||||
name = "speed loader (.32 Huntsman)"
|
||||
desc = "A small device designed to quickly reload revolvers. Seven round capacity."
|
||||
materials = list()
|
||||
ammo_type = /obj/item/ammo_casing/huntsman32
|
||||
multi_sprite_step = 1 // see: /obj/item/ammo_box/update_icon()
|
||||
icon_state = "357"
|
||||
|
||||
/obj/item/ammo_box/c9mm
|
||||
name = "ammo box (9mm)"
|
||||
desc = "An ammunition box filled with 9mm pistol cartridges, commonly used in handguns and submachine guns."
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
|
||||
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
|
||||
|
||||
/obj/item/ammo_casing/huntsman32
|
||||
name = ".32 huntsman round"
|
||||
desc = "A .32 caliber round. The bullet is scored and designed to fragment on hit, dealing large amounts of damage to large creatures."
|
||||
icon_state = "magnum_steel"
|
||||
caliber = "32"
|
||||
projectile_type = /obj/projectile/bullet
|
||||
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
|
||||
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
|
||||
|
||||
/obj/item/ammo_casing/rubber9mm
|
||||
name = "9mm rubber round"
|
||||
desc = "A 9mm pistol cartridge. This one has a rubber tip for less-lethal takedowns."
|
||||
@@ -19,7 +28,7 @@
|
||||
desc = "A 7.62mm rifle cartridge, often used in Soviet rifles."
|
||||
icon_state = "rifle_brass"
|
||||
caliber = "a762"
|
||||
projectile_type = /obj/projectile/bullet
|
||||
projectile_type = /obj/projectile/bullet/huntsman32
|
||||
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG
|
||||
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@
|
||||
|
||||
return 0
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/cylinder/charons_special
|
||||
ammo_type = /obj/item/ammo_casing/huntsman32
|
||||
caliber = "32"
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible
|
||||
name = "finger gun cylinder"
|
||||
desc = "Wait, what?"
|
||||
|
||||
@@ -132,6 +132,14 @@
|
||||
options["Fancy"] = "revolver-fancy"
|
||||
options["Rose Gold Finish"] = "revolver-rose"
|
||||
|
||||
/// Special revolver found in the vulpkanin ship lavaland ruin
|
||||
/obj/item/gun/projectile/revolver/charons_special
|
||||
name = "Charon's Modified Revolver"
|
||||
desc = "A bog-standard service revolver, modified to fire special .32 caliber \"Huntsman\" rounds. The barrel is engraved with a captain's insignia and a pair of wings."
|
||||
icon_state = "charons-special"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/charons_special
|
||||
origin_tech = "combat=6;materials=5"
|
||||
|
||||
/// Summoned by the Finger Gun spell, from advanced mimery traitor item
|
||||
/obj/item/gun/projectile/revolver/fingergun
|
||||
name = "finger gun"
|
||||
|
||||
@@ -52,6 +52,15 @@
|
||||
stamina = 30
|
||||
icon_state = "bullet-r"
|
||||
|
||||
/obj/projectile/bullet/huntsman32
|
||||
damage = 20
|
||||
|
||||
/obj/projectile/bullet/huntsman32/on_hit(atom/target, blocked = 0)
|
||||
if(..(target, blocked) && isliving(target))
|
||||
var/mob/living/L = target
|
||||
if(L.mob_size >= MOB_SIZE_LARGE)
|
||||
L.apply_damage(80, BRUTE)
|
||||
|
||||
/obj/projectile/bullet/toxinbullet
|
||||
damage = 15
|
||||
damage_type = TOX
|
||||
|
||||
Reference in New Issue
Block a user