mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-16 13:12:22 +00:00
Added grafadreka.
More work on drakes. Added grafadreka den site. Working on refining drake AI. Fixing drake languages.
This commit is contained in:
@@ -142,4 +142,3 @@
|
||||
adjust_instability(controlled_mobs.len)
|
||||
to_chat(user, "<span class='notice'>You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to move \
|
||||
towards \the [T].</span>")
|
||||
|
||||
|
||||
@@ -916,3 +916,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
|
||||
// this gets called when the item gets chucked by the vending machine
|
||||
/obj/item/proc/vendor_action(var/obj/machinery/vending/V)
|
||||
return
|
||||
|
||||
// Called when a simple animal finishes eating the item.
|
||||
/obj/item/proc/animal_consumed(var/mob/user)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] finishes eating \the [src]."), SPAN_NOTICE("You finish eating \the [src]."))
|
||||
qdel(src)
|
||||
|
||||
@@ -123,6 +123,15 @@
|
||||
return pick(prob(1);/mob/living/simple_mob/animal/sif/kururak/hibernate,
|
||||
prob(20);/mob/living/simple_mob/animal/sif/kururak)
|
||||
|
||||
/obj/random/mob/sif/grafadreka
|
||||
name = "Random Grafadreka"
|
||||
desc = "This is a random grafadreka, either waking or hibernating."
|
||||
icon_state = "frost"
|
||||
|
||||
/obj/random/mob/sif/grafadreka/item_to_spawn()
|
||||
return pick(prob(3);/mob/living/simple_mob/animal/sif/grafadreka/wild/hibernate,
|
||||
prob(12);/mob/living/simple_mob/animal/sif/grafadreka/wild)
|
||||
|
||||
/obj/random/mob/spider
|
||||
name = "Random Spider" //Spiders should patrol where they spawn.
|
||||
desc = "This is a random boring spider."
|
||||
@@ -397,6 +406,11 @@
|
||||
/mob/living/simple_mob/animal/sif/kururak,
|
||||
/mob/living/simple_mob/animal/sif/kururak
|
||||
),
|
||||
prob(3);list(
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild,
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild,
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild
|
||||
),
|
||||
prob(5);list(
|
||||
/mob/living/simple_mob/animal/sif/glitterfly,
|
||||
/mob/living/simple_mob/animal/sif/glitterfly,
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
return TRUE
|
||||
if(try_grab_occupant(user))
|
||||
return TRUE
|
||||
if(!do_after(user, src, 1 SECOND))
|
||||
if(!do_after(user, 1 SECOND, src))
|
||||
return TRUE
|
||||
if(length(contents))
|
||||
to_chat(user, SPAN_WARNING("You can't hide in \the [src]; it's occupied."))
|
||||
@@ -104,7 +104,6 @@
|
||||
/obj/structure/animal_den/ghost_join
|
||||
var/mob/living/critter
|
||||
var/ban_check = "Critter"
|
||||
var/welcome_text
|
||||
|
||||
/obj/structure/animal_den/ghost_join/Initialize()
|
||||
. = ..()
|
||||
@@ -120,40 +119,39 @@
|
||||
critter = null
|
||||
|
||||
/obj/structure/animal_den/ghost_join/examine(mob/user, infix, suffix)
|
||||
. = ..()
|
||||
var/list/output = ..()
|
||||
if(isobserver(user))
|
||||
if(critter)
|
||||
if(ban_check && ckey_is_jobbanned(user.ckey, ban_check))
|
||||
to_chat(user, SPAN_WARNING("You are banned from [ban_check] roles and cannot join via this den."))
|
||||
output += SPAN_WARNING("You are banned from [ban_check] roles and cannot join via this den.")
|
||||
else if(user.MayRespawn(TRUE))
|
||||
to_chat(user, SPAN_NOTICE("<b>Click on the den to join as \a [critter].</b>"))
|
||||
output += SPAN_NOTICE("<b>Click on the den to join as \a [critter].</b>")
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("This den is no longer available for joining."))
|
||||
output += SPAN_WARNING("This den is no longer available for joining.")
|
||||
return output
|
||||
|
||||
/obj/structure/animal_den/ghost_join/attack_ghost(mob/user)
|
||||
if(critter)
|
||||
transfer_personality(user)
|
||||
return
|
||||
if(!critter)
|
||||
return ..()
|
||||
if(ban_check && ckey_is_jobbanned(user.ckey, ban_check))
|
||||
to_chat(user, SPAN_WARNING("You are banned from [ban_check] roles and cannot join via this den."))
|
||||
return
|
||||
transfer_personality(user)
|
||||
|
||||
/obj/structure/animal_den/ghost_join/proc/transfer_personality(var/mob/user)
|
||||
|
||||
set waitfor = FALSE
|
||||
if(!critter)
|
||||
return
|
||||
|
||||
// Transfer over mind and key.
|
||||
if(user.mind)
|
||||
user.mind.transfer_to(critter)
|
||||
else
|
||||
critter.key = user.key
|
||||
|
||||
var/mob/living/critter_ref = critter
|
||||
critter = null
|
||||
|
||||
// Sleep long enough for them to login and get any other text out of the way.
|
||||
sleep(1)
|
||||
to_chat(critter_ref, SPAN_NOTICE("<b>You are \a [critter]!</b>"))
|
||||
if(welcome_text)
|
||||
to_chat(critter_ref, SPAN_NOTICE(welcome_text))
|
||||
|
||||
// Sleep long enough for the logged-in critter to update state and regen icon.
|
||||
sleep(SSmobs.wait)
|
||||
if(critter_ref.resting) // get up lazybones
|
||||
|
||||
@@ -97,9 +97,13 @@
|
||||
if(!can_forage(critter))
|
||||
to_chat(critter, SPAN_WARNING("You cannot see any edible fruit on \the [src]."))
|
||||
return TRUE
|
||||
critter.set_AI_busy(TRUE)
|
||||
show_animal_foraging_message(critter)
|
||||
if(!do_after(critter, 2 SECONDS, src) || QDELETED(src) || !can_forage(user))
|
||||
critter.setClickCooldown(5 SECONDS)
|
||||
if(!do_after(critter, 5 SECONDS, src) || QDELETED(src) || !can_forage(user))
|
||||
critter.set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
critter.set_AI_busy(FALSE)
|
||||
show_animal_eating_message(critter)
|
||||
playsound(critter, 'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
harvest_count++
|
||||
|
||||
@@ -47,6 +47,7 @@ var/global/list/turf_edge_cache = list()
|
||||
to_chat(critter, SPAN_WARNING("There is already a den here."))
|
||||
return TRUE
|
||||
critter.visible_message(SPAN_NOTICE("\The [critter] begins digging industriously."))
|
||||
critter.setClickCooldown(10 SECONDS)
|
||||
if(!do_after(critter, 10 SECONDS, src))
|
||||
return TRUE
|
||||
if(locate(/obj/structure/animal_den) in contents)
|
||||
|
||||
@@ -23,4 +23,5 @@ var/global/list/_default_mob_emotes = list(
|
||||
/decl/emote/audible/choke,
|
||||
/decl/emote/audible/moan,
|
||||
/decl/emote/audible/gnarl,
|
||||
/decl/emote/audible/snarl
|
||||
)
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
emote_message_3p = "chokes."
|
||||
conscious = FALSE
|
||||
|
||||
/decl/emote/audible/snarl
|
||||
key = "snarl"
|
||||
emote_message_3p = "snarls menacingly."
|
||||
|
||||
/decl/emote/audible/gnarl
|
||||
key = "gnarl"
|
||||
emote_message_3p = "gnarls and shows USER_THEIR teeth."
|
||||
|
||||
@@ -83,7 +83,7 @@ var/global/list/emotes_by_key
|
||||
var/mob/M = user
|
||||
if(M.restrained())
|
||||
to_chat(user, SPAN_WARNING("You are restrained and cannot do that."))
|
||||
return
|
||||
return FALSE
|
||||
|
||||
var/atom/target
|
||||
if(can_target() && extra_params)
|
||||
@@ -104,7 +104,7 @@ var/global/list/emotes_by_key
|
||||
|
||||
if(!target)
|
||||
to_chat(user, SPAN_WARNING("You cannot see a '[extra_params]' within range."))
|
||||
return
|
||||
return FALSE
|
||||
|
||||
var/use_1p = get_emote_message_1p(user, target, extra_params)
|
||||
if(use_1p)
|
||||
@@ -136,7 +136,7 @@ var/global/list/emotes_by_key
|
||||
var/mob/living/L = user
|
||||
if(L.silent)
|
||||
M.visible_message(message = "[user] opens their mouth silently!", self_message = "You cannot say anything!", blind_message = emote_message_impaired, runemessage = "opens their mouth silently!")
|
||||
return
|
||||
return FALSE
|
||||
else
|
||||
M.audible_message(message = use_3p, self_message = use_1p, deaf_message = emote_message_impaired, hearing_distance = use_range, radio_message = use_radio, runemessage = prefinal_3p)
|
||||
else
|
||||
@@ -144,6 +144,7 @@ var/global/list/emotes_by_key
|
||||
|
||||
do_extra(user, target)
|
||||
do_sound(user)
|
||||
return TRUE
|
||||
|
||||
/decl/emote/proc/replace_target_tokens(var/msg, var/atom/target)
|
||||
. = msg
|
||||
|
||||
@@ -230,14 +230,22 @@
|
||||
/obj/item/reagent_containers/food/snacks/attack_generic(var/mob/living/user)
|
||||
if(!isanimal(user) && !isalien(user))
|
||||
return
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_mob/animal/critter = user
|
||||
critter.eat_food_item(src, bitesize)
|
||||
else
|
||||
user.visible_message("<b>[user]</b> nibbles away at \the [src].","You nibble away at \the [src].")
|
||||
bitecount++
|
||||
if(reagents)
|
||||
reagents.trans_to_mob(user, bitesize, CHEM_INGEST)
|
||||
bitecount++
|
||||
spawn(5)
|
||||
if(!src && !user.client)
|
||||
user.custom_emote(1,"[pick("burps", "cries for more", "burps twice", "looks at the area where the food was")]")
|
||||
qdel(src)
|
||||
if(!QDELETED(src))
|
||||
On_Consume(user)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/animal_consumed(var/mob/user)
|
||||
On_Consume(user)
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
@@ -76,3 +76,15 @@
|
||||
machine_understands = 0
|
||||
space_chance = 100
|
||||
syllables = list("chirp", "squawk", "tweet")
|
||||
|
||||
/datum/language/drake
|
||||
name = "Drake"
|
||||
desc = "Hiss hiss, feed me siffets."
|
||||
speech_verb = "hisses"
|
||||
ask_verb = "chirps"
|
||||
exclaim_verb = "rumbles"
|
||||
key = "D"
|
||||
flags = RESTRICTED
|
||||
machine_understands = 0
|
||||
space_chance = 30
|
||||
syllables = list("hss", "ssh", "khs", "hrr", "rrr", "rrn")
|
||||
|
||||
@@ -1161,5 +1161,7 @@
|
||||
/mob/living/proc/rip_out_internal_organ(var/zone, var/skip_wounding = FALSE, var/damage_descriptor)
|
||||
if(length(internal_organs))
|
||||
. = pick_n_take(internal_organs)
|
||||
if(ispath(.))
|
||||
. = new .(src)
|
||||
if(!skip_wounding)
|
||||
take_damage(rand(10,20))
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
try_reload()
|
||||
return FALSE
|
||||
|
||||
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [A]!</span>")
|
||||
visible_message(SPAN_DANGER("\The [src] [projectileverb] at \the [A]!"))
|
||||
shoot(A)
|
||||
if(casingtype)
|
||||
new casingtype(loc)
|
||||
|
||||
@@ -48,9 +48,12 @@
|
||||
apply_damage(damage = harm_intent_damage, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE)
|
||||
L.visible_message("<span class='warning'>\The [L] [response_harm] \the [src]!</span>")
|
||||
L.do_attack_animation(src)
|
||||
IWasAttackedBy(L)
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_mob/proc/IWasAttackedBy(var/mob/living/attacker)
|
||||
return
|
||||
|
||||
// When somoene clicks us with an item in hand
|
||||
/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user)
|
||||
@@ -304,4 +307,3 @@
|
||||
if(ignore_thickness)
|
||||
return TRUE
|
||||
return !thick_armor
|
||||
|
||||
|
||||
@@ -17,13 +17,31 @@
|
||||
|
||||
/mob/living/simple_mob/examine(mob/user)
|
||||
. = ..()
|
||||
if(stat != DEAD && user && harvest_tool && (get_dist(user, src) <= 3))
|
||||
. += "<span class='notice'>\The [src] can be [harvest_verb] with a [initial(harvest_tool.name)] every [round(harvest_cooldown, 0.1)] minutes.</span>"
|
||||
if(user && (get_dist(user, src) <= 3))
|
||||
|
||||
var/datum/gender/G = gender_datums[get_visible_gender()]
|
||||
if(stat == DEAD)
|
||||
. += "<b><span class='cult'>[G.He] [G.is] dead.</span></b>"
|
||||
return
|
||||
|
||||
if(harvest_tool)
|
||||
. += SPAN_NOTICE("\The [src] can be [harvest_verb] with a [initial(harvest_tool.name)] every [round(harvest_cooldown, 0.1)] minutes.")
|
||||
var/time_to_harvest = (harvest_recent + harvest_cooldown) - world.time
|
||||
if(time_to_harvest > 0)
|
||||
. += "<span class='notice'>It can be [harvest_verb] in [time_to_harvest / (1 MINUTE)] second(s).</span>"
|
||||
. += SPAN_NOTICE("It can be [harvest_verb] in [time_to_harvest / (1 MINUTE)] second(s).")
|
||||
else
|
||||
. += "<span class='notice'>It can be [harvest_verb] now.</span>"
|
||||
. += SPAN_NOTICE("It can be [harvest_verb] now.")
|
||||
|
||||
var/percent_health = health / maxHealth
|
||||
if(percent_health >= 1)
|
||||
. += SPAN_NOTICE("[G.He] [G.is] uninjured.")
|
||||
else if(percent_health >= 0.7)
|
||||
. += SPAN_WARNING("[G.He] [G.is] mildly injured.")
|
||||
else if(percent_health >= 0.4)
|
||||
. += SPAN_WARNING("[G.He] [G.is] moderately injured.")
|
||||
else
|
||||
. += SPAN_DANGER("[G.He] [G.is] badly injured.")
|
||||
|
||||
|
||||
/mob/living/simple_mob/proc/livestock_harvest(var/obj/item/tool, var/mob/living/user)
|
||||
if(!LAZYLEN(harvest_results)) // Might be a unique interaction of an object using the proc to do something weird, or just someone's a donk.
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
if(can_special_attack(A) && special_attack_target(A))
|
||||
return
|
||||
|
||||
if(projectiletype)
|
||||
if(can_projectile_attack(A))
|
||||
shoot_target(A)
|
||||
|
||||
/mob/living/simple_mob/proc/can_projectile_attack(var/atom/A)
|
||||
return !!projectiletype
|
||||
|
||||
/mob/living/simple_mob/proc/do_interaction(var/atom/A)
|
||||
switch(a_intent)
|
||||
if(I_HURT)
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
var/special_attack_charges = null // If set, special attacks will work off of a charge system, and won't be usable if all charges are expended. Good for grenades.
|
||||
var/special_attack_cooldown = null // If set, special attacks will have a cooldown between uses.
|
||||
var/last_special_attack = null // world.time when a special attack occured last, for cooldown calculations.
|
||||
var/projectileverb = "fires"
|
||||
|
||||
//Damage resistances
|
||||
var/grab_resist = 0 // Chance for a grab attempt to fail. Note that this is not a true resist and is just a prob() of failure.
|
||||
|
||||
@@ -26,41 +26,65 @@
|
||||
var/forager = TRUE // Can eat from trees and bushes.
|
||||
var/scavenger = FALSE // Can eat from corpses.
|
||||
var/burrower = FALSE // Can dig dens.
|
||||
var/bitesize = 3 // How many reagents to take per nibble
|
||||
|
||||
/decl/mob_organ_names/quadruped //Most subtypes have this basic body layout.
|
||||
hit_zones = list("head", "torso", "left foreleg", "right foreleg", "left hind leg", "right hind leg", "tail")
|
||||
|
||||
/mob/living/simple_mob/animal/do_interaction(var/atom/A)
|
||||
|
||||
// Can we do some critter interaction?
|
||||
var/static/list/atom_types_with_animal_interactions = list(
|
||||
/obj/structure/animal_den,
|
||||
/obj/structure/flora,
|
||||
/turf/simulated/floor/outdoors
|
||||
/turf/simulated/floor/outdoors,
|
||||
/obj/item/organ,
|
||||
/obj/item/reagent_containers/food
|
||||
)
|
||||
for(var/checktype in atom_types_with_animal_interactions)
|
||||
if(istype(A, checktype))
|
||||
attack_generic(A, 0, "investigates")
|
||||
A.attack_generic(src, 0, "investigates")
|
||||
return TRUE
|
||||
|
||||
// Can we eat a dead guy?
|
||||
if(scavenger && isliving(A) && a_intent == I_HURT)
|
||||
var/mob/living/M = A
|
||||
if(M.stat == DEAD && length(M.internal_organs))
|
||||
to_chat(src, SPAN_NOTICE("You dig into the guts of \the [M], hunting for the sweetest meat."))
|
||||
if(do_after(src, M, 2 SECOND) && !QDELETED(M) && length(M.internal_organs))
|
||||
var/obj/item/organ = M.rip_out_internal_organ(check_zone(zone_sel.selecting), damage_descriptor = "animal teeth")
|
||||
set_AI_busy(TRUE)
|
||||
if(do_after(src, 2 SECONDS, M) && !QDELETED(M) && length(M.internal_organs))
|
||||
var/obj/item/organ = M.rip_out_internal_organ(check_zone(zone_sel?.selecting), damage_descriptor = "animal teeth")
|
||||
if(organ)
|
||||
visible_message(SPAN_DANGER("\The [src] rips \the [organ] out of \the [M] and devours [organ.gender == PLURAL ? "them" : "it"]!"))
|
||||
eat_food_item(organ)
|
||||
if(!length(M.internal_organs) && M.gib_on_butchery)
|
||||
M.gib()
|
||||
set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/proc/eat_food_item(var/obj/item/snack, var/silent)
|
||||
/mob/living/simple_mob/animal/proc/eat_food_item(var/obj/item/snack, var/override_bitesize, var/silent)
|
||||
|
||||
if(!silent)
|
||||
visible_message("<b>\The [src]</b> nibbles away at \the [snack].")
|
||||
playsound(src, 'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
|
||||
if(istype(snack, /obj/item/organ))
|
||||
var/obj/item/organ/organ = snack
|
||||
if(organ.meat_type)
|
||||
eat_food_item(new organ.meat_type(src), silent = TRUE)
|
||||
if(!silent)
|
||||
playsound(src, 'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
snack = new organ.meat_type(src)
|
||||
qdel(organ)
|
||||
|
||||
if(snack.reagents?.total_volume)
|
||||
snack.reagents.trans_to_mob(src, snack.reagents.total_volume, CHEM_INGEST)
|
||||
qdel(snack)
|
||||
var/use_bitesize = (override_bitesize || bitesize)
|
||||
var/removing_reagents = clamp(use_bitesize, 0, min(max(0, reagents?.maximum_volume - reagents?.total_volume), snack.reagents.total_volume))
|
||||
if(reagents && removing_reagents)
|
||||
snack.reagents.trans_to_holder(reagents, removing_reagents)
|
||||
else
|
||||
snack.reagents.trans_to_mob(src, use_bitesize, CHEM_INGEST)
|
||||
|
||||
// If there's some left, drop it onto the ground.
|
||||
if(snack.reagents?.total_volume > 0)
|
||||
snack.dropInto(loc)
|
||||
else
|
||||
snack.animal_consumed(src)
|
||||
|
||||
@@ -0,0 +1,509 @@
|
||||
/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 as infection is burned away.</span>"
|
||||
on_expired_text = "<span class = 'notice'>The last of the sap in your wounds is absorbed by your body.</span>"
|
||||
|
||||
/datum/modifier/sifsap_salve/tick()
|
||||
if(holder.stat == DEAD)
|
||||
expire()
|
||||
if(istype(holder, /mob/living/simple_mob/animal/sif))
|
||||
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'
|
||||
|
||||
/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"
|
||||
)
|
||||
|
||||
var/global/list/last_drake_howl = list()
|
||||
/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'
|
||||
var/cooldown = 20 SECONDS
|
||||
|
||||
/decl/emote/audible/drake_howl/do_emote(var/atom/user, var/extra_params)
|
||||
if(world.time < last_drake_howl["\ref[user]"])
|
||||
to_chat(user, SPAN_WARNING("You cannot howl again so soon."))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
last_drake_howl["\ref[user]"] = world.time + cooldown
|
||||
|
||||
/decl/emote/audible/drake_howl/do_sound(var/atom/user)
|
||||
..()
|
||||
var/turf/user_turf = get_turf(user)
|
||||
if(!istype(user_turf))
|
||||
return
|
||||
var/list/close_listeners = hearers(world.view * 3, user_turf)
|
||||
for(var/mob/M in player_list)
|
||||
var/turf/T = get_turf(M)
|
||||
if(!istype(T) || istype(T,/turf/space) || T.z != user_turf.z || (M in close_listeners) || M.ear_deaf > 0)
|
||||
continue
|
||||
M << 'sound/effects/drakehowl_far.ogg'
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/get_available_emotes()
|
||||
return global._default_mob_emotes | /decl/emote/audible/drake_howl
|
||||
|
||||
/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."
|
||||
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>There are humans moving through your territory; whether you help them get home safely, or treat them as a snack, \
|
||||
is up to you."
|
||||
color = "#608894"
|
||||
icon = 'icons/mob/64x32.dmi'
|
||||
catalogue_data = list(/datum/category_item/catalogue/fauna/grafadreka)
|
||||
icon_state = "doggo"
|
||||
icon_living = "doggo"
|
||||
icon_dead = "doggo_lying"
|
||||
icon_rest = "doggo_lying"
|
||||
projectileverb = "spits"
|
||||
friendly = list("headbutts", "grooms", "play-bites", "rubs against")
|
||||
bitesize = 10 // chomp
|
||||
has_langs = list("Drake")
|
||||
|
||||
tt_desc = "S tesca pabulator"
|
||||
faction = "grafadreka"
|
||||
|
||||
maxHealth = 150
|
||||
health = 150
|
||||
movement_cooldown = 2
|
||||
base_attack_cooldown = 1 SECOND
|
||||
|
||||
organ_names = /decl/mob_organ_names/grafadreka
|
||||
say_list_type = /datum/say_list/grafadreka
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/intentional/grafadreka
|
||||
|
||||
scavenger = TRUE
|
||||
burrower = TRUE
|
||||
|
||||
projectilesound = 'sound/effects/splat.ogg'
|
||||
projectiletype = /obj/item/projectile/drake_spit
|
||||
|
||||
// Claw attacks.
|
||||
attack_sharp = TRUE
|
||||
melee_damage_lower = 8
|
||||
melee_damage_upper = 18
|
||||
attack_armor_pen = 15
|
||||
attack_sound = 'sound/weapons/slice.ogg'
|
||||
|
||||
// Attack strings for swapping.
|
||||
attacktext = null
|
||||
var/static/list/claw_attacktext = list("slashed", "clawed", "swiped", "gouged")
|
||||
var/static/list/bite_attacktext = list("savaged", "bitten", "mauled")
|
||||
|
||||
// Bite attacks.
|
||||
var/const/bite_melee_damage_lower = 30
|
||||
var/const/bite_melee_damage_upper = 40
|
||||
var/const/bite_attack_armor_pen = 60
|
||||
var/const/bite_attack_sound = 'sound/weapons/bite.ogg'
|
||||
|
||||
// Used to avoid setting vars every single attack.
|
||||
var/attacking_with_claws = TRUE
|
||||
|
||||
// Set during initialize and used to generate overlays.
|
||||
var/tmp/fur_colour
|
||||
var/tmp/claw_colour
|
||||
var/tmp/glow_colour
|
||||
var/tmp/base_colour
|
||||
var/tmp/eye_colour
|
||||
|
||||
var/next_spit = 0
|
||||
var/spit_cooldown = 5 SECONDS
|
||||
var/next_alpha_check = 0
|
||||
var/dominance = 0 // A score used to determine pack leader.
|
||||
var/stored_sap = 0
|
||||
var/max_stored_sap = 100
|
||||
var/attacked_by_human = FALSE
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/can_tend_wounds(var/mob/living/friend)
|
||||
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
|
||||
return (friend.health < friend.maxHealth)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Initialize()
|
||||
gender = pick(MALE, FEMALE)
|
||||
attacktext = claw_attacktext.Copy()
|
||||
setup_colours()
|
||||
create_reagents(50)
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/examine(var/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)
|
||||
. += SPAN_NOTICE("[G.His] sap reserves are high.")
|
||||
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)
|
||||
return FALSE
|
||||
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))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(.)
|
||||
next_spit = world.time + spit_cooldown
|
||||
spend_sap(2)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/Life()
|
||||
. = ..()
|
||||
|
||||
// Process food and sap chems.
|
||||
if(stat == CONSCIOUS) // Hibernating drakes don't get hungy.
|
||||
// by default we lose nutrition. Hungry hungry drakes.
|
||||
var/food_val = resting ? -0.2 : -0.5
|
||||
|
||||
// Very basic metabolism.
|
||||
if(reagents?.total_volume)
|
||||
for(var/datum/reagent/chem in reagents.reagent_list)
|
||||
var/removing = min(REM, chem.volume)
|
||||
if(istype(chem, /datum/reagent/nutriment))
|
||||
var/datum/reagent/nutriment/food = chem
|
||||
food_val += (food.nutriment_factor * removing) * ((food.allergen_type & ALLERGEN_MEAT) ? 0.5 : 0.1)
|
||||
else if(istype(chem, /datum/reagent/toxin/sifslurry))
|
||||
add_sap(removing * 3)
|
||||
reagents.remove_reagent(chem.id, removing)
|
||||
|
||||
if(food_val != 0)
|
||||
nutrition = clamp(nutrition + food_val, 0, max_nutrition)
|
||||
|
||||
/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(stored_sap + amt, 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(stored_sap - amt, 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))
|
||||
return
|
||||
var/health_deficiency = (getMaxHealth() - health)
|
||||
if(health_deficiency >= 40)
|
||||
. += (health_deficiency / 25)
|
||||
var/hungry = (max_nutrition - nutrition) / 5
|
||||
if (hungry >= 70)
|
||||
. += hungry/50
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/update_icon()
|
||||
|
||||
. = ..()
|
||||
|
||||
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)
|
||||
I = image(icon, "[icon_state]-eye_overlay")
|
||||
I.color = eye_colour
|
||||
add_images += I
|
||||
|
||||
if(stat != DEAD)
|
||||
I = image(icon, "[icon_state]-glow")
|
||||
I.color = glow_colour
|
||||
I.plane = PLANE_LIGHTING_ABOVE
|
||||
I.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1))
|
||||
add_images += I
|
||||
|
||||
for(var/image/adding in add_images)
|
||||
adding.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
|
||||
adding.pixel_x = -16 // 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.
|
||||
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/handle_special()
|
||||
..()
|
||||
if(client || world.time >= next_alpha_check)
|
||||
next_alpha_check = world.time + (60 SECONDS)
|
||||
check_alpha_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)
|
||||
to_chat(src, SPAN_WARNING("You are unwounded."))
|
||||
else
|
||||
return ..()
|
||||
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)
|
||||
|
||||
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))
|
||||
set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
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)
|
||||
H.bloodstr.add_reagent("sifsap", rand(1,2))
|
||||
for(var/datum/wound/W in E.wounds)
|
||||
W.salve()
|
||||
// Everyone else is just poisoned.
|
||||
else if(!istype(friend, /mob/living/simple_mob/animal/sif))
|
||||
friend.adjustToxLoss(rand(10,20))
|
||||
return TRUE
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/get_local_alpha()
|
||||
var/pack = FALSE
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/alpha = src
|
||||
for(var/mob/living/simple_mob/animal/sif/grafadreka/beta in hearers(7, loc))
|
||||
if(beta != src && beta.stat != DEAD)
|
||||
pack = TRUE
|
||||
if(beta.dominance > alpha.dominance)
|
||||
alpha = beta
|
||||
if(pack)
|
||||
return alpha
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/check_alpha_status()
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/alpha = get_local_alpha()
|
||||
if(src == alpha)
|
||||
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/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 = TRUE
|
||||
if(isliving(A))
|
||||
var/mob/living/M = A
|
||||
if(M.lying || M.incapacitated())
|
||||
attacking_with_claws = FALSE
|
||||
|
||||
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()
|
||||
else // Use ur teef
|
||||
damage_amount = max(damage_amount, rand(bite_melee_damage_lower, bite_melee_damage_upper))
|
||||
attack_armor_pen = bite_attack_armor_pen
|
||||
attack_sound = bite_attack_sound
|
||||
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/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))
|
||||
to_chat(src, SPAN_WARNING("You aren't the pack leader! Sit down!"))
|
||||
return
|
||||
|
||||
for(var/mob/living/simple_mob/animal/sif/grafadreka/drake in hearers(7, src))
|
||||
if(drake == src || drake.faction != faction)
|
||||
continue
|
||||
if(drake.client)
|
||||
to_chat(drake, SPAN_NOTICE("The pack leader wishes for you to follow them."))
|
||||
else if(drake.ai_holder)
|
||||
drake.ai_holder.set_follow(src)
|
||||
|
||||
audible_message("<b>\The [src]</b> barks loudly and rattles its neck spines.")
|
||||
|
||||
/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 = "grafadreka den"
|
||||
critter = /mob/living/simple_mob/animal/sif/grafadreka/wild/hibernate
|
||||
|
||||
// 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/wild/Initialize()
|
||||
dominance = rand(5, 15)
|
||||
stored_sap = rand(20, 30)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild/Login()
|
||||
. = ..()
|
||||
if(client)
|
||||
dominance = INFINITY // Let players lead by default.
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild/Logout()
|
||||
. = ..()
|
||||
if(!client)
|
||||
dominance = rand(5, 15)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/wild/hibernate/Initialize()
|
||||
. = ..()
|
||||
dominance = 0
|
||||
lay_down()
|
||||
@@ -0,0 +1,152 @@
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/ICheckRangedAttack(atom/A)
|
||||
. = ..() && isliving(A)
|
||||
if(.)
|
||||
var/mob/living/M = A
|
||||
if(M.lying || M.incapacitated())
|
||||
. = FALSE // They're already stunned, go bite their nipples off.
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/IIsAlly(mob/living/L)
|
||||
. = ..()
|
||||
// Avoid humans unless we're very hungry, very angry, or they are small).
|
||||
if(!. && ishuman(L) && !attacked_by_human)
|
||||
. = !issmall(L) && (nutrition > max_nutrition * 0.15)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/proc/IDoInteraction(var/obj/item/target)
|
||||
set waitfor = FALSE
|
||||
face_atom(target)
|
||||
do_interaction(target)
|
||||
|
||||
/mob/living/simple_mob/animal/sif/grafadreka/IWasAttackedBy(var/mob/living/attacker)
|
||||
if(ishuman(attacker))
|
||||
attacked_by_human = TRUE
|
||||
if(istype(ai_holder, /datum/ai_holder/simple_mob/intentional/grafadreka))
|
||||
var/datum/ai_holder/simple_mob/intentional/grafadreka/drake_brain = ai_holder
|
||||
drake_brain.hostile = TRUE
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka
|
||||
hostile = TRUE
|
||||
retaliate = TRUE
|
||||
cooperative = TRUE
|
||||
can_flee = TRUE
|
||||
flee_when_dying = TRUE
|
||||
var/next_food_check
|
||||
var/next_sap_check
|
||||
var/next_heal_check
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/should_flee(force)
|
||||
. = ..()
|
||||
if(!. && ismob(target))
|
||||
var/mob/living/prey = target
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/drake = holder
|
||||
// If they're incapacitated, don't need to spit on them again.
|
||||
if(!istype(drake) || prey.lying || prey.incapacitated())
|
||||
return FALSE
|
||||
// We can spit at them - disengage so you can pew pew.
|
||||
if(get_dist(target, holder) <= 1 && drake.has_sap(2))
|
||||
return TRUE
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/pre_ranged_attack(atom/A)
|
||||
holder.a_intent = I_HURT
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/pre_melee_attack(atom/A)
|
||||
holder.a_intent = I_HURT
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/post_melee_attack()
|
||||
if(holder.has_modifier_of_type(/datum/modifier/ace))
|
||||
request_help()
|
||||
return ..()
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/handle_special_strategical()
|
||||
follow_distance = rand(initial(follow_distance), initial(follow_distance) + 2)
|
||||
if(holder.has_modifier_of_type(/datum/modifier/ace))
|
||||
if(leader)
|
||||
lose_follow()
|
||||
else
|
||||
if(!leader)
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/drake = holder
|
||||
if(istype(drake))
|
||||
set_follow(drake.get_local_alpha())
|
||||
return ..()
|
||||
|
||||
/datum/ai_holder/simple_mob/intentional/grafadreka/handle_stance_strategical()
|
||||
|
||||
. = ..()
|
||||
|
||||
var/mob/living/simple_mob/animal/sif/grafadreka/drake = holder
|
||||
if(!istype(drake))
|
||||
return
|
||||
|
||||
if(drake.resting && stance != STANCE_IDLE)
|
||||
drake.lay_down()
|
||||
|
||||
if(stance in STANCES_COMBAT)
|
||||
return
|
||||
|
||||
// Heal anyone near us who needs it.
|
||||
if(stance == STANCE_IDLE && drake.has_sap(10)) // explicit stance check so they stop trying to heal for 15 seconds during flee behavior
|
||||
for(var/mob/living/friend in range(1, get_turf(holder)))
|
||||
if(!holder.Adjacent(friend) || friend == target || friend.stat == DEAD)
|
||||
continue
|
||||
// Some specific checks here rather than IIsAlly() as we have some behavior to avoid
|
||||
// attacking humans, but don't necessarily want to go lick them if they get a papercut.
|
||||
if(!(friend in drake.friends) && !(friend in drake.tamers) && drake.faction != friend.faction)
|
||||
continue
|
||||
if(!friend.has_modifier_of_type(/datum/modifier/sifsap_salve) && drake.can_tend_wounds(friend))
|
||||
drake.a_intent = I_HELP
|
||||
drake.IDoInteraction(friend)
|
||||
return
|
||||
if(world.time >= next_heal_check)
|
||||
next_heal_check = world.time + 15 SECONDS
|
||||
for(var/mob/living/friend in view(world.view, drake))
|
||||
if(target == friend || friend.stat == DEAD || friend.has_modifier_of_type(/datum/modifier/sifsap_salve))
|
||||
continue
|
||||
if(((friend in drake.friends) || (friend in drake.tamers) || drake.faction == friend.faction) && drake.can_tend_wounds(friend))
|
||||
give_destination(get_turf(friend))
|
||||
return
|
||||
|
||||
// These actions need space in our stomach to be worth considering at all.
|
||||
if(drake.reagents && abs(drake.reagents.total_volume - drake.reagents.maximum_volume) >= 10)
|
||||
|
||||
// Find some sap if we're low and aren't already digesting some.
|
||||
if(!drake.has_sap(20) && !drake.reagents.has_reagent("sifsap"))
|
||||
for(var/obj/structure/flora/tree/sif/tree in range(1, holder))
|
||||
if(holder.Adjacent(tree) && tree.can_forage(drake))
|
||||
drake.a_intent = I_HELP
|
||||
drake.IDoInteraction(tree)
|
||||
return
|
||||
if(world.time >= next_sap_check)
|
||||
next_sap_check = world.time + 15 SECONDS
|
||||
for(var/obj/structure/flora/tree/sif/tree in view(world.view, drake))
|
||||
if(tree.can_forage(drake))
|
||||
give_destination(get_turf(tree))
|
||||
return
|
||||
|
||||
// Are we hungy?
|
||||
if(drake.nutrition < drake.max_nutrition * 0.6)
|
||||
var/atom/movable/food
|
||||
for(var/thing in range(1, drake))
|
||||
if(istype(thing, /obj/item/reagent_containers/food) || istype(thing, /obj/item/organ))
|
||||
food = thing
|
||||
break
|
||||
if(isliving(thing))
|
||||
var/mob/living/M = thing
|
||||
if(M.stat == DEAD)
|
||||
food = M
|
||||
break
|
||||
if(food)
|
||||
drake.a_intent = I_HURT
|
||||
drake.IDoInteraction(food)
|
||||
return
|
||||
if(world.time >= next_food_check)
|
||||
food = null
|
||||
next_food_check = world.time + 15 SECONDS
|
||||
for(var/thing in view(world.view, drake))
|
||||
if(istype(thing, /obj/item/reagent_containers/food) || istype(thing, /obj/item/organ))
|
||||
food = thing
|
||||
else if(isliving(thing))
|
||||
var/mob/living/M = thing
|
||||
if(M.stat == DEAD)
|
||||
food = M
|
||||
if(food)
|
||||
give_destination(get_turf(food))
|
||||
return
|
||||
@@ -558,3 +558,10 @@ var/global/list/organ_cache = list()
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/obj/item/organ/attack_generic(mob/user)
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_mob/animal/critter = user
|
||||
critter.eat_food_item(src)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
prob(45);/mob/living/simple_mob/animal/sif/shantak,
|
||||
prob(15);/mob/living/simple_mob/animal/sif/kururak/hibernate,
|
||||
prob(10);/mob/living/simple_mob/animal/sif/kururak,
|
||||
prob(5);/mob/living/simple_mob/animal/sif/grafadreka/wild/hibernate,
|
||||
prob(3);/mob/living/simple_mob/animal/sif/grafadreka/wild,
|
||||
prob(3);/mob/living/simple_mob/animal/giant_spider,
|
||||
prob(8);/mob/living/simple_mob/animal/giant_spider/hunter,
|
||||
prob(3);/mob/living/simple_mob/animal/giant_spider/webslinger,
|
||||
|
||||
26
maps/submaps/surface_submaps/wilderness/grafadreka_den.dmm
Normal file
26
maps/submaps/surface_submaps/wilderness/grafadreka_den.dmm
Normal file
@@ -0,0 +1,26 @@
|
||||
"a" = (/turf/template_noop,/area/template_noop)
|
||||
"b" = (/turf/simulated/mineral/ignore_mapgen,/area/submap/GrafadrekaDen)
|
||||
"c" = (/obj/random/junk,/obj/random/medical/pillbottle,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"d" = (/obj/random/junk,/obj/random/humanoidremains,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"e" = (/obj/random/mob/sif/grafadreka,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"f" = (/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"g" = (/obj/structure/animal_den/ghost_join/grafadreka,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"h" = (/obj/random/junk,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"i" = (/obj/random/outcrop,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"j" = (/obj/random/humanoidremains,/obj/random/coin,/obj/random/contraband,/obj/random/projectile/random,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"o" = (/obj/random/humanoidremains,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"D" = (/obj/structure/animal_den/ghost_join/grafadreka,/turf/simulated/mineral/floor/sif,/area/submap/GrafadrekaDen)
|
||||
"P" = (/turf/simulated/mineral/ignore_mapgen,/area/submap/GrafadrekaDen)
|
||||
|
||||
(1,1,1) = {"
|
||||
aaaaaaaaaa
|
||||
aaabbbbaaa
|
||||
aabbDbffaa
|
||||
abbgoifbba
|
||||
abbiffecba
|
||||
abhffffdba
|
||||
abbDfffbba
|
||||
abbPjfbbba
|
||||
aabbbffaaa
|
||||
aaaaaaaaaa
|
||||
"}
|
||||
@@ -217,6 +217,12 @@
|
||||
mappath = 'maps/submaps/surface_submaps/wilderness/kururakden.dmm'
|
||||
cost = 5
|
||||
|
||||
/datum/map_template/surface/wilderness/normal/GrafadrekaDen
|
||||
name = "Grafadreka Den"
|
||||
desc = "The den of a Grafadreka pack. May contain hibernating members."
|
||||
mappath = 'maps/submaps/surface_submaps/wilderness/grafadreka_den.dmm'
|
||||
cost = 5
|
||||
|
||||
/datum/map_template/surface/wilderness/normal/DecoupledEngine
|
||||
name = "Decoupled Engine"
|
||||
desc = "A damaged fission engine jettisoned from a starship long ago."
|
||||
@@ -342,4 +348,3 @@
|
||||
desc = "Gathering of acolytes gone wrong."
|
||||
mappath = 'maps/submaps/surface_submaps/wilderness/deathden.dmm'
|
||||
cost = 15
|
||||
|
||||
|
||||
@@ -100,6 +100,10 @@
|
||||
name = "POI - Kururak Den"
|
||||
ambience = AMBIENCE_SIF
|
||||
|
||||
/area/submap/GrafadrekaDen
|
||||
name = "POI - Grafadreka Den"
|
||||
ambience = AMBIENCE_SIF
|
||||
|
||||
/area/submap/DecoupledEngine
|
||||
name = "POI - Decoupled Engine"
|
||||
ambience = AMBIENCE_FOREBODING
|
||||
|
||||
@@ -2479,6 +2479,8 @@
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\duck.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\frostfly.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\glitterfly.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\grafadreka.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\grafadreka_ai.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\hare.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\hooligan_crab.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\kururak.dm"
|
||||
|
||||
BIN
sound/effects/drakehowl_close.ogg
Normal file
BIN
sound/effects/drakehowl_close.ogg
Normal file
Binary file not shown.
BIN
sound/effects/drakehowl_far.ogg
Normal file
BIN
sound/effects/drakehowl_far.ogg
Normal file
Binary file not shown.
Reference in New Issue
Block a user