mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-01-01 21:13:07 +00:00
Merge pull request #8829 from Spookerton/spkrtn/cng/gnarl
updates drakes & drake harnesses
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
/obj/item/storage/internal/animal_harness
|
||||
abstract_type = /obj/item/storage/internal/animal_harness
|
||||
name = "animal harness"
|
||||
color = COLOR_BEASTY_BROWN
|
||||
max_w_class = ITEMSIZE_LARGE
|
||||
max_storage_space = INVENTORY_STANDARD_SPACE
|
||||
|
||||
/// Null, or a list of (/obj/item/foo = "item key"), or (/path = ("item key", proc/handler)).
|
||||
var/list/attachable_types
|
||||
|
||||
/// Null, or a list of ("item key" = null|Instance)
|
||||
var/list/attached_items
|
||||
|
||||
/// The time it takes for the wearer to adjust this harness' attachments.
|
||||
var/self_attach_delay = 5 SECONDS
|
||||
|
||||
/// The time it takes for someone else to adjust this harness' attachments.
|
||||
var/other_attach_delay = 3 SECONDS
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/Destroy()
|
||||
var/mob/living/simple_mob/animal/wearer = loc
|
||||
if (istype(wearer) && wearer.harness == src)
|
||||
wearer.harness = null
|
||||
for (var/key in attached_items)
|
||||
var/obj/item/item = attached_items[key]
|
||||
qdel(item)
|
||||
LAZYCLEARLIST(attached_items)
|
||||
attachable_types = null
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/Initialize()
|
||||
. = ..()
|
||||
if (!attachable_types)
|
||||
return
|
||||
attached_items = list()
|
||||
for (var/path in attachable_types)
|
||||
var/key = attachable_types[path]
|
||||
attached_items[key] = null
|
||||
CreateAttachments()
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/LateInitializeName()
|
||||
return
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/attackby(obj/item/item, mob/living/user, silent)
|
||||
. = TRUE
|
||||
if (user == loc) // Animals can only shove stuff in their storage.
|
||||
return ..()
|
||||
for (var/path in attachable_types)
|
||||
if (!istype(item, path))
|
||||
continue
|
||||
var/key = attachable_types[path]
|
||||
var/handler
|
||||
if (islist(key))
|
||||
handler = key[2]
|
||||
key = key[1]
|
||||
if (attached_items[key])
|
||||
if (!silent)
|
||||
var/datum/gender/gender
|
||||
if (ismob(loc))
|
||||
var/mob/living/simple_mob/animal/owner = loc
|
||||
gender = gender_datums[owner.get_visible_gender()]
|
||||
to_chat(user, SPAN_WARNING("\The [loc] already has \a [key] attached to [gender ? gender.his : "its"] harness."))
|
||||
return
|
||||
if (!user.unEquip(item, target = loc))
|
||||
return
|
||||
if (!silent)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] attaches \the [item] to \the [loc]'s harness."))
|
||||
attached_items[key] = item
|
||||
if (handler)
|
||||
call(src, handler)(item)
|
||||
return
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/proc/GetAttachedKeys()
|
||||
var/list/obj/item/result = list()
|
||||
for (var/key in attached_items)
|
||||
if (attached_items[key])
|
||||
result += key
|
||||
return result
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/proc/GetAttachedItems()
|
||||
var/list/obj/item/result = list()
|
||||
for (var/key in attached_items)
|
||||
var/obj/item/item = attached_items[key]
|
||||
if (item)
|
||||
result += item
|
||||
return result
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/proc/RemoveAttachment(obj/item/attachment)
|
||||
if (istext(attachment))
|
||||
var/obj/item/item = attached_items[attachment]
|
||||
if (!item)
|
||||
return
|
||||
item.dropInto(get_turf(src))
|
||||
attached_items[attachment] = null
|
||||
return item
|
||||
for (var/key in attached_items)
|
||||
if (attached_items[key] == attachment)
|
||||
attachment.dropInto(get_turf(src))
|
||||
attached_items[key] = null
|
||||
return attachment
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/proc/CreateAttachments()
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal
|
||||
var/obj/item/storage/internal/animal_harness/harness
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/Destroy()
|
||||
if (istype(harness))
|
||||
QDEL_NULL(harness)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/Initialize()
|
||||
. = ..()
|
||||
if (ispath(harness))
|
||||
harness = new harness (src)
|
||||
verbs += /mob/living/simple_mob/animal/proc/RemoveAttachmentVerb
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/examine(mob/living/user)
|
||||
. = ..()
|
||||
if (!harness)
|
||||
return
|
||||
. += "\The [src] is wearing \a [harness]."
|
||||
for (var/obj/item/item in harness.GetAttachedItems())
|
||||
. += "There is \a [item] attached."
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/proc/RemoveAttachmentVerb()
|
||||
set name = "Remove Harness Attachment"
|
||||
set category = "IC"
|
||||
set src in view(1)
|
||||
var/mob/living/user = usr
|
||||
if (!istype(user))
|
||||
return
|
||||
if (!Adjacent(user) || user.incapacitated())
|
||||
to_chat(user, SPAN_WARNING("You are in no condition to do that."))
|
||||
return
|
||||
if (!user.check_dexterity(MOB_DEXTERITY_SIMPLE_MACHINES))
|
||||
return
|
||||
var/list/attached = harness.GetAttachedKeys()
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
if (!length(attached))
|
||||
to_chat(user, SPAN_WARNING("\The [src] has nothing attached to [gender.his] [harness.name]."))
|
||||
return
|
||||
var/obj/item/response = input(user, "Select attachment to remove:") as null | anything in attached
|
||||
if (!response)
|
||||
return
|
||||
if (!(response in harness.GetAttachedKeys()))
|
||||
to_chat(user, SPAN_WARNING("\The [response] is already missing from \the [harness]."))
|
||||
return
|
||||
if (!Adjacent(user) || user.incapacitated())
|
||||
to_chat(user, SPAN_WARNING("You are in no condition to do that."))
|
||||
return
|
||||
user.visible_message(
|
||||
SPAN_ITALIC("\The [user] begins to remove \a [response] from [(user == src) ? "[gender.his]" : "\the [src]'s"] harness."),
|
||||
SPAN_ITALIC("You begin to remove \the [response] from [(user == src) ? "your" : "\the [src]'s"] harness."),
|
||||
range = 5
|
||||
)
|
||||
if (!do_after(user, (user == src) ? harness.self_attach_delay : harness.other_attach_delay, loc))
|
||||
return
|
||||
if (QDELETED(harness))
|
||||
return
|
||||
if (!(response in harness.GetAttachedKeys()))
|
||||
to_chat(user, SPAN_WARNING("\The [response] is already missing from \the [src]."))
|
||||
return
|
||||
var/obj/item/removed = harness.RemoveAttachment(response)
|
||||
user.put_in_hands(removed)
|
||||
@@ -1,120 +1,3 @@
|
||||
/datum/modifier/sifsap_salve
|
||||
name = "Sifsap Salve"
|
||||
desc = "Your wounds have been salved with Sivian sap."
|
||||
mob_overlay_state = "cyan_sparkles"
|
||||
stacks = MODIFIER_STACK_FORBID
|
||||
on_created_text = "<span class = 'notice'>The glowing sap seethes and bubbles in your wounds, tingling and stinging.</span>"
|
||||
on_expired_text = "<span class = 'notice'>The last of the sap in your wounds fizzles away.</span>"
|
||||
|
||||
/datum/modifier/sifsap_salve/tick()
|
||||
|
||||
if(holder.stat == DEAD || holder.isSynthetic())
|
||||
expire()
|
||||
|
||||
if(istype(holder, /mob/living/simple_mob/animal/sif))
|
||||
|
||||
var/mob/living/simple_mob/animal/sif/critter = holder
|
||||
if(critter.health >= (critter.getMaxHealth() * critter.sap_heal_threshold))
|
||||
return
|
||||
|
||||
if(holder.resting)
|
||||
if(istype(holder.loc, /obj/structure/animal_den))
|
||||
holder.adjustBruteLoss(-3)
|
||||
holder.adjustFireLoss(-3)
|
||||
holder.adjustToxLoss(-2)
|
||||
else
|
||||
holder.adjustBruteLoss(-2)
|
||||
holder.adjustFireLoss(-2)
|
||||
holder.adjustToxLoss(-1)
|
||||
else
|
||||
holder.adjustBruteLoss(-1)
|
||||
holder.adjustFireLoss(-1)
|
||||
|
||||
/obj/item/projectile/drake_spit
|
||||
name = "drake spit"
|
||||
icon_state = "ice_1"
|
||||
damage = 0
|
||||
embed_chance = 0
|
||||
damage_type = BRUTE
|
||||
muzzle_type = null
|
||||
hud_state = "monkey"
|
||||
combustion = FALSE
|
||||
stun = 3
|
||||
weaken = 3
|
||||
eyeblur = 5
|
||||
fire_sound = 'sound/effects/splat.ogg'
|
||||
|
||||
/obj/item/projectile/drake_spit/weak
|
||||
stun = 0
|
||||
weaken = 0
|
||||
eyeblur = 2
|
||||
|
||||
/datum/category_item/catalogue/fauna/grafadreka
|
||||
name = "Sivian Fauna - Grafadreka"
|
||||
desc = {"Classification: S tesca pabulator
|
||||
<br><br>
|
||||
The reclusive grafadreka (Icelandic, lit. 'digging dragon'), also known as the snow drake, is a large reptillian pack predator similar in size and morphology to old Earth hyenas. They commonly dig shallow dens in dirt, snow or foliage, sometimes using them for concealment prior to an ambush. Biological cousins to the elusive kururak, they have heavy, low-slung bodies and powerful jaws suited to hunting land prey rather than fishing. Colonization and subsequent expansion have displaced many populations from their tundral territories into colder areas; as a result, their diet of Sivian prey animals has pivoted to a diet of giant spider meat.
|
||||
<br><br>
|
||||
Grafadrekas are capable of exerting bite pressures in excess of 900 PSI, which allows them to crack bones or carapace when scavenging for food. While they share the hypercarnivorous metabolism of their cousins, they have developed a symbiotic relationship with the bacteria responsible for the bioluminescence of Sivian trees. This assists with digesting plant matter, and gives their pelts a distinctive and eerie glow.
|
||||
<br><br>
|
||||
They have been observed to occasionally attack and kill colonists, generally when conditions are too poor to hunt their usual prey. Despite this, and despite their disposition being generally skittish and avoidant of colonists, some Sivian communities hold that they have been observed to guide or protect lost travellers.
|
||||
<br><br>
|
||||
Field studies suggest analytical abilities on par with some species of cepholapods, but their symbiotic physiology rapidly fails in captivity, making laboratory testing difficult. Their inability to make use of tools or form wider social groups beyond a handful of individuals has been hypothesised to prevent the expression of more complex social behaviors."}
|
||||
value = CATALOGUER_REWARD_HARD
|
||||
|
||||
/decl/mob_organ_names/grafadreka
|
||||
hit_zones = list(
|
||||
"head",
|
||||
"chest",
|
||||
"left foreleg",
|
||||
"right foreleg",
|
||||
"left hind leg",
|
||||
"right hind leg",
|
||||
"face spines",
|
||||
"body spines",
|
||||
"tail spines",
|
||||
"tail"
|
||||
)
|
||||
|
||||
/decl/emote/audible/drake_howl
|
||||
key = "dhowl"
|
||||
emote_message_3p = "lifts USER_THEIR head up and gives an eerie howl."
|
||||
emote_sound = 'sound/effects/drakehowl_close.ogg'
|
||||
broadcast_sound ='sound/effects/drakehowl_far.ogg'
|
||||
emote_cooldown = 20 SECONDS
|
||||
broadcast_distance = 90
|
||||
|
||||
/decl/emote/audible/drake_howl/broadcast_emote_to(var/send_sound, var/mob/target, var/direction)
|
||||
if((. = ..()))
|
||||
to_chat(target, SPAN_NOTICE("You hear an eerie howl from somewhere to the [dir2text(direction)]."))
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_available_emotes()
|
||||
if(!is_baby)
|
||||
return global._default_mob_emotes | /decl/emote/audible/drake_howl
|
||||
return global._default_mob_emotes
|
||||
|
||||
// Overriding this to handle sitting.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/lay_down()
|
||||
. = ..()
|
||||
if(!resting && sitting)
|
||||
sitting = FALSE
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/verb/sit_down()
|
||||
set name = "Sit Down"
|
||||
set category = "IC"
|
||||
|
||||
if(sitting)
|
||||
resting = FALSE
|
||||
sitting = FALSE
|
||||
else
|
||||
resting = TRUE
|
||||
sitting = TRUE
|
||||
|
||||
to_chat(src, SPAN_NOTICE("You are now [sitting ? "sitting" : "getting up"]."))
|
||||
update_canmove()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka
|
||||
name = "grafadreka"
|
||||
desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body."
|
||||
@@ -211,195 +94,276 @@ Field studies suggest analytical abilities on par with some species of cepholapo
|
||||
|
||||
var/list/original_armor
|
||||
|
||||
var/global/list/wounds_being_tended_by_drakes = list()
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/can_tend_wounds(var/mob/living/friend)
|
||||
/// A convenience value for whether this drake is the trained subtype.
|
||||
var/trained_drake = FALSE
|
||||
|
||||
// We can't heal robots.
|
||||
if(friend.isSynthetic())
|
||||
return FALSE
|
||||
|
||||
// Check if someone else is looking after them already.
|
||||
if(global.wounds_being_tended_by_drakes["\ref[friend]"] > world.time)
|
||||
return FALSE
|
||||
|
||||
// Humans need to have a bleeding external organ to qualify.
|
||||
if(ishuman(friend))
|
||||
var/mob/living/carbon/human/H = friend
|
||||
for(var/obj/item/organ/external/E in H.bad_external_organs)
|
||||
if(E.status & ORGAN_BLEEDING)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Sif animals need to be able to regenerate past their current HP value.
|
||||
if(istype(friend, /mob/living/simple_mob/animal/sif))
|
||||
var/mob/living/simple_mob/animal/sif/critter = friend
|
||||
return critter.health < (critter.getMaxHealth() * critter.sap_heal_threshold)
|
||||
|
||||
// Other animals just need to be injured.
|
||||
return (friend.health < friend.maxHealth)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Initialize()
|
||||
|
||||
charisma = rand(5, 15)
|
||||
stored_sap = rand(20, 30)
|
||||
nutrition = rand(400,500)
|
||||
|
||||
if(gender == NEUTER)
|
||||
if (gender == NEUTER)
|
||||
gender = pick(MALE, FEMALE)
|
||||
attacktext = claw_attacktext.Copy()
|
||||
|
||||
setup_colours()
|
||||
create_reagents(50)
|
||||
|
||||
. = ..()
|
||||
|
||||
original_armor = armor
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/examine(var/mob/living/user)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Stat()
|
||||
. = ..()
|
||||
if(istype(user, /mob/living/simple_mob/animal/sif/grafadreka) || isobserver(user))
|
||||
if (statpanel("Status"))
|
||||
stat("Nutrition:", "[nutrition]/[max_nutrition]")
|
||||
stat("Stored sap:", "[stored_sap]/[max_stored_sap]")
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Login()
|
||||
. = ..()
|
||||
charisma = (client && !is_baby) ? INFINITY : 0
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Logout()
|
||||
. = ..()
|
||||
if (!client)
|
||||
charisma = rand(5, 15)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/examine(mob/living/user)
|
||||
. = ..()
|
||||
if (istype(user, /mob/living/simple_mob/animal/sif/grafadreka) || isobserver(user))
|
||||
var/datum/gender/G = gender_datums[get_visible_gender()]
|
||||
if(stored_sap >= 20)
|
||||
if (stored_sap >= 20)
|
||||
. += SPAN_NOTICE("[G.His] sap reserves are high.")
|
||||
else if(stored_sap >= 10)
|
||||
else if (stored_sap >= 10)
|
||||
. += SPAN_WARNING("[G.His] sap reserves are running low.")
|
||||
else
|
||||
. += SPAN_DANGER("[G.His] sap reserves are depleted.")
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/can_projectile_attack(var/atom/A)
|
||||
if(a_intent != I_HURT || world.time < next_spit)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/do_interaction(atom/atom)
|
||||
if (atom.interaction_grafadreka(src))
|
||||
return ATTACK_SUCCESSFUL
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_available_emotes()
|
||||
if (!is_baby)
|
||||
return global._default_mob_emotes | /decl/emote/audible/drake_howl
|
||||
return global._default_mob_emotes
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/lay_down()
|
||||
if (sitting)
|
||||
to_chat(src, SPAN_NOTICE("You are now lying down."))
|
||||
sitting = FALSE
|
||||
update_icon()
|
||||
else
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/can_projectile_attack(atom/target)
|
||||
if (a_intent != I_HURT || world.time < next_spit)
|
||||
return FALSE
|
||||
if(!has_sap(2))
|
||||
if (!has_sap(2))
|
||||
to_chat(src, SPAN_WARNING("You have no sap to spit!"))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
|
||||
// Checking this in the proc itself as AI doesn't seem to care about ranged attack cooldowns.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/shoot_target(atom/A)
|
||||
if(world.time < next_spit || !has_sap(2))
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/shoot_target(atom/target)
|
||||
if (world.time < next_spit || !has_sap(2))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
if (.)
|
||||
next_spit = world.time + spit_cooldown
|
||||
setMoveCooldown(1 SECOND)
|
||||
spend_sap(2)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_dietary_food_modifier(var/datum/reagent/nutriment/food)
|
||||
if(food.allergen_type & ALLERGEN_MEAT)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_dietary_food_modifier(datum/reagent/nutriment/food)
|
||||
if (food.allergen_type & ALLERGEN_MEAT)
|
||||
return ..()
|
||||
return 0.25 // Quarter nutrition from non-meat.
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/handle_reagent_transfer(var/datum/reagents/holder, var/amount = 1, var/chem_type = CHEM_BLOOD, var/multiplier = 1, var/copy = 0)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/handle_reagent_transfer(datum/reagents/holder, amount = 1, chem_type = CHEM_BLOOD, multiplier = 1, copy = 0)
|
||||
return holder.trans_to_holder(reagents, amount, multiplier, copy)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Life()
|
||||
. = ..()
|
||||
|
||||
if(stat == CONSCIOUS)
|
||||
|
||||
if (stat == CONSCIOUS)
|
||||
// Don't make clientless drakes lose nutrition or they'll all go feral.
|
||||
if(!resting && client)
|
||||
if (!resting && client)
|
||||
remove_nutrition(0.3)
|
||||
|
||||
// Very slowly regenerate enough sap to defend ourselves. spit is 2 sap,
|
||||
// spit cooldown is 8s, life is 2s, so this is one free spit per 12 seconds.
|
||||
if(stored_sap < 10)
|
||||
if (stored_sap < 10)
|
||||
add_sap(0.35)
|
||||
|
||||
// Process food and sap chems.
|
||||
if(reagents?.total_volume)
|
||||
for(var/datum/reagent/chem in reagents.reagent_list)
|
||||
if (reagents?.total_volume)
|
||||
for (var/datum/reagent/chem in reagents.reagent_list)
|
||||
var/removed = clamp(chem.ingest_met, REM, chem.volume)
|
||||
chem.affect_animal(src, removed)
|
||||
reagents.remove_reagent(chem.id, removed)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/has_sap(var/amt)
|
||||
return stored_sap >= amt
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/add_sap(var/amt)
|
||||
stored_sap = clamp(round(stored_sap + amt, 0.01), 0, max_stored_sap)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/spend_sap(var/amt)
|
||||
if(has_sap(amt))
|
||||
stored_sap = clamp(round(stored_sap - amt, 0.01), 0, max_stored_sap)
|
||||
update_icon()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/setup_colours()
|
||||
|
||||
var/static/list/fur_colours = list(COLOR_SILVER, COLOR_WHITE, COLOR_GREEN_GRAY, COLOR_PALE_RED_GRAY, COLOR_BLUE_GRAY)
|
||||
var/static/list/claw_colours = list(COLOR_GRAY, COLOR_SILVER, COLOR_WHITE, COLOR_GRAY15, COLOR_GRAY20, COLOR_GRAY40, COLOR_GRAY80)
|
||||
var/static/list/glow_colours = list(COLOR_BLUE_LIGHT, COLOR_LIGHT_CYAN, COLOR_CYAN, COLOR_CYAN_BLUE)
|
||||
var/static/list/base_colours = list("#608894", "#436974", "#7fa3ae")
|
||||
var/static/list/eye_colours = list(COLOR_WHITE, COLOR_SILVER)
|
||||
|
||||
if(!glow_colour)
|
||||
glow_colour = pick(glow_colours)
|
||||
if(!fur_colour)
|
||||
fur_colour = pick(fur_colours)
|
||||
if(!claw_colour)
|
||||
claw_colour = pick(claw_colours)
|
||||
if(!base_colour)
|
||||
base_colour = pick(base_colours)
|
||||
if(!eye_colour)
|
||||
eye_colour = pick(eye_colours)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/movement_delay(oldloc, direct)
|
||||
. = ..()
|
||||
if(istype(loc, /turf/space))
|
||||
if (istype(loc, /turf/space))
|
||||
return
|
||||
var/health_slowdown_threshold = round(maxHealth * 0.65)
|
||||
if(health < health_slowdown_threshold)
|
||||
if (health < health_slowdown_threshold)
|
||||
. += round(5 * (1-(health / health_slowdown_threshold)), 0.1)
|
||||
var/nut_slowdown_threshold = round(max_nutrition * 0.65)
|
||||
if(nutrition < nut_slowdown_threshold)
|
||||
if (nutrition < nut_slowdown_threshold)
|
||||
. += round(5 * (1-(nutrition / nut_slowdown_threshold)), 0.1)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/update_icon()
|
||||
|
||||
. = ..()
|
||||
|
||||
if(sitting && stat == CONSCIOUS)
|
||||
if (sitting && stat == CONSCIOUS)
|
||||
icon_state = "[initial(icon_state)]_sitting"
|
||||
|
||||
var/list/add_images = list()
|
||||
var/image/I = image(icon, "[icon_state]")
|
||||
I.color = base_colour
|
||||
add_images += I
|
||||
|
||||
I = image(icon, "[icon_state]-fur")
|
||||
I.color = fur_colour
|
||||
add_images += I
|
||||
|
||||
I = image(icon, "[icon_state]-claws")
|
||||
I.color = claw_colour
|
||||
add_images += I
|
||||
|
||||
if(stat == CONSCIOUS && !sleeping)
|
||||
if (stat == CONSCIOUS && !sleeping)
|
||||
I = image(icon, "[icon_state]-eye_overlay")
|
||||
I.color = eye_colour
|
||||
add_images += I
|
||||
|
||||
if(stat != DEAD)
|
||||
if (stat != DEAD)
|
||||
var/glow = add_glow()
|
||||
if(glow)
|
||||
if (glow)
|
||||
add_images += glow
|
||||
|
||||
for(var/image/adding in add_images)
|
||||
for (var/image/adding in add_images)
|
||||
adding.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
|
||||
if(offset_compiled_icon)
|
||||
if (offset_compiled_icon)
|
||||
adding.pixel_x = offset_compiled_icon // Offset here so that things like modifiers, runechat text, etc. are centered
|
||||
add_overlay(adding)
|
||||
|
||||
// We do this last so the default mob icon_state can be used for the overlays.
|
||||
current_icon_state = icon_state
|
||||
icon_state = "blank"
|
||||
color = COLOR_WHITE // Due to KEEP_TOGETHER etc. overlays ignore RESET_COLOR.
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_eye_color()
|
||||
return eye_colour
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/do_tame(obj/obj, mob/user)
|
||||
. = ..()
|
||||
attacked_by_neutral = FALSE
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/handle_special()
|
||||
..()
|
||||
if(client || world.time >= next_leader_check)
|
||||
next_leader_check = world.time + (60 SECONDS)
|
||||
check_leader_status()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/do_help_interaction(atom/A)
|
||||
if(isliving(A))
|
||||
var/mob/living/friend = A
|
||||
if (friend.stat == DEAD)
|
||||
if(friend == src)
|
||||
to_chat(src, SPAN_WARNING("\The [friend] is dead; tending their wounds is pointless."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
if (!can_tend_wounds(friend))
|
||||
if (friend == src)
|
||||
if (health == maxHealth)
|
||||
to_chat(src, SPAN_WARNING("You are unwounded."))
|
||||
else
|
||||
to_chat(src, SPAN_WARNING("You cannot tend any of your wounds."))
|
||||
else
|
||||
if (friend.health == friend.maxHealth)
|
||||
return ..()
|
||||
to_chat(src, SPAN_WARNING("You cannot tend any of \the [friend]'s wounds."))
|
||||
return TRUE
|
||||
if (friend.has_modifier_of_type(/datum/modifier/sifsap_salve))
|
||||
if (friend == src)
|
||||
to_chat(src, SPAN_WARNING("You have already cleaned your wounds."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
if (!has_sap(10))
|
||||
if (friend == src)
|
||||
to_chat(src, SPAN_WARNING("You don't have enough sap to clean your wounds."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
if (friend == src)
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over their wounds."))
|
||||
else
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over \the [friend]'s wounds."))
|
||||
playsound(src, 'sound/effects/ointment.ogg', 25)
|
||||
var/friend_ref = "\ref[friend]"
|
||||
global.wounds_being_tended_by_drakes[friend_ref] = world.time + (8 SECONDS)
|
||||
set_AI_busy(TRUE)
|
||||
if (!do_after(src, 8 SECONDS, friend) || QDELETED(friend) || friend.has_modifier_of_type(/datum/modifier/sifsap_salve) || incapacitated() || !spend_sap(10))
|
||||
global.wounds_being_tended_by_drakes -= friend_ref
|
||||
set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
global.wounds_being_tended_by_drakes -= friend_ref
|
||||
set_AI_busy(FALSE)
|
||||
if (friend == src)
|
||||
visible_message(SPAN_NOTICE("\The [src] finishes licking at their wounds."))
|
||||
else
|
||||
visible_message(SPAN_NOTICE("\The [src] finishes licking at \the [friend]'s wounds."))
|
||||
playsound(src, 'sound/effects/ointment.ogg', 25)
|
||||
// Sivian animals get a heal buff from the modifier, others just
|
||||
// get it to stop friendly drakes constantly licking their wounds.
|
||||
friend.add_modifier(/datum/modifier/sifsap_salve, 60 SECONDS)
|
||||
// Human wounds are closed, but they get sifsap via open wounds.
|
||||
if (ishuman(friend))
|
||||
var/mob/living/carbon/human/H = friend
|
||||
for (var/obj/item/organ/external/E in H.organs)
|
||||
if (E.status & ORGAN_BLEEDING)
|
||||
E.organ_clamp()
|
||||
H.bloodstr.add_reagent("sifsap", rand(1,2))
|
||||
for (var/datum/wound/W in E.wounds)
|
||||
W.salve()
|
||||
W.disinfect()
|
||||
// Everyone else is just poisoned.
|
||||
else if (!istype(friend, /mob/living/simple_mob/animal/sif))
|
||||
friend.adjustToxLoss(rand(10,20))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
// Eating sifsap makes bites toxic and changes our glow intensity.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/apply_attack(atom/A, damage_to_do)
|
||||
var/tox_damage = 0
|
||||
if (!attacking_with_claws && isliving(A) && has_sap(5))
|
||||
tox_damage = rand(5,15)
|
||||
. = ..()
|
||||
if (. && tox_damage && spend_sap(5))
|
||||
var/mob/living/M = A
|
||||
M.adjustToxLoss(tox_damage)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/rejuvenate()
|
||||
remove_modifiers_of_type(/datum/modifier/sifsap_salve, TRUE)
|
||||
stored_sap = rand(20, 30)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/has_appetite()
|
||||
return reagents && abs(reagents.total_volume - reagents.maximum_volume) >= 10
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/add_glow()
|
||||
var/image/I = image(icon, "[icon_state]-glow")
|
||||
I.color = glow_colour
|
||||
@@ -407,140 +371,99 @@ var/global/list/wounds_being_tended_by_drakes = list()
|
||||
I.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1))
|
||||
return I
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_eye_color()
|
||||
return eye_colour
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/do_tame(var/obj/O, var/mob/user)
|
||||
. = ..()
|
||||
attacked_by_neutral = FALSE
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/has_sap(amount)
|
||||
return stored_sap >= amount
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/handle_special()
|
||||
..()
|
||||
if(client || world.time >= next_leader_check)
|
||||
next_leader_check = world.time + (60 SECONDS)
|
||||
check_leader_status()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/do_help_interaction(atom/A)
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/add_sap(amount)
|
||||
stored_sap = clamp(round(stored_sap + amount, 0.01), 0, max_stored_sap)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if(isliving(A))
|
||||
|
||||
var/mob/living/friend = A
|
||||
if(friend.stat == DEAD)
|
||||
if(friend == src)
|
||||
to_chat(src, SPAN_WARNING("\The [friend] is dead; tending their wounds is pointless."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
|
||||
if(!can_tend_wounds(friend))
|
||||
if(friend == src)
|
||||
if(health == maxHealth)
|
||||
to_chat(src, SPAN_WARNING("You are unwounded."))
|
||||
else
|
||||
to_chat(src, SPAN_WARNING("You cannot tend any of your wounds."))
|
||||
else
|
||||
if(friend.health == friend.maxHealth)
|
||||
return ..()
|
||||
to_chat(src, SPAN_WARNING("You cannot tend any of \the [friend]'s wounds."))
|
||||
return TRUE
|
||||
|
||||
if(friend.has_modifier_of_type(/datum/modifier/sifsap_salve))
|
||||
if(friend == src)
|
||||
to_chat(src, SPAN_WARNING("You have already cleaned your wounds."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
|
||||
if(!has_sap(10))
|
||||
if(friend == src)
|
||||
to_chat(src, SPAN_WARNING("You don't have enough sap to clean your wounds."))
|
||||
else
|
||||
return ..()
|
||||
return TRUE
|
||||
|
||||
if(friend == src)
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over their wounds."))
|
||||
else
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over \the [friend]'s wounds."))
|
||||
|
||||
playsound(src, 'sound/effects/ointment.ogg', 25)
|
||||
|
||||
var/friend_ref = "\ref[friend]"
|
||||
global.wounds_being_tended_by_drakes[friend_ref] = world.time + (8 SECONDS)
|
||||
set_AI_busy(TRUE)
|
||||
|
||||
if(!do_after(src, 8 SECONDS, friend) || QDELETED(friend) || friend.has_modifier_of_type(/datum/modifier/sifsap_salve) || incapacitated() || !spend_sap(10))
|
||||
global.wounds_being_tended_by_drakes -= friend_ref
|
||||
set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
|
||||
global.wounds_being_tended_by_drakes -= friend_ref
|
||||
set_AI_busy(FALSE)
|
||||
|
||||
if(friend == src)
|
||||
visible_message(SPAN_NOTICE("\The [src] finishes licking at their wounds."))
|
||||
else
|
||||
visible_message(SPAN_NOTICE("\The [src] finishes licking at \the [friend]'s wounds."))
|
||||
playsound(src, 'sound/effects/ointment.ogg', 25)
|
||||
|
||||
// Sivian animals get a heal buff from the modifier, others just
|
||||
// get it to stop friendly drakes constantly licking their wounds.
|
||||
friend.add_modifier(/datum/modifier/sifsap_salve, 60 SECONDS)
|
||||
// Human wounds are closed, but they get sifsap via open wounds.
|
||||
if(ishuman(friend))
|
||||
var/mob/living/carbon/human/H = friend
|
||||
for(var/obj/item/organ/external/E in H.organs)
|
||||
if(E.status & ORGAN_BLEEDING)
|
||||
E.organ_clamp()
|
||||
H.bloodstr.add_reagent("sifsap", rand(1,2))
|
||||
for(var/datum/wound/W in E.wounds)
|
||||
W.salve()
|
||||
W.disinfect()
|
||||
|
||||
// Everyone else is just poisoned.
|
||||
else if(!istype(friend, /mob/living/simple_mob/animal/sif))
|
||||
friend.adjustToxLoss(rand(10,20))
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/spend_sap(amount)
|
||||
if (has_sap(amount))
|
||||
stored_sap = clamp(round(stored_sap - amount, 0.01), 0, max_stored_sap)
|
||||
update_icon()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/setup_colours()
|
||||
var/static/list/fur_colours = list(COLOR_SILVER, COLOR_WHITE, COLOR_GREEN_GRAY, COLOR_PALE_RED_GRAY, COLOR_BLUE_GRAY)
|
||||
var/static/list/claw_colours = list(COLOR_GRAY, COLOR_SILVER, COLOR_WHITE, COLOR_GRAY15, COLOR_GRAY20, COLOR_GRAY40, COLOR_GRAY80)
|
||||
var/static/list/glow_colours = list(COLOR_BLUE_LIGHT, COLOR_LIGHT_CYAN, COLOR_CYAN, COLOR_CYAN_BLUE)
|
||||
var/static/list/base_colours = list("#608894", "#436974", "#7fa3ae")
|
||||
var/static/list/eye_colours = list(COLOR_WHITE, COLOR_SILVER)
|
||||
if (!glow_colour)
|
||||
glow_colour = pick(glow_colours)
|
||||
if (!fur_colour)
|
||||
fur_colour = pick(fur_colours)
|
||||
if (!claw_colour)
|
||||
claw_colour = pick(claw_colours)
|
||||
if (!base_colour)
|
||||
base_colour = pick(base_colours)
|
||||
if (!eye_colour)
|
||||
eye_colour = pick(eye_colours)
|
||||
|
||||
|
||||
var/global/list/wounds_being_tended_by_drakes = list()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/can_tend_wounds(mob/living/friend)
|
||||
// We can't heal robots.
|
||||
if (friend.isSynthetic())
|
||||
return FALSE
|
||||
// Check if someone else is looking after them already.
|
||||
if (global.wounds_being_tended_by_drakes["\ref[friend]"] > world.time)
|
||||
return FALSE
|
||||
// Humans need to have a bleeding external organ to qualify.
|
||||
if (ishuman(friend))
|
||||
var/mob/living/carbon/human/H = friend
|
||||
for (var/obj/item/organ/external/E in H.bad_external_organs)
|
||||
if (E.status & ORGAN_BLEEDING)
|
||||
return TRUE
|
||||
return FALSE
|
||||
// Sif animals need to be able to regenerate past their current HP value.
|
||||
if (istype(friend, /mob/living/simple_mob/animal/sif))
|
||||
var/mob/living/simple_mob/animal/sif/critter = friend
|
||||
return critter.health < (critter.getMaxHealth() * critter.sap_heal_threshold)
|
||||
// Other animals just need to be injured.
|
||||
return (friend.health < friend.maxHealth)
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/get_pack_leader()
|
||||
var/pack = FALSE
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/leader
|
||||
if(!is_baby)
|
||||
if (!is_baby)
|
||||
leader = src
|
||||
for(var/mob/living/simple_mob/animal/sif/grafadreka/follower in hearers(7, loc))
|
||||
if(follower == src || follower.is_baby || follower.stat == DEAD || follower.faction != faction)
|
||||
for (var/mob/living/simple_mob/animal/sif/grafadreka/follower in hearers(7, loc))
|
||||
if (follower == src || follower.is_baby || follower.stat == DEAD || follower.faction != faction)
|
||||
continue
|
||||
pack = TRUE
|
||||
if(!leader || follower.charisma > leader.charisma)
|
||||
if (!leader || follower.charisma > leader.charisma)
|
||||
leader = follower
|
||||
if(pack)
|
||||
if (pack)
|
||||
return leader
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/check_leader_status()
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/leader = get_pack_leader()
|
||||
if(src == leader)
|
||||
if (src == leader)
|
||||
add_modifier(/datum/modifier/ace, 60 SECONDS)
|
||||
else
|
||||
remove_modifiers_of_type(/datum/modifier/ace)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Stat()
|
||||
. = ..()
|
||||
if(statpanel("Status"))
|
||||
stat("Nutrition:", "[nutrition]/[max_nutrition]")
|
||||
stat("Stored sap:", "[stored_sap]/[max_stored_sap]")
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/can_bite(var/mob/living/M)
|
||||
return istype(M) && (M.lying || M.confused || M.incapacitated())
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/apply_bonus_melee_damage(atom/A, damage_amount)
|
||||
// Melee attack on incapacitated or prone enemies bites instead of slashing
|
||||
var/last_attack_was_claws = attacking_with_claws
|
||||
attacking_with_claws = !can_bite(A)
|
||||
|
||||
if(last_attack_was_claws != attacking_with_claws)
|
||||
if(attacking_with_claws) // Use claws.
|
||||
if (last_attack_was_claws != attacking_with_claws)
|
||||
if (attacking_with_claws) // Use claws.
|
||||
attack_armor_pen = initial(attack_armor_pen)
|
||||
attack_sound = initial(attack_sound)
|
||||
attacktext = claw_attacktext.Copy()
|
||||
@@ -551,83 +474,33 @@ var/global/list/wounds_being_tended_by_drakes = list()
|
||||
attacktext = bite_attacktext.Copy()
|
||||
. = ..()
|
||||
|
||||
// Eating sifsap makes bites toxic and changes our glow intensity.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/apply_attack(atom/A, damage_to_do)
|
||||
var/tox_damage = 0
|
||||
if(!attacking_with_claws && isliving(A) && has_sap(5))
|
||||
tox_damage = rand(5,15)
|
||||
. = ..()
|
||||
if(. && tox_damage && spend_sap(5))
|
||||
var/mob/living/M = A
|
||||
M.adjustToxLoss(tox_damage)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/verb/sit_down()
|
||||
set name = "Sit Down"
|
||||
set category = "IC"
|
||||
if (sitting)
|
||||
resting = FALSE
|
||||
sitting = FALSE
|
||||
else
|
||||
resting = TRUE
|
||||
sitting = TRUE
|
||||
to_chat(src, SPAN_NOTICE("You are now [sitting ? "sitting" : "getting up"]."))
|
||||
update_canmove()
|
||||
update_icon()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/verb/rally_pack()
|
||||
set name = "Rally Pack"
|
||||
set desc = "Tries to command your fellow pack members to follow you."
|
||||
set category = "Abilities"
|
||||
|
||||
if(!has_modifier_of_type(/datum/modifier/ace))
|
||||
if (!has_modifier_of_type(/datum/modifier/ace))
|
||||
to_chat(src, SPAN_WARNING("You aren't the pack leader! Sit down!"))
|
||||
return
|
||||
|
||||
audible_message("<b>\The [src]</b> barks loudly and rattles its neck spines.")
|
||||
for(var/mob/living/simple_mob/animal/sif/grafadreka/drake in hearers(world.view * 3, src))
|
||||
if(drake == src || drake.faction != faction)
|
||||
for (var/mob/living/simple_mob/animal/sif/grafadreka/drake in hearers(world.view * 3, src))
|
||||
if (drake == src || drake.faction != faction)
|
||||
continue
|
||||
if(drake.client)
|
||||
if (drake.client)
|
||||
to_chat(drake, SPAN_NOTICE("<b>The pack leader wishes for you to follow them.</b>"))
|
||||
else if(drake.ai_holder)
|
||||
else if (drake.ai_holder)
|
||||
drake.ai_holder.set_follow(src)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/has_appetite()
|
||||
return reagents && abs(reagents.total_volume - reagents.maximum_volume) >= 10
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Login()
|
||||
. = ..()
|
||||
charisma = (client && !is_baby) ? INFINITY : 0
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Logout()
|
||||
. = ..()
|
||||
if(!client)
|
||||
charisma = rand(5, 15)
|
||||
|
||||
/datum/say_list/grafadreka
|
||||
speak = list("Chff!","Skhh.", "Rrrss...")
|
||||
emote_see = list("scratches its ears","grooms its spines", "sways its tail", "claws at the ground")
|
||||
emote_hear = list("hisses", "rattles", "rasps", "barks")
|
||||
|
||||
/obj/structure/animal_den/ghost_join/grafadreka
|
||||
name = "drake den"
|
||||
critter = /mob/living/simple_mob/animal/sif/grafadreka
|
||||
|
||||
/obj/structure/animal_den/ghost_join/grafadreka_hatchling
|
||||
name = "drake hatchling den"
|
||||
critter = /mob/living/simple_mob/animal/sif/grafadreka/hatchling
|
||||
|
||||
// Subtypes!
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/rainbow/setup_colours()
|
||||
glow_colour = get_random_colour(TRUE)
|
||||
fur_colour = get_random_colour(TRUE)
|
||||
claw_colour = get_random_colour(TRUE)
|
||||
base_colour = get_random_colour(TRUE)
|
||||
eye_colour = get_random_colour(TRUE)
|
||||
..()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/hatchling
|
||||
name = "grafadreka hatchling"
|
||||
icon = 'icons/mob/drake_baby.dmi'
|
||||
mob_size = MOB_SMALL
|
||||
desc = "An immature snow drake, not long out of the shell."
|
||||
is_baby = TRUE
|
||||
offset_compiled_icon = null
|
||||
|
||||
melee_damage_lower = 3
|
||||
melee_damage_upper = 5
|
||||
attack_armor_pen = 2
|
||||
bite_melee_damage_lower = 5
|
||||
bite_melee_damage_upper = 10
|
||||
bite_attack_armor_pen = 16
|
||||
|
||||
projectiletype = /obj/item/projectile/drake_spit/weak
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
@@ -0,0 +1,97 @@
|
||||
/obj/item/storage/internal/animal_harness/grafadreka
|
||||
abstract_type = /obj/item/storage/internal/animal_harness/grafadreka
|
||||
name = "grafadreka harness"
|
||||
self_attach_delay = 3 SECONDS
|
||||
|
||||
// keys for animal_harness/attachable_type
|
||||
var/const/ATTACHED_GPS = "gps"
|
||||
var/const/ATTACHED_RADIO = "radio"
|
||||
var/const/ATTACHED_ARMOR = "armor plate"
|
||||
var/const/ATTACHED_LIGHT = "light"
|
||||
|
||||
/// An attachable_types list shared between drake harness instances.
|
||||
var/static/list/grafadreka_attachable_types = list(
|
||||
/obj/item/gps = ATTACHED_GPS,
|
||||
/obj/item/radio = ATTACHED_RADIO,
|
||||
/obj/item/clothing/accessory/armor = list(
|
||||
ATTACHED_ARMOR,
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor
|
||||
),
|
||||
/obj/item/clothing/accessory/material/makeshift = list(
|
||||
ATTACHED_ARMOR,
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor
|
||||
),
|
||||
/obj/item/flashlight = ATTACHED_LIGHT
|
||||
)
|
||||
|
||||
/// The drake that owns this harness.
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/trained/owner
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/Destroy()
|
||||
attachable_types = null
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/Initialize(mapload)
|
||||
attachable_types = grafadreka_attachable_types
|
||||
. = ..()
|
||||
owner = loc
|
||||
if (!istype(owner))
|
||||
log_debug("Drake harness created without a drake!")
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/proc/UpdateArmor()
|
||||
if (!owner)
|
||||
return
|
||||
var/obj/item/clothing/accessory/armor = attached_items[ATTACHED_ARMOR]
|
||||
if (!armor)
|
||||
owner.armor = owner.original_armor
|
||||
return
|
||||
for (var/key in armor.armor)
|
||||
armor[key] = max(owner.original_armor[key], armor.armor[key])
|
||||
|
||||
|
||||
// Basic trained drake harness contents on spawn
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/trained/CreateAttachments()
|
||||
attached_items[ATTACHED_RADIO] = new /obj/item/radio (owner)
|
||||
new /obj/item/stack/medical/bruise_pack (src)
|
||||
new /obj/item/stack/medical/ointment (src)
|
||||
var/obj/item/storage/mre/mre_type = pick(typesof(/obj/item/storage/mre) - list(
|
||||
/obj/item/storage/mre/menu11,
|
||||
/obj/item/storage/mre/menu12,
|
||||
/obj/item/storage/mre/menu13
|
||||
))
|
||||
new mre_type (src)
|
||||
|
||||
|
||||
// Station/Science drake harness contents on spawn
|
||||
/obj/item/storage/internal/animal_harness/grafadreka/expedition/CreateAttachments()
|
||||
attached_items[ATTACHED_RADIO] = new /obj/item/radio (owner)
|
||||
new /obj/item/stack/medical/bruise_pack (src)
|
||||
new /obj/item/stack/medical/ointment (src)
|
||||
new /obj/item/storage/mre/menu13 (src) // The good stuff
|
||||
var/obj/item/gps/explorer/on/gps = new (owner)
|
||||
gps.SetTag(owner.name)
|
||||
attached_items[ATTACHED_GPS] = gps
|
||||
attached_items[ATTACHED_LIGHT] = new /obj/item/flashlight/glowstick/grafadreka (owner)
|
||||
|
||||
|
||||
|
||||
/obj/item/flashlight/glowstick/grafadreka
|
||||
name = "high duration glowstick"
|
||||
action_button_name = null
|
||||
|
||||
|
||||
/obj/item/flashlight/glowstick/grafadreka/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/flashlight/glowstick/archetype = pick(typesof(/obj/item/flashlight/glowstick) - type)
|
||||
flashlight_colour = initial(archetype.flashlight_colour)
|
||||
icon_state = initial(archetype.icon_state)
|
||||
item_state = initial(archetype.item_state)
|
||||
fuel = rand(3200, 4800)
|
||||
on = TRUE
|
||||
update_icon()
|
||||
START_PROCESSING(SSobj, src)
|
||||
@@ -0,0 +1,18 @@
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/hatchling
|
||||
name = "grafadreka hatchling"
|
||||
icon = 'icons/mob/drake_baby.dmi'
|
||||
mob_size = MOB_SMALL
|
||||
desc = "An immature snow drake, not long out of the shell."
|
||||
is_baby = TRUE
|
||||
offset_compiled_icon = null
|
||||
|
||||
melee_damage_lower = 3
|
||||
melee_damage_upper = 5
|
||||
attack_armor_pen = 2
|
||||
bite_melee_damage_lower = 5
|
||||
bite_melee_damage_upper = 10
|
||||
bite_attack_armor_pen = 16
|
||||
|
||||
projectiletype = /obj/item/projectile/drake_spit/weak
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
@@ -0,0 +1,115 @@
|
||||
/// Behaviors for being booped by drakes. If falsy, falls through to other behaviors.
|
||||
/atom/proc/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/drake)
|
||||
return
|
||||
|
||||
|
||||
/* Simple drake interactions */
|
||||
|
||||
/obj/structure/simple_door/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/drake)
|
||||
. = TRUE
|
||||
if (drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
if (!state && !isSwitchingStates)
|
||||
Open()
|
||||
|
||||
|
||||
/obj/structure/loot_pile/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/drake)
|
||||
. = TRUE
|
||||
if (drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
attack_hand(drake)
|
||||
|
||||
|
||||
/obj/item/bikehorn/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/drake)
|
||||
. = TRUE
|
||||
if (drake.a_intent != I_HELP)
|
||||
return ..()
|
||||
if (!do_after(drake, 1 SECOND, src))
|
||||
return
|
||||
drake.visible_message(
|
||||
SPAN_ITALIC("\The [drake] gnaws on \a [src]."),
|
||||
SPAN_ITALIC("You gnaw on \the [src]."),
|
||||
range = 5
|
||||
)
|
||||
attack_self(drake)
|
||||
|
||||
|
||||
/* Trained drake interactions */
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/drake)
|
||||
if (drake == src && a_intent == I_GRAB)
|
||||
DropItem()
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/trained/drake)
|
||||
if (drake.trained_drake && drake.a_intent == I_GRAB)
|
||||
drake.CollectItem(src)
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/machinery/button/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/trained/drake)
|
||||
. = TRUE
|
||||
if (!drake.trained_drake || drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
var/datum/gender/gender = gender_datums[drake.get_visible_gender()]
|
||||
drake.visible_message(
|
||||
SPAN_ITALIC("\The [drake] stands up awkwardly on [gender.his] hind legs and paws at \a [src]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [src] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(drake, 5 SECONDS, src))
|
||||
return
|
||||
to_chat(drake, SPAN_NOTICE("After some effort, you manage to push \the [src]."))
|
||||
attack_hand(drake)
|
||||
|
||||
|
||||
/obj/machinery/access_button/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/trained/drake)
|
||||
. = TRUE
|
||||
if (!drake.trained_drake || drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
var/datum/gender/gender = gender_datums[drake.get_visible_gender()]
|
||||
drake.visible_message(
|
||||
SPAN_ITALIC("\The [drake] stands up awkwardly on [gender.his] hind legs and paws at \a [src]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [src] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(drake, 5 SECONDS, src))
|
||||
return
|
||||
to_chat(drake, SPAN_NOTICE("After some effort, you manage to push \the [src]."))
|
||||
attack_hand(drake)
|
||||
|
||||
|
||||
/obj/machinery/firealarm/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/trained/drake)
|
||||
. = TRUE
|
||||
if (!drake.trained_drake || drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
var/datum/gender/gender = gender_datums[drake.get_visible_gender()]
|
||||
drake.visible_message(
|
||||
SPAN_ITALIC("\The [drake] stands up awkwardly on [gender.his] hind legs and paws at \a [src]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [src] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(drake, 5 SECONDS, src))
|
||||
return
|
||||
to_chat(drake, SPAN_NOTICE("After some effort, you manage to push \the [src]."))
|
||||
attack_hand(src)
|
||||
|
||||
|
||||
/obj/machinery/conveyor_switch/interaction_grafadreka(mob/living/simple_mob/animal/sif/grafadreka/trained/drake)
|
||||
. = TRUE
|
||||
if (!drake.trained_drake || drake.a_intent == I_HURT)
|
||||
return ..()
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [drake] pushes bodily against \a [src]."),
|
||||
SPAN_ITALIC("You press your shoulder into \the [src], trying to change its direction."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(drake, 2 SECONDS, src))
|
||||
return
|
||||
to_chat(drake, SPAN_NOTICE("After some effort, you manage to push \the [src]."))
|
||||
attack_hand(drake)
|
||||
@@ -0,0 +1,118 @@
|
||||
/datum/modifier/sifsap_salve
|
||||
name = "Sifsap Salve"
|
||||
desc = "Your wounds have been salved with Sivian sap."
|
||||
mob_overlay_state = "cyan_sparkles"
|
||||
stacks = MODIFIER_STACK_FORBID
|
||||
on_created_text = "<span class = 'notice'>The glowing sap seethes and bubbles in your wounds, tingling and stinging.</span>"
|
||||
on_expired_text = "<span class = 'notice'>The last of the sap in your wounds fizzles away.</span>"
|
||||
|
||||
|
||||
/datum/modifier/sifsap_salve/tick()
|
||||
if (holder.stat == DEAD || holder.isSynthetic())
|
||||
expire()
|
||||
if (istype(holder, /mob/living/simple_mob/animal/sif))
|
||||
var/mob/living/simple_mob/animal/sif/critter = holder
|
||||
if (critter.health >= (critter.getMaxHealth() * critter.sap_heal_threshold))
|
||||
return
|
||||
if (holder.resting)
|
||||
if (istype(holder.loc, /obj/structure/animal_den))
|
||||
holder.adjustBruteLoss(-3)
|
||||
holder.adjustFireLoss(-3)
|
||||
holder.adjustToxLoss(-2)
|
||||
else
|
||||
holder.adjustBruteLoss(-2)
|
||||
holder.adjustFireLoss(-2)
|
||||
holder.adjustToxLoss(-1)
|
||||
else
|
||||
holder.adjustBruteLoss(-1)
|
||||
holder.adjustFireLoss(-1)
|
||||
|
||||
|
||||
/datum/category_item/catalogue/fauna/grafadreka
|
||||
name = "Sivian Fauna - Grafadreka"
|
||||
desc = {"Classification: S tesca pabulator
|
||||
<br><br>
|
||||
The reclusive grafadreka (Icelandic, lit. 'digging dragon'), also known as the snow drake, is a large reptillian pack predator similar in size and morphology to old Earth hyenas. They commonly dig shallow dens in dirt, snow or foliage, sometimes using them for concealment prior to an ambush. Biological cousins to the elusive kururak, they have heavy, low-slung bodies and powerful jaws suited to hunting land prey rather than fishing. Colonization and subsequent expansion have displaced many populations from their tundral territories into colder areas; as a result, their diet of Sivian prey animals has pivoted to a diet of giant spider meat.
|
||||
<br><br>
|
||||
Grafadrekas are capable of exerting bite pressures in excess of 900 PSI, which allows them to crack bones or carapace when scavenging for food. While they share the hypercarnivorous metabolism of their cousins, they have developed a symbiotic relationship with the bacteria responsible for the bioluminescence of Sivian trees. This assists with digesting plant matter, and gives their pelts a distinctive and eerie glow.
|
||||
<br><br>
|
||||
They have been observed to occasionally attack and kill colonists, generally when conditions are too poor to hunt their usual prey. Despite this, and despite their disposition being generally skittish and avoidant of colonists, some Sivian communities hold that they have been observed to guide or protect lost travellers.
|
||||
<br><br>
|
||||
Field studies suggest analytical abilities on par with some species of cepholapods, but their symbiotic physiology rapidly fails in captivity, making laboratory testing difficult. Their inability to make use of tools or form wider social groups beyond a handful of individuals has been hypothesised to prevent the expression of more complex social behaviors."}
|
||||
value = CATALOGUER_REWARD_HARD
|
||||
|
||||
|
||||
/datum/say_list/grafadreka
|
||||
speak = list("Chff!", "Skhh.", "Rrrss...")
|
||||
emote_see = list("scratches its ears","grooms its spines", "sways its tail", "claws at the ground")
|
||||
emote_hear = list("hisses", "rattles", "rasps", "barks")
|
||||
|
||||
|
||||
/decl/mob_organ_names/grafadreka
|
||||
hit_zones = list(
|
||||
"head",
|
||||
"chest",
|
||||
"left foreleg",
|
||||
"right foreleg",
|
||||
"left hind leg",
|
||||
"right hind leg",
|
||||
"face spines",
|
||||
"body spines",
|
||||
"tail spines",
|
||||
"tail"
|
||||
)
|
||||
|
||||
|
||||
/decl/emote/audible/drake_howl
|
||||
key = "dhowl"
|
||||
emote_message_3p = "lifts USER_THEIR head up and gives an eerie howl."
|
||||
emote_sound = 'sound/effects/drakehowl_close.ogg'
|
||||
broadcast_sound ='sound/effects/drakehowl_far.ogg'
|
||||
emote_cooldown = 20 SECONDS
|
||||
broadcast_distance = 90
|
||||
|
||||
|
||||
/decl/emote/audible/drake_howl/broadcast_emote_to(send_sound, mob/target, direction)
|
||||
. = ..()
|
||||
if (.)
|
||||
to_chat(target, SPAN_NOTICE("You hear an eerie howl from somewhere to the [dir2text(direction)]."))
|
||||
|
||||
|
||||
/obj/item/projectile/drake_spit
|
||||
name = "drake spit"
|
||||
icon_state = "ice_1"
|
||||
damage = 0
|
||||
embed_chance = 0
|
||||
damage_type = BRUTE
|
||||
muzzle_type = null
|
||||
hud_state = "monkey"
|
||||
combustion = FALSE
|
||||
stun = 3
|
||||
weaken = 3
|
||||
eyeblur = 5
|
||||
fire_sound = 'sound/effects/splat.ogg'
|
||||
|
||||
|
||||
/obj/item/projectile/drake_spit/weak
|
||||
stun = 0
|
||||
weaken = 0
|
||||
eyeblur = 2
|
||||
|
||||
|
||||
/obj/structure/animal_den/ghost_join/grafadreka
|
||||
name = "drake den"
|
||||
critter = /mob/living/simple_mob/animal/sif/grafadreka
|
||||
|
||||
|
||||
/obj/structure/animal_den/ghost_join/grafadreka_hatchling
|
||||
name = "drake hatchling den"
|
||||
critter = /mob/living/simple_mob/animal/sif/grafadreka/hatchling
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/rainbow/setup_colours()
|
||||
glow_colour = get_random_colour(TRUE)
|
||||
fur_colour = get_random_colour(TRUE)
|
||||
claw_colour = get_random_colour(TRUE)
|
||||
base_colour = get_random_colour(TRUE)
|
||||
eye_colour = get_random_colour(TRUE)
|
||||
..()
|
||||
@@ -0,0 +1,136 @@
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained
|
||||
desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body. This one is wearing some kind of harness; maybe it belongs to someone."
|
||||
player_msg = "You are a large Sivian pack predator in symbiosis with the local bioluminescent bacteria. You can eat glowing \
|
||||
tree fruit to fuel your <b>ranged spitting attack</b> and <b>poisonous bite</b> (on <span class = 'danger'>harm intent</span>), as well as <b>healing saliva</b> \
|
||||
(on <b><font color = '#009900'>help intent</font></b>).<br>Using <font color='#e0a000'>grab intent</font> you can pick up and drop items on by clicking them or yourself, \
|
||||
and can interact with some simple machines like buttons and levers.<br>Unlike your wild kin, you are <b>trained</b> and work happily with your two-legged packmates."
|
||||
faction = "station"
|
||||
ai_holder_type = null // These guys should not exist without players.
|
||||
gender = PLURAL // Will take gender from prefs = set to non-NEUTER here to avoid randomizing in Initialize().
|
||||
movement_cooldown = 1.5 // ~Red~ trained ones go faster.
|
||||
dexterity = MOB_DEXTERITY_SIMPLE_MACHINES
|
||||
harness = /obj/item/storage/internal/animal_harness/grafadreka/trained
|
||||
trained_drake = TRUE
|
||||
|
||||
/// On clicking with an item, stuff that should use behaviors instead of being placed in storage.
|
||||
var/static/list/allow_type_to_pass = list(
|
||||
/obj/item/healthanalyzer,
|
||||
/obj/item/stack/medical,
|
||||
/obj/item/reagent_containers/syringe
|
||||
)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Destroy()
|
||||
if (istype(harness))
|
||||
QDEL_NULL(harness)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/add_glow()
|
||||
. = ..()
|
||||
if (. && harness)
|
||||
var/image/I = .
|
||||
I.icon_state = "[I.icon_state]-pannier"
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Logout()
|
||||
..()
|
||||
if (stat != DEAD)
|
||||
lying = TRUE
|
||||
resting = TRUE
|
||||
sitting = FALSE
|
||||
Sleeping(2)
|
||||
update_icon()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Login()
|
||||
..()
|
||||
SetSleeping(0)
|
||||
update_icon()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/attack_hand(mob/living/user)
|
||||
// Permit headpats/smacks
|
||||
if (!harness || user.a_intent == I_HURT || (user.a_intent == I_HELP && user.zone_sel?.selecting == BP_HEAD))
|
||||
return ..()
|
||||
return harness.handle_attack_hand(user)
|
||||
|
||||
|
||||
// universal_understand is buggy and produces double lines, so we'll just do this hack instead.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/say_understands(mob/other, datum/language/speaking)
|
||||
if (!speaking || speaking.name == LANGUAGE_GALCOM)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/update_icon()
|
||||
. = ..()
|
||||
if (harness)
|
||||
var/image/I = image(icon, "[current_icon_state]-pannier")
|
||||
I.color = harness.color
|
||||
I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
|
||||
if (offset_compiled_icon)
|
||||
I.pixel_x = offset_compiled_icon
|
||||
add_overlay(I)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/attackby(obj/item/item, mob/user)
|
||||
if (user.a_intent == I_HURT)
|
||||
return ..()
|
||||
if (user.a_intent == I_HELP)
|
||||
for (var/pass_type in allow_type_to_pass)
|
||||
if (istype(item, pass_type))
|
||||
return ..()
|
||||
if (harness?.attackby(item, user))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/DropItem()
|
||||
if (!length(harness?.contents))
|
||||
to_chat(src, SPAN_WARNING("You have nothing to drop."))
|
||||
return
|
||||
var/obj/item/response = input(src, "Select an item to drop:") as null | anything in harness.contents
|
||||
if (!response)
|
||||
return
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] begins rooting around in the pouch on [gender.his] harness."),
|
||||
SPAN_ITALIC("You begin working \the [response] out of your harness pouch."),
|
||||
SPAN_ITALIC("You hear something rustling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 3 SECONDS, ignore_movement = TRUE))
|
||||
return
|
||||
harness.remove_from_storage(response, loc)
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] pulls \a [response] from [gender.his] harness and drops it."),
|
||||
SPAN_NOTICE("You pull \the [response] from your harness and drop it."),
|
||||
SPAN_WARNING("Clank!"),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/CollectItem(obj/item/item)
|
||||
if (!item.simulated || item.abstract || !item.Adjacent(src))
|
||||
return
|
||||
if (!harness)
|
||||
to_chat(src, SPAN_WARNING("Your harness is missing; you cannot store \the [item]."))
|
||||
return
|
||||
if (item.anchored)
|
||||
to_chat(src, SPAN_WARNING("\The [item] is securely anchored; you can't take it."))
|
||||
return
|
||||
face_atom(item)
|
||||
if (!do_after(src, 3 SECONDS, item))
|
||||
return
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
if (harness?.attackby(item, src, TRUE))
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] grabs \a [item] in [gender.his] teeth and noses it into [gender.his] harness pouch."),
|
||||
SPAN_NOTICE("You grab \the [item] in your teeth and push it into your harness pouch."),
|
||||
SPAN_ITALIC("You hear something rustling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
return
|
||||
to_chat(src, SPAN_WARNING("There's not enough space in your harness pouch for \the [item] to fit!"))
|
||||
@@ -1,133 +0,0 @@
|
||||
// It's just a backpack.
|
||||
/obj/item/storage/internal/animal_harness
|
||||
color = COLOR_BEASTY_BROWN
|
||||
max_w_class = ITEMSIZE_LARGE
|
||||
max_storage_space = INVENTORY_STANDARD_SPACE
|
||||
var/obj/item/gps/attached_gps
|
||||
var/obj/item/clothing/accessory/attached_plate
|
||||
var/obj/item/radio/attached_radio
|
||||
|
||||
/obj/item/storage/internal/animal_harness/Initialize()
|
||||
|
||||
// Spawn some useful items. We can't use them, but anyone we find can.
|
||||
new /obj/item/stack/medical/bruise_pack(src)
|
||||
new /obj/item/stack/medical/ointment(src)
|
||||
var/ration_type = pick(list(
|
||||
/obj/item/storage/mre,
|
||||
/obj/item/storage/mre/menu2,
|
||||
/obj/item/storage/mre/menu3,
|
||||
/obj/item/storage/mre/menu4,
|
||||
/obj/item/storage/mre/menu5,
|
||||
/obj/item/storage/mre/menu6,
|
||||
/obj/item/storage/mre/menu7,
|
||||
/obj/item/storage/mre/menu8,
|
||||
/obj/item/storage/mre/menu9,
|
||||
/obj/item/storage/mre/menu10
|
||||
))
|
||||
new ration_type(src)
|
||||
|
||||
. = ..() // Name is set lower in Initialize() so we set it again here.
|
||||
name = "animal harness"
|
||||
|
||||
/obj/item/storage/internal/animal_harness/Destroy()
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/trained/drake = loc
|
||||
if(istype(drake) && drake.harness == src)
|
||||
drake.harness = null
|
||||
drake.armor = drake.original_armor
|
||||
QDEL_NULL(attached_radio)
|
||||
QDEL_NULL(attached_gps)
|
||||
QDEL_NULL(attached_plate)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/storage/internal/animal_harness/attackby(obj/item/W, mob/user, silent)
|
||||
if (user == loc) // Drakes can't attach stuff to themselves, only shove it in their storage.
|
||||
return ..()
|
||||
if (!istype(loc, /mob/living/simple_mob/animal/sif/grafadreka/trained)) // Only allow attaching behaviors when worn by an appropriate subject.
|
||||
return ..()
|
||||
if (istype(W, /obj/item/gps)) // Attach a tracker.
|
||||
if (attached_gps)
|
||||
if (!silent)
|
||||
to_chat(user, SPAN_WARNING("There is already \a [attached_gps] attached to \the [loc]'s harness."))
|
||||
else if (user.unEquip(W))
|
||||
W.forceMove(loc)
|
||||
if (!silent)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] attaches \the [W] to \the [loc]'s harness."))
|
||||
attached_gps = W
|
||||
return TRUE
|
||||
if (istype(W, /obj/item/radio)) // Attach a radio.
|
||||
if (attached_radio)
|
||||
if (!silent)
|
||||
to_chat(user, SPAN_WARNING("There is already \a [attached_radio] attached to \the [loc]'s harness."))
|
||||
else if (user.unEquip(W))
|
||||
W.forceMove(loc)
|
||||
if (!silent)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] attaches \the [W] to \the [loc]'s harness."))
|
||||
attached_radio = W
|
||||
return TRUE
|
||||
if (istype(W, /obj/item/clothing/accessory/armor) || istype(W, /obj/item/clothing/accessory/material/makeshift)) // Attach an armor plate.
|
||||
if (attached_plate)
|
||||
if (!silent)
|
||||
to_chat(user, SPAN_WARNING("There is already \a [attached_plate] inside \the [loc]'s harness."))
|
||||
else if (user.unEquip(W))
|
||||
W.forceMove(loc)
|
||||
if (!silent)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] secures \the [W] inside \the [loc]'s harness."))
|
||||
attached_plate = W
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/trained/drake = loc
|
||||
drake.recalculate_armor()
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/recalculate_armor()
|
||||
armor = list()
|
||||
if(istype(harness) && harness.attached_plate)
|
||||
for(var/armor_key in harness.attached_plate.armor)
|
||||
armor[armor_key] = max(original_armor[armor_key], harness.attached_plate.armor[armor_key])
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_plate()
|
||||
set name = "Remove Attached Plate"
|
||||
set category = "IC"
|
||||
set src in view(1)
|
||||
if(istype(harness) && harness.attached_plate)
|
||||
harness.attached_plate.dropInto(get_turf(src))
|
||||
if(usr == src)
|
||||
var/datum/gender/G = gender_datums[get_visible_gender()]
|
||||
visible_message("\The [src] awkwardly pulls \the [harness.attached_plate] out of [G.his] harness and drops it.")
|
||||
else
|
||||
visible_message("\The [usr] removes \the [harness.attached_plate] from \the [src]'s harness.")
|
||||
usr.put_in_hands(harness.attached_plate)
|
||||
harness.attached_plate = null
|
||||
armor = original_armor
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_plate
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_gps()
|
||||
set name = "Remove Attached GPS"
|
||||
set category = "IC"
|
||||
set src in view(1)
|
||||
if(istype(harness) && harness.attached_gps)
|
||||
harness.attached_gps.dropInto(get_turf(src))
|
||||
if(usr == src)
|
||||
var/datum/gender/G = gender_datums[get_visible_gender()]
|
||||
visible_message("\The [src] awkwardly pulls \the [harness.attached_gps] off [G.his] harness and drops it.")
|
||||
else
|
||||
visible_message("\The [usr] detaches \the [harness.attached_gps] from \the [src]'s harness.")
|
||||
usr.put_in_hands(harness.attached_gps)
|
||||
harness.attached_gps = null
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_gps
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_radio()
|
||||
set name = "Remove Attached Radio"
|
||||
set category = "IC"
|
||||
set src in view(1)
|
||||
if(istype(harness) && harness.attached_radio)
|
||||
harness.attached_radio.dropInto(get_turf(src))
|
||||
if(usr == src)
|
||||
var/datum/gender/G = gender_datums[get_visible_gender()]
|
||||
visible_message("\The [src] awkwardly pulls \the [harness.attached_radio] off [G.his] harness and drops it.")
|
||||
else
|
||||
visible_message("\The [usr] detaches \the [harness.attached_radio] from \the [src]'s harness.")
|
||||
usr.put_in_hands(harness.attached_radio)
|
||||
harness.attached_radio = null
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_radio
|
||||
@@ -1,298 +0,0 @@
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained
|
||||
desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body. This one is wearing some kind of harness; maybe it belongs to someone."
|
||||
player_msg = "You are a large Sivian pack predator in symbiosis with the local bioluminescent bacteria. You can eat glowing \
|
||||
tree fruit to fuel your <b>ranged spitting attack</b> and <b>poisonous bite</b> (on <span class = 'danger'>harm intent</span>), as well as <b>healing saliva</b> \
|
||||
(on <b><font color = '#009900'>help intent</font></b>).<br>Using <font color='#e0a000'>grab intent</font> you can pick up and drop items on by clicking them or yourself, \
|
||||
and can interact with some simple machines like buttons and levers.<br>Unlike your wild kin, you are <b>trained</b> and work happily with your two-legged packmates."
|
||||
faction = "station"
|
||||
ai_holder_type = null // These guys should not exist without players.
|
||||
gender = PLURAL // Will take gender from prefs = set to non-NEUTER here to avoid randomizing in Initialize().
|
||||
movement_cooldown = 1.5 // ~Red~ trained ones go faster.
|
||||
dexterity = MOB_DEXTERITY_SIMPLE_MACHINES
|
||||
|
||||
/// If a valid harness path, the harness this drake will be wearing on Initialize.
|
||||
var/obj/item/storage/internal/animal_harness/harness = /obj/item/storage/internal/animal_harness
|
||||
|
||||
/// On clicking with an item, stuff that should use behaviors instead of being placed in storage.
|
||||
var/static/list/allow_type_to_pass = list(
|
||||
/obj/item/healthanalyzer,
|
||||
/obj/item/stack/medical,
|
||||
/obj/item/reagent_containers/syringe
|
||||
)
|
||||
|
||||
/// A type path -> proc path mapping for objects that drakes can use.
|
||||
var/static/list/interactable_objects = list(
|
||||
/obj/machinery/button = /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractButtonBasic,
|
||||
/obj/machinery/access_button = /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractButtonAccess,
|
||||
/obj/machinery/firealarm = /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractFireAlarm,
|
||||
/obj/machinery/conveyor_switch = /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractConveyorSwitch
|
||||
)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Destroy()
|
||||
if (istype(harness))
|
||||
QDEL_NULL(harness)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Initialize()
|
||||
if (ispath(harness, /obj/item/storage/internal/animal_harness))
|
||||
harness = new harness(src)
|
||||
harness.attached_radio = new /obj/item/radio(src)
|
||||
regenerate_harness_verbs()
|
||||
else
|
||||
if (harness)
|
||||
log_error("[type] initialized with an invalid harness [harness]")
|
||||
harness = null
|
||||
// Do this after creating the harness so the update icon proc doesn't runtime.
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/examine(mob/living/user)
|
||||
. = ..()
|
||||
if (istype(harness))
|
||||
. += "\The [src] is wearing \a [harness]."
|
||||
for (var/obj/item/thing in list(harness.attached_gps, harness.attached_plate, harness.attached_radio))
|
||||
. += "There is \a [thing] attached."
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/add_glow()
|
||||
. = ..()
|
||||
if (. && harness)
|
||||
var/image/I = .
|
||||
I.icon_state = "[I.icon_state]-pannier"
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Logout()
|
||||
..()
|
||||
if (stat != DEAD)
|
||||
lying = TRUE
|
||||
resting = TRUE
|
||||
sitting = FALSE
|
||||
Sleeping(2)
|
||||
update_icon()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/Login()
|
||||
..()
|
||||
SetSleeping(0)
|
||||
update_icon()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/attack_hand(mob/living/user)
|
||||
// Permit headpats/smacks
|
||||
if (!harness || user.a_intent == I_HURT || (user.a_intent == I_HELP && user.zone_sel?.selecting == BP_HEAD))
|
||||
return ..()
|
||||
return harness.handle_attack_hand(user)
|
||||
|
||||
|
||||
// universal_understand is buggy and produces double lines, so we'll just do this hack instead.
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/say_understands(mob/other, datum/language/speaking)
|
||||
if (!speaking || speaking.name == LANGUAGE_GALCOM)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/update_icon()
|
||||
. = ..()
|
||||
if (istype(harness))
|
||||
var/image/I = image(icon, "[current_icon_state]-pannier")
|
||||
I.color = harness.color
|
||||
I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
|
||||
if (offset_compiled_icon)
|
||||
I.pixel_x = offset_compiled_icon
|
||||
add_overlay(I)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/attackby(obj/item/item, mob/user)
|
||||
// bonk
|
||||
if (user.a_intent == I_HURT)
|
||||
return ..()
|
||||
if (user.a_intent == I_HELP)
|
||||
for (var/pass_type in allow_type_to_pass)
|
||||
if (istype(item, pass_type))
|
||||
return ..()
|
||||
// Open our storage, if we have it.
|
||||
if (istype(harness) && harness.attackby(item, user))
|
||||
regenerate_harness_verbs()
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/regenerate_harness_verbs()
|
||||
if (!istype(harness))
|
||||
verbs -= list(
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_gps,
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_plate,
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_radio
|
||||
)
|
||||
return
|
||||
if (harness.attached_gps)
|
||||
verbs |= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_gps
|
||||
else
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_gps
|
||||
if (harness.attached_plate)
|
||||
verbs |= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_plate
|
||||
else
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_plate
|
||||
if (harness.attached_radio)
|
||||
verbs |= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_radio
|
||||
else
|
||||
verbs -= /mob/living/simple_mob/animal/sif/grafadreka/trained/proc/remove_attached_radio
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/attack_target(atom/atom)
|
||||
if (a_intent == I_GRAB)
|
||||
if (atom == src)
|
||||
return DropItem()
|
||||
if (isobj(atom))
|
||||
var/interact_outcome = AttemptInteract(atom)
|
||||
if (!isnull(interact_outcome))
|
||||
return interact_outcome
|
||||
if (isitem(atom))
|
||||
return CollectItem(atom)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/DropItem()
|
||||
if(!istype(harness) || !length(harness.contents))
|
||||
to_chat(src, SPAN_WARNING("You have nothing to drop."))
|
||||
return ATTACK_FAILED
|
||||
var/list/attached = list(harness.attached_gps, harness.attached_radio, harness.attached_plate)
|
||||
var/obj/item/response = input(src, "Select an item to drop:") as null | anything in attached + harness.contents
|
||||
if (!response)
|
||||
return ATTACK_FAILED
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
var/is_special = (response in attached)
|
||||
if (is_special)
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] begins tugging at \the [response] on [gender.his] harness."),
|
||||
SPAN_ITALIC("You begin tugging \the [response] off your harness."),
|
||||
SPAN_ITALIC("You hear something rustling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
else
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] begins rooting around in the pouch on [gender.his] harness."),
|
||||
SPAN_ITALIC("You begin working \the [response] out of your harness pouch."),
|
||||
SPAN_ITALIC("You hear something rustling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 5 SECONDS, ignore_movement = TRUE))
|
||||
return ATTACK_FAILED
|
||||
if (is_special)
|
||||
if (!(response in src))
|
||||
to_chat(src, SPAN_WARNING("\The [response] is already gone!"))
|
||||
return ATTACK_FAILED
|
||||
if (response == harness.attached_gps)
|
||||
harness.attached_gps = null
|
||||
else if (response == harness.attached_radio)
|
||||
harness.attached_radio = null
|
||||
else if (response == harness.attached_plate)
|
||||
harness.attached_plate = null
|
||||
response.dropInto(loc)
|
||||
regenerate_harness_verbs()
|
||||
else
|
||||
harness.remove_from_storage(response, loc)
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] pulls \a [response] from [gender.his] harness and drops it."),
|
||||
SPAN_NOTICE("You pull \the [response] from your harness and drop it."),
|
||||
SPAN_WARNING("Clank!"),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
return ATTACK_SUCCESSFUL
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/CollectItem(obj/item/item)
|
||||
if (!item.simulated || item.abstract || !item.Adjacent(src))
|
||||
return ATTACK_FAILED
|
||||
if (!istype(harness))
|
||||
to_chat(src, SPAN_WARNING("Your harness is missing; you cannot store \the [item]."))
|
||||
return ATTACK_FAILED
|
||||
if (item.anchored)
|
||||
to_chat(src, SPAN_WARNING("\The [item] is securely anchored; you can't take it."))
|
||||
return ATTACK_FAILED
|
||||
face_atom(item)
|
||||
if (!do_after(src, 5 SECONDS, item))
|
||||
return ATTACK_FAILED
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
if (istype(harness) && harness.attackby(item, src, TRUE))
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] grabs \a [item] in [gender.his] teeth and noses it into [gender.his] harness pouch."),
|
||||
SPAN_NOTICE("You grab \the [item] in your teeth and push it into your harness pouch."),
|
||||
SPAN_ITALIC("You hear something rustling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
return ATTACK_SUCCESSFUL
|
||||
to_chat(src, SPAN_WARNING("There's not enough space in your harness pouch for \the [item] to fit!"))
|
||||
return ATTACK_FAILED
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/AttemptInteract(obj/obj)
|
||||
if (!obj.Adjacent(src))
|
||||
return
|
||||
var/handler
|
||||
for (var/type in interactable_objects)
|
||||
if (ispath(obj.type, type))
|
||||
handler = interactable_objects[type]
|
||||
break
|
||||
if (!handler)
|
||||
return
|
||||
return call(src, handler)(obj)
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractButtonBasic(obj/machinery/button/button)
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] stands up awkwardly on [gender.his] hind legs and paws at \a [button]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [button] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 5 SECONDS, button))
|
||||
return ATTACK_FAILED
|
||||
to_chat(src, SPAN_NOTICE("After some effort, you manage to push \the [button]."))
|
||||
button.attack_hand(src)
|
||||
return ATTACK_SUCCESSFUL
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractButtonAccess(obj/machinery/access_button/button)
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] stands up awkwardly on [gender.his] hind legs and paws at \a [button]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [button] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 5 SECONDS, button))
|
||||
return ATTACK_FAILED
|
||||
to_chat(src, SPAN_NOTICE("After some effort, you manage to push \the [button]."))
|
||||
button.attack_hand(src)
|
||||
return ATTACK_SUCCESSFUL
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractFireAlarm(obj/machinery/firealarm/alarm)
|
||||
var/datum/gender/gender = gender_datums[get_visible_gender()]
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] stands up awkwardly on [gender.his] hind legs and paws at \a [alarm]."),
|
||||
SPAN_ITALIC("You rear up, attempting to push \the [alarm] with your foreclaws."),
|
||||
SPAN_WARNING("You hear something scratching and scrabbling."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 5 SECONDS, alarm))
|
||||
return ATTACK_FAILED
|
||||
to_chat(src, SPAN_NOTICE("After some effort, you manage to push \the [alarm]."))
|
||||
alarm.attack_hand(src)
|
||||
return ATTACK_SUCCESSFUL
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/trained/proc/InteractConveyorSwitch(obj/machinery/conveyor_switch/lever)
|
||||
visible_message(
|
||||
SPAN_ITALIC("\The [src] pushes bodily against \a [lever]."),
|
||||
SPAN_ITALIC("You press your shoulder into \the [lever], trying to change its direction."),
|
||||
runemessage = CHAT_MESSAGE_DEFAULT_ACTION
|
||||
)
|
||||
if (!do_after(src, 2 SECONDS, lever))
|
||||
return ATTACK_FAILED
|
||||
to_chat(src, SPAN_NOTICE("After some effort, you manage to push \the [lever]."))
|
||||
lever.attack_hand(src)
|
||||
return ATTACK_SUCCESSFUL
|
||||
Reference in New Issue
Block a user