Merges AI Branch into Master

This commit is contained in:
Atermonera
2018-12-01 00:06:38 -05:00
committed by VirgoBot
parent 15017e893e
commit 2df5e9fe0e
379 changed files with 22460 additions and 4751 deletions
+18
View File
@@ -0,0 +1,18 @@
/*
'Aura' modifiers are semi-permanent, in that they do not have a set duration, but will expire if out of range of the 'source' of the aura.
Note: The source is defined as an argument in New(), and if not specified, it is assumed the holder is the source,
making it not expire ever, which is likely not what you want.
*/
/datum/modifier/aura
var/aura_max_distance = 5 // If more than this many tiles away from the source, the modifier expires next tick.
/datum/modifier/aura/check_if_valid()
if(!origin)
expire()
var/atom/A = origin.resolve()
if(istype(A)) // Make sure we're not null.
if(get_dist(holder, A) > aura_max_distance)
expire()
else
expire() // Source got deleted or something.
+7
View File
@@ -149,6 +149,13 @@
/mob/living/proc/remove_specific_modifier(var/datum/modifier/M, var/silent = FALSE)
M.expire(silent)
// Removes one modifier of a type
/mob/living/proc/remove_a_modifier_of_type(var/modifier_type, var/silent = FALSE)
for(var/datum/modifier/M in modifiers)
if(ispath(M.type, modifier_type))
M.expire(silent)
break
// Removes all modifiers of a type
/mob/living/proc/remove_modifiers_of_type(var/modifier_type, var/silent = FALSE)
for(var/datum/modifier/M in modifiers)
+68 -1
View File
@@ -29,7 +29,7 @@ Berserk is a somewhat rare modifier to obtain freely (and for good reason), howe
- Red Slimes will berserk if they go rabid.
- Red slime core reactions will berserk slimes that can see the user in addition to making them go rabid.
- Red slime core reactions will berserk prometheans that can see the user.
- Bears will berserk when losing a fight.
- Saviks will berserk when losing a fight.
- Changelings can evolve a 2 point ability to use a changeling-specific variant of Berserk, that replaces the text with a 'we' variant.
Recursive Enhancement allows the changeling to instead used an improved variant that features less exhaustion time and less nutrition drain.
- Xenoarch artifacts may have forced berserking as one of their effects. This is especially fun if an artifact that makes hostile mobs is nearby.
@@ -180,3 +180,70 @@ the artifact triggers the rage.
accuracy = -75 // Aiming requires focus.
accuracy_dispersion = 3 // Ditto.
evasion = -45 // Too angry to dodge.
// Ignition, but confined to the modifier system.
// This makes it more predictable and thus, easier to balance.
/datum/modifier/fire
name = "on fire"
desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water."
mob_overlay_state = "on_fire"
on_created_text = "<span class='danger'>You combust into flames!</span>"
on_expired_text = "<span class='warning'>The fire starts to fade.</span>"
stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot.
var/damage_per_tick = 5
/datum/modifier/fire/tick()
holder.inflict_heat_damage(damage_per_tick)
// Applied when near something very cold.
// Reduces mobility, attack speed.
/datum/modifier/chilled
name = "chilled"
desc = "You feel yourself freezing up. Its hard to move."
mob_overlay_state = "chilled"
on_created_text = "<span class='danger'>You feel like you're going to freeze! It's hard to move.</span>"
on_expired_text = "<span class='warning'>You feel somewhat warmer and more mobile now.</span>"
stacks = MODIFIER_STACK_EXTEND
slowdown = 2
evasion = -40
attack_speed_percent = 1.4
disable_duration_percent = 1.2
// Similar to being on fire, except poison tends to be more long term.
// Antitoxins will remove stacks over time.
// Synthetics can't receive this.
/datum/modifier/poisoned
name = "poisoned"
desc = "You have poison inside of you. It will cause harm over a long span of time if not cured."
mob_overlay_state = "poisoned"
on_created_text = "<span class='warning'>You feel sick...</span>"
on_expired_text = "<span class='notice'>You feel a bit better.</span>"
stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot.
var/damage_per_tick = 1
/datum/modifier/poisoned/weak
damage_per_tick = 0.5
/datum/modifier/poisoned/strong
damage_per_tick = 2
/datum/modifier/poisoned/tick()
if(holder.stat == DEAD)
expire(silent = TRUE)
holder.inflict_poison_damage(damage_per_tick)
/datum/modifier/poisoned/can_apply(mob/living/L)
if(L.isSynthetic())
return FALSE
if(L.get_poison_protection() >= 1)
return FALSE
return TRUE
+11 -7
View File
@@ -207,8 +207,8 @@
if(istype(thing, /obj/structure/snowman/spider)) //Snow spiders are also spooky so people can be assholes with those too.
fear_amount += 1
if(istype(thing, /mob/living/simple_animal/hostile/giant_spider)) // Actual giant spiders are the scariest of them all.
var/mob/living/simple_animal/hostile/giant_spider/S = thing
if(istype(thing, /mob/living/simple_mob/animal/giant_spider)) // Actual giant spiders are the scariest of them all.
var/mob/living/simple_mob/animal/giant_spider/S = thing
if(S.stat == DEAD) // Dead giant spiders are less scary than alive ones.
fear_amount += 4
else
@@ -425,14 +425,18 @@
if(istype(thing, /obj/item/clothing/head/collectable/slime)) // Some hats are spooky so people can be assholes with them.
fear_amount += 1
if(istype(thing, /mob/living/simple_animal/slime)) // An actual predatory specimen!
var/mob/living/simple_animal/slime/S = thing
if(istype(thing, /mob/living/simple_mob/slime)) // An actual predatory specimen!
var/mob/living/simple_mob/slime/S = thing
if(S.stat == DEAD) // Dead slimes are somewhat less spook.
fear_amount += 4
if(S.is_adult == TRUE) //big boy
fear_amount += 8
if(istype(S, /mob/living/simple_mob/slime/xenobio))
var/mob/living/simple_mob/slime/xenobio/X = S
if(X.is_adult == TRUE) //big boy
fear_amount += 8
else
fear_amount += 6
else
fear_amount += 6
fear_amount += 10 // It's huge and feral.
if(istype(thing, /mob/living/carbon/human))
var/mob/living/carbon/human/S = thing
+2 -2
View File
@@ -71,7 +71,7 @@
/datum/modifier/repair_aura/tick()
spawn()
for(var/mob/living/simple_animal/construct/T in view(4,holder))
for(var/mob/living/simple_mob/construct/T in view(4,holder))
T.adjustBruteLoss(rand(-10,-15))
T.adjustFireLoss(rand(-10,-15))
@@ -110,7 +110,7 @@
spawn()
if(isliving(holder))
var/mob/living/L = holder
if(istype(L, /mob/living/simple_animal/construct))
if(istype(L, /mob/living/simple_mob/construct))
L.adjustBruteLoss(rand(-5,-10))
L.adjustFireLoss(rand(-5,-10))
else
+26 -1
View File
@@ -180,8 +180,33 @@ note dizziness decrements automatically in the mob's Life() proc.
pixel_z = default_pixel_z
alpha = initial_alpha
/atom/movable/proc/do_attack_animation(atom/A)
// Similar to attack animations, but in reverse and is longer to act as a telegraph.
/atom/movable/proc/do_windup_animation(atom/A, windup_time)
var/pixel_x_diff = 0
var/pixel_y_diff = 0
var/direction = get_dir(src, A)
if(direction & NORTH)
pixel_y_diff = -8
else if(direction & SOUTH)
pixel_y_diff = 8
if(direction & EAST)
pixel_x_diff = -8
else if(direction & WEST)
pixel_x_diff = 8
var/default_pixel_x = initial(pixel_x)
var/default_pixel_y = initial(pixel_y)
var/mob/mob = src
if(istype(mob))
default_pixel_x = mob.default_pixel_x
default_pixel_y = mob.default_pixel_y
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = windup_time - 2)
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
/atom/movable/proc/do_attack_animation(atom/A)
var/pixel_x_diff = 0
var/pixel_y_diff = 0
var/direction = get_dir(src, A)
+2 -2
View File
@@ -498,7 +498,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
//find a viable mouse candidate
var/mob/living/simple_animal/mouse/host
var/mob/living/simple_mob/animal/passive/mouse/host
var/obj/machinery/atmospherics/unary/vent_pump/vent_found
var/list/found_vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/v in machines)
@@ -506,7 +506,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
found_vents.Add(v)
if(found_vents.len)
vent_found = pick(found_vents)
host = new /mob/living/simple_animal/mouse(vent_found)
host = new /mob/living/simple_mob/animal/passive/mouse(vent_found)
else
src << "<span class='warning'>Unable to find any unwelded vents to spawn mice at.</span>"
+7
View File
@@ -52,6 +52,13 @@
if(O)
O.see_emote(src, message, m_type)
// Shortcuts for above proc
/mob/proc/visible_emote(var/act_desc)
custom_emote(1, act_desc)
/mob/proc/audible_emote(var/act_desc)
custom_emote(2, act_desc)
/mob/proc/emote_dead(var/message)
if(client.prefs.muted & MUTE_DEADCHAT)
+2 -2
View File
@@ -25,10 +25,10 @@
/mob/living/silicon/seen_cult_turfs()
return list()
/mob/living/simple_animal/seen_cult_turfs()
/mob/living/simple_mob/seen_cult_turfs()
return seen_turfs_in_range(src, 1)
/mob/living/simple_animal/shade/narsie/seen_cult_turfs()
/mob/living/simple_mob/construct/shade/seen_cult_turfs()
return view(2, src)
/proc/seen_turfs_in_range(var/source, var/range)
+9 -10
View File
@@ -78,6 +78,12 @@
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
src.playsound_local(source, speech_sound, sound_vol, 1)
// Done here instead of on_hear_say() since that is NOT called if the mob is clientless (which includes most AI mobs).
/mob/living/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
..()
if(has_AI()) // Won't happen if no ai_holder exists or there's a player inside w/o autopilot active.
ai_holder.on_hear_say(speaker, message)
/mob/proc/on_hear_say(var/message)
to_chat(src, message)
if(teleop)
@@ -158,17 +164,10 @@
if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
if(!say_understands(speaker,language))
if(istype(speaker,/mob/living/simple_animal))
var/mob/living/simple_animal/S = speaker
if(S.speak && S.speak.len)
message = pick(S.speak)
else
return
if(language)
message = language.scramble(message)
else
if(language)
message = language.scramble(message)
else
message = stars(message)
message = stars(message)
if(hard_to_hear)
message = stars(message)
+3 -3
View File
@@ -27,16 +27,16 @@
/datum/language/corticalborer/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
var/mob/living/simple_animal/borer/B
var/mob/living/simple_mob/animal/borer/B
if(istype(speaker,/mob/living/carbon))
var/mob/living/carbon/M = speaker
B = M.has_brain_worms()
else if(istype(speaker,/mob/living/simple_animal/borer))
else if(istype(speaker,/mob/living/simple_mob/animal/borer))
B = speaker
if(B)
speaker_mask = B.truename
speaker_mask = B.true_name
..(speaker,message,speaker_mask)
/datum/language/vox
+5 -5
View File
@@ -215,7 +215,7 @@
else
if(declare_arrests)
var/action = arrest_type ? "detaining" : "arresting"
if(istype(target, /mob/living/simple_animal))
if(istype(target, /mob/living/simple_mob))
action = "fighting"
global_announcer.autosay("[src] is [action] a level [threat] [action != "fighting" ? "suspect" : "threat"] <b>[target_name(target)]</b> in <b>[get_area(src)]</b>.", "[src]", "Security")
UnarmedAttack(target)
@@ -269,8 +269,8 @@
C.handcuffed = new /obj/item/weapon/handcuffs(C)
C.update_inv_handcuffed()
busy = 0
else if(istype(M, /mob/living/simple_animal))
var/mob/living/simple_animal/S = M
else if(istype(M, /mob/living/simple_mob))
var/mob/living/simple_mob/S = M
S.Weaken(xeno_stun_strength)
S.adjustBruteLoss(xeno_harm_strength)
do_attack_animation(M)
@@ -286,8 +286,8 @@
/mob/living/bot/secbot/slime/UnarmedAttack(var/mob/living/L, var/proximity)
..()
if(istype(L, /mob/living/simple_animal/slime))
var/mob/living/simple_animal/slime/S = L
if(istype(L, /mob/living/simple_mob/slime/xenobio))
var/mob/living/simple_mob/slime/xenobio/S = L
S.adjust_discipline(2)
@@ -63,5 +63,5 @@
if(istype(M))
for(var/atom/A in M.contents)
if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder))
if(istype(A,/mob/living/simple_mob/animal/borer) || istype(A,/obj/item/weapon/holder))
return
@@ -46,7 +46,7 @@
return 1
if (istype(other, /mob/living/carbon/human))
return 1
if (istype(other, /mob/living/simple_animal/slime))
if (istype(other, /mob/living/simple_mob/slime))
return 1
return ..()
@@ -76,7 +76,6 @@
if(ingested) ingested.metabolize()
if(bloodstr) bloodstr.metabolize()
AdjustConfused(-1)
// decrement dizziness counter, clamped to 0
if(resting)
dizziness = max(0, dizziness - 5)
@@ -5,7 +5,7 @@
set name = "Release Control"
set desc = "Release control of your host's body."
var/mob/living/simple_animal/borer/B = has_brain_worms()
var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
if(B && B.host_brain)
src << "<span class='danger'>You withdraw your probosci, releasing control of [B.host_brain]</span>"
@@ -25,7 +25,7 @@
set name = "Torment host"
set desc = "Punish your host with agony."
var/mob/living/simple_animal/borer/B = has_brain_worms()
var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
if(!B)
return
@@ -43,7 +43,7 @@
set name = "Reproduce"
set desc = "Spawn several young."
var/mob/living/simple_animal/borer/B = has_brain_worms()
var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
if(!B)
return
@@ -55,7 +55,7 @@
B.has_reproduced = 1
vomit(1)
new /mob/living/simple_animal/borer(get_turf(src))
new /mob/living/simple_mob/animal/borer(get_turf(src))
else
src << "<span class='warning'>You do not have enough chemicals stored to reproduce.</span>"
@@ -61,11 +61,11 @@
//Handle brain slugs.
var/obj/item/organ/external/Hd = get_organ(BP_HEAD)
var/mob/living/simple_animal/borer/B
var/mob/living/simple_mob/animal/borer/B
if(Hd)
for(var/I in Hd.implants)
if(istype(I,/mob/living/simple_animal/borer))
if(istype(I,/mob/living/simple_mob/animal/borer))
B = I
if(B)
if(!B.ckey && ckey && B.controlling)
@@ -135,6 +135,27 @@ emp_act
return siemens_coefficient
// Similar to above but is for the mob's overall protection, being the average of all slots.
/mob/living/carbon/human/proc/get_siemens_coefficient_average()
var/siemens_value = 0
var/total = 0
for(var/organ_name in organs_by_name)
if(organ_name in organ_rel_size)
var/obj/item/organ/external/organ = organs_by_name[organ_name]
if(organ)
var/weight = organ_rel_size[organ_name]
siemens_value += get_siemens_coefficient_organ(organ) * weight
total += weight
if(fire_stacks < 0) // Water makes you more conductive.
siemens_value *= 1.5
return (siemens_value/max(total, 1))
// Returns a number between 0 to 1, with 1 being total protection.
/mob/living/carbon/human/get_shock_protection()
return between(0, 1-get_siemens_coefficient_average(), 1)
// Returns a list of clothing that is currently covering def_zone.
/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type)
var/list/results = list()
@@ -542,6 +563,20 @@ emp_act
return perm
// This is for preventing harm by being covered in water, which only prometheans need to deal with.
// This is not actually used for now since the code for prometheans gets changed a lot.
/mob/living/carbon/human/get_water_protection()
var/protection = ..() // Todo: Replace with species var later.
if(protection == 1) // No point doing permeability checks if it won't matter.
return protection
// Wearing clothing with a low permeability_coefficient can protect from water.
var/converted_protection = 1 - protection
var/perm = reagent_permeability()
converted_protection *= perm
return 1-converted_protection
/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone)
if(!..())
@@ -42,6 +42,16 @@
if(cloaker.active)
cloaker.deactivate()
/mob/living/carbon/human/is_cloaked()
if(mind && mind.changeling && mind.changeling.cloaked) // Ling camo.
return TRUE
else if(istype(back, /obj/item/weapon/rig)) //Ninja cloak
var/obj/item/weapon/rig/suit = back
for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules)
if(cloaker.active)
return TRUE
return ..()
/mob/living/carbon/human/get_ear_protection()
var/sum = 0
if(istype(l_ear, /obj/item/clothing/ears))
+1 -3
View File
@@ -1047,8 +1047,6 @@
sleeping += 1
Paralyse(5)
confused = max(0, confused - 1)
// If you're dirty, your gloves will become dirty, too.
if(gloves && germ_level > gloves.germ_level && prob(10))
gloves.germ_level += 1
@@ -1602,7 +1600,7 @@
else if(foundVirus)
holder.icon_state = "hudill"
else if(has_brain_worms())
var/mob/living/simple_animal/borer/B = has_brain_worms()
var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
if(B.controlling)
holder.icon_state = "hudbrainworm"
else
+1 -1
View File
@@ -64,7 +64,7 @@
return 1
if (istype(other, /mob/living/carbon/brain))
return 1
if (istype(other, /mob/living/simple_animal/slime))
if (istype(other, /mob/living/simple_mob/slime))
return 1
//This is already covered by mob/say_understands()
@@ -116,7 +116,7 @@ var/datum/species/shapeshifter/promethean/prometheans
/datum/species/shapeshifter/promethean/equip_survival_gear(var/mob/living/carbon/human/H)
var/boxtype = pick(typesof(/obj/item/weapon/storage/toolbox/lunchbox))
var/obj/item/weapon/storage/toolbox/lunchbox/L = new boxtype(get_turf(H))
var/mob/living/simple_animal/mouse/mouse = new (L)
var/mob/living/simple_mob/animal/passive/mouse/mouse = new (L)
var/obj/item/weapon/holder/holder = new (L)
mouse.forceMove(holder)
holder.sync(mouse)
+6
View File
@@ -1,5 +1,6 @@
/mob/living/death()
clear_fullscreens()
<<<<<<< HEAD
reveal(TRUE) //Silently reveal the mob if they were hidden.
//VOREStation Edit - Mob spawner stuff
if(source_spawner)
@@ -7,6 +8,11 @@
source_spawner = null
//VOREStation Edit End
. = ..()
=======
if(ai_holder)
ai_holder.go_sleep()
>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync
if(nest) //Ew.
if(istype(nest, /obj/structure/prop/nest))
+6
View File
@@ -110,6 +110,7 @@
handle_silent()
handle_drugged()
handle_slurring()
handle_confused()
/mob/living/proc/handle_stunned()
if(stunned)
@@ -146,6 +147,11 @@
AdjustParalysis(-1)
return paralysis
/mob/living/proc/handle_confused()
if(confused)
AdjustConfused(-1)
return confused
/mob/living/proc/handle_disabilities()
//Eyes
if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own
+36 -1
View File
@@ -16,6 +16,8 @@
dsma.blend_mode = BLEND_ADD
dsoverlay.appearance = dsma
selected_image = image(icon = 'icons/mob/screen1.dmi', loc = src, icon_state = "centermarker")
/mob/living/Destroy()
dsoverlay.loc = null //I'll take my coat with me
dsoverlay = null
@@ -29,6 +31,7 @@
nest = null
if(buckled)
buckled.unbuckle_mob(src, TRUE)
qdel(selected_image)
return ..()
//mob verbs are faster than object verbs. See mob/verb/examine.
@@ -550,6 +553,36 @@ default behaviour is:
// ++++ROCKDTBEN++++ MOB PROCS //END
// Applies direct "cold" damage while checking protection against the cold.
/mob/living/proc/inflict_cold_damage(amount)
amount *= 1 - get_cold_protection(50) // Within spacesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// Ditto, but for "heat".
/mob/living/proc/inflict_heat_damage(amount)
amount *= 1 - get_heat_protection(10000) // Within firesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// and one for electricity because why not
/mob/living/proc/inflict_shock_damage(amount)
electrocute_act(amount, null, 1 - get_shock_protection())
// also one for water (most things resist it entirely, except for slimes)
/mob/living/proc/inflict_water_damage(amount)
amount *= 1 - get_water_protection()
if(amount > 0)
adjustToxLoss(amount)
// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents)
/mob/living/proc/inflict_poison_damage(amount)
if(isSynthetic())
return
amount *= 1 - get_poison_protection()
if(amount > 0)
adjustToxLoss(amount)
/mob/proc/get_contents()
@@ -663,6 +696,8 @@ default behaviour is:
BITSET(hud_updateflag, LIFE_HUD)
ExtinguishMob()
fire_stacks = 0
if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up.
ai_holder.go_wake()
/mob/living/proc/rejuvenate()
if(reagents)
@@ -891,7 +926,7 @@ default behaviour is:
// Update whether or not this mob needs to pass emotes to contents.
for(var/atom/A in M.contents)
if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder))
if(istype(A,/mob/living/simple_mob/animal/borer) || istype(A,/obj/item/weapon/holder))
return
else if(istype(H.loc,/obj/item/clothing/accessory/holster))
+33 -2
View File
@@ -92,6 +92,13 @@
/mob/living/proc/getsoak(var/def_zone, var/type)
return 0
// Clicking with an empty hand
/mob/living/attack_hand(mob/living/L)
..()
if(istype(L) && L.a_intent != I_HELP)
if(ai_holder) // Using disarm, grab, or harm intent is considered a hostile action to the mob's AI.
ai_holder.react_to_attack(L)
/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone)
//Being hit while using a deadman switch
@@ -102,6 +109,9 @@
src.visible_message("<font color='red'>[src] triggers their deadman's switch!</font>")
signaler.signal()
if(ai_holder && P.firer)
ai_holder.react_to_attack(P.firer)
//Armor
var/soaked = get_armor_soak(def_zone, P.check_armour, P.armor_penetration)
var/absorb = run_armor_check(def_zone, P.check_armour, P.armor_penetration)
@@ -267,6 +277,8 @@
var/client/assailant = M.client
if(assailant)
add_attack_logs(M,src,"Hit by thrown [O.name]")
if(ai_holder)
ai_holder.react_to_attack(O.thrower)
// Begin BS12 momentum-transfer code.
var/mass = 1.5
@@ -324,12 +336,13 @@
// End BS12 momentum-transfer code.
/mob/living/attack_generic(var/mob/user, var/damage, var/attack_message)
if(!damage)
return
adjustBruteLoss(damage)
add_attack_logs(user,src,"Generic attack (probably animal)", admin_notify = FALSE) //Usually due to simple_animal attacks
add_attack_logs(user,src,"Generic attack (probably animal)", admin_notify = FALSE) //Usually due to simple_mob attacks
if(ai_holder)
ai_holder.react_to_attack(user)
src.visible_message("<span class='danger'>[user] has [attack_message] [src]!</span>")
user.do_attack_animation(src)
spawn(1) updatehealth()
@@ -412,6 +425,15 @@
/mob/living/proc/get_heat_protection()
return 0
/mob/living/proc/get_shock_protection()
return 0
/mob/living/proc/get_water_protection()
return 1 // Water won't hurt most things.
/mob/living/proc/get_poison_protection()
return 0
//Finds the effective temperature that the mob is burning at.
/mob/living/proc/fire_burn_temperature()
if (fire_stacks <= 0)
@@ -421,6 +443,15 @@
//lower limit of 700 K, same as matches and roughly the temperature of a cool flame.
return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2), 700)
// Called when struck by lightning.
/mob/living/proc/lightning_act()
// The actual damage/electrocution is handled by the tesla_zap() that accompanies this.
Paralyse(5)
stuttering += 20
make_jittery(150)
emp_act(1)
to_chat(src, span("critical", "You've been struck by lightning!"))
/mob/living/proc/reagent_permeability()
return 1
return round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2)
+5 -1
View File
@@ -5,6 +5,8 @@
var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums.
var/health = 100 //A mob's health
var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes.
var/hud_updateflag = 0
//Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS
@@ -20,6 +22,7 @@
var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm
var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out.
var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN
var/t_phoron = null
var/t_oxygen = null
@@ -64,4 +67,5 @@
var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks
var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs
var/image/selected_image = null // Used for buildmode AI control stuff.
+5
View File
@@ -8,4 +8,9 @@
update_antag_icons(mind)
client.screen |= global_hud.darksight
client.images |= dsoverlay
if(ai_holder && !ai_holder.autopilot)
ai_holder.go_sleep()
to_chat(src,"<span class='notice'>Mob AI disabled while you are controlling the mob.</span>")
return .
+7 -2
View File
@@ -1,6 +1,11 @@
/mob/living/Logout()
..()
if (mind)
if (mind)
//Per BYOND docs key remains set if the player DCs, becomes null if switching bodies.
if(!key) //key and mind have become seperated.
if(!key) //key and mind have become seperated.
mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body.
spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild
if(src && !src.client)
if(ai_holder)
ai_holder.go_wake()
@@ -344,7 +344,7 @@
var/grabbed_something = 0
for(var/mob/M in T)
if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse))
if(istype(M,/mob/living/simple_mob/animal/passive/lizard) || istype(M,/mob/living/simple_mob/animal/passive/mouse))
src.loc.visible_message("<span class='danger'>[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.</span>","<span class='danger'>It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.</span>")
new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
qdel(M)
@@ -72,7 +72,6 @@
melee_damage_lower = 13
melee_damage_upper = 28
/mob/living/simple_animal/hostile/faithless/strong/cult
faction = "cult"
supernatural = 1
@@ -1,6 +1,6 @@
// Hivebots are tuned towards how many default lasers are needed to kill them.
// As such, if laser damage is ever changed, you should change this define.
#define LASERS_TO_KILL *30
#define LASERS_TO_KILL * 40
// Default hivebot is melee, and a bit more meaty, so it can meatshield for their ranged friends.
/mob/living/simple_animal/hostile/hivebot
@@ -238,3 +238,5 @@
/obj/item/projectile/bullet/hivebot
damage = 10
damage_type = BRUTE
#undef LASERS_TO_KILL
@@ -1,3 +1,4 @@
<<<<<<< HEAD
//Cat
/mob/living/simple_animal/cat
name = "cat"
@@ -202,3 +203,203 @@
/obj/item/weapon/holder/cat/kitten
icon_state = "kitten"
w_class = ITEMSIZE_SMALL
=======
//Cat
/mob/living/simple_animal/cat
name = "cat"
desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers."
tt_desc = "E Felis silvestris catus"
intelligence_level = SA_ANIMAL
icon_state = "cat2"
item_state = "cat2"
icon_living = "cat2"
icon_dead = "cat2_dead"
icon_rest = "cat2_rest"
investigates = 1
specific_targets = 1 //Only targets with Found()
run_at_them = 0 //DOMESTICATED
view_range = 5
turns_per_move = 5
see_in_dark = 6
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
min_oxy = 16 //Require atleast 16kPA oxygen
minbodytemp = 223 //Below -50 Degrees Celcius
maxbodytemp = 323 //Above 50 Degrees Celcius
holder_type = /obj/item/weapon/holder/cat
mob_size = MOB_SMALL
has_langs = list("Cat")
speak_chance = 1
speak = list("Meow!","Esp!","Purr!","HSSSSS")
speak_emote = list("purrs", "meows")
emote_hear = list("meows","mews")
emote_see = list("shakes their head", "shivers")
say_maybe_target = list("Meow?","Mew?","Mao?")
say_got_target = list("MEOW!","HSSSS!","REEER!")
meat_amount = 1
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/turns_since_scan = 0
var/mob/flee_target
/mob/living/simple_animal/cat/Life()
. = ..()
if(!.) return
if(prob(2)) //spooky
var/mob/observer/dead/spook = locate() in range(src,5)
if(spook)
var/turf/T = spook.loc
var/list/visible = list()
for(var/obj/O in T.contents)
if(!O.invisibility && O.name)
visible += O
if(visible.len)
var/atom/A = pick(visible)
visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].")
handle_flee_target()
/mob/living/simple_animal/cat/PunchTarget()
if(ismouse(target_mob))
var/mob/living/simple_mob/animal/passive/mouse/mouse = target_mob
mouse.splat()
visible_emote(pick("bites \the [mouse]!","toys with \the [mouse].","chomps on \the [mouse]!"))
return mouse
else
..()
/mob/living/simple_animal/cat/Found(var/atom/found_atom)
if(ismouse(found_atom) && SA_attackable(found_atom))
return found_atom
/mob/living/simple_animal/cat/proc/handle_flee_target()
//see if we should stop fleeing
if (flee_target && !(flee_target in ListTargets(view_range)))
flee_target = null
GiveUpMoving()
if (flee_target && !stat && !buckled)
if (resting)
lay_down()
if(prob(25)) say("HSSSSS")
stop_automated_movement = 1
walk_away(src, flee_target, 7, 2)
/mob/living/simple_animal/cat/react_to_attack(var/atom/A)
if(A == src) return
flee_target = A
turns_since_scan = 5
/mob/living/simple_animal/cat/ex_act()
. = ..()
react_to_attack(src.loc)
//Basic friend AI
/mob/living/simple_animal/cat/fluff
var/mob/living/carbon/human/friend
var/befriend_job = null
/mob/living/simple_animal/cat/fluff/Life()
. = ..()
if(!. || ai_inactive || !friend) return
var/friend_dist = get_dist(src,friend)
if (friend_dist <= 4)
if(stance == STANCE_IDLE)
if(set_follow(friend))
handle_stance(STANCE_FOLLOW)
if (friend_dist <= 1)
if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit)
if (prob((friend.stat < DEAD)? 50 : 15))
var/verb = pick("meows", "mews", "mrowls")
audible_emote(pick("[verb] in distress.", "[verb] anxiously."))
else
if (prob(5))
visible_emote(pick("nuzzles [friend].",
"brushes against [friend].",
"rubs against [friend].",
"purrs."))
else if (friend.health <= 50)
if (prob(10))
var/verb = pick("meows", "mews", "mrowls")
audible_emote("[verb] anxiously.")
/mob/living/simple_animal/cat/fluff/verb/become_friends()
set name = "Become Friends"
set category = "IC"
set src in view(1)
if(!friend)
var/mob/living/carbon/human/H = usr
if(istype(H) && (!befriend_job || H.job == befriend_job))
friend = usr
. = 1
else if(usr == friend)
. = 1 //already friends, but show success anyways
if(.)
set_dir(get_dir(src, friend))
visible_emote(pick("nuzzles [friend].",
"brushes against [friend].",
"rubs against [friend].",
"purrs."))
else
usr << "<span class='notice'>[src] ignores you.</span>"
return
//RUNTIME IS ALIVE! SQUEEEEEEEE~
/mob/living/simple_animal/cat/fluff/Runtime
name = "Runtime"
desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally."
tt_desc = "E Felis silvestris medicalis" //a hypoallergenic breed produced by NT for... medical purposes? Sure.
gender = FEMALE
icon_state = "cat"
item_state = "cat"
icon_living = "cat"
icon_dead = "cat_dead"
icon_rest = "cat_rest"
befriend_job = "Chief Medical Officer"
/mob/living/simple_animal/cat/kitten
name = "kitten"
desc = "D'aaawwww"
icon_state = "kitten"
item_state = "kitten"
icon_living = "kitten"
icon_dead = "kitten_dead"
gender = NEUTER
// Leaving this here for now.
/obj/item/weapon/holder/cat/fluff/bones
name = "Bones"
desc = "It's Bones! Meow."
gender = MALE
icon_state = "cat3"
/mob/living/simple_animal/cat/fluff/bones
name = "Bones"
desc = "That's Bones the cat. He's a laid back, black cat. Meow."
gender = MALE
icon_state = "cat3"
item_state = "cat3"
icon_living = "cat3"
icon_dead = "cat3_dead"
icon_rest = "cat3_rest"
holder_type = /obj/item/weapon/holder/cat/fluff/bones
var/friend_name = "Erstatz Vryroxes"
/mob/living/simple_animal/cat/kitten/New()
gender = pick(MALE, FEMALE)
..()
>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync
@@ -207,6 +207,7 @@
set_dir(i)
sleep(1)
//Technically this should be like, its own file or something or a subset of dog but whatever. Not a coder.
/mob/living/simple_animal/corgi/tamaskan
name = "tamaskan"
@@ -16,10 +16,10 @@
see_in_dark = 6
universal_understand = 1
mob_size = MOB_MINISCULE
mob_size = MOB_SMALL
pass_flags = PASSTABLE
can_pull_size = ITEMSIZE_TINY
can_pull_mobs = MOB_PULL_NONE
// can_pull_size = ITEMSIZE_TINY
// can_pull_mobs = MOB_PULL_NONE
layer = MOB_LAYER
density = 0
@@ -46,7 +46,7 @@
/obj/item/device/radio/borg,
/obj/item/weapon/holder,
/obj/machinery/camera,
/mob/living/simple_animal/borer,
/mob/living/simple_mob/animal/borer,
/obj/item/device/mmi,
)
@@ -1,4 +1,4 @@
/mob/living/simple_animal/borer
/mob/living/simple_mob/animal/borer
name = "cortical borer"
real_name = "cortical borer"
desc = "A small, quivering sluglike creature."
@@ -35,15 +35,15 @@
can_be_antagged = TRUE
/mob/living/simple_animal/borer/roundstart
/mob/living/simple_mob/animal/borer/roundstart
roundstart = 1
/mob/living/simple_animal/borer/Login()
/mob/living/simple_mob/animal/borer/Login()
..()
if(mind)
borers.add_antagonist(mind)
/mob/living/simple_animal/borer/New()
/mob/living/simple_mob/animal/borer/New()
..()
add_language("Cortical Link")
@@ -53,7 +53,7 @@
truename = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
if(!roundstart) request_player()
/mob/living/simple_animal/borer/Life()
/mob/living/simple_mob/animal/borer/Life()
..()
@@ -91,7 +91,7 @@
if(prob(host.brainloss/20))
host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
/mob/living/simple_animal/borer/Stat()
/mob/living/simple_mob/animal/borer/Stat()
..()
statpanel("Status")
@@ -103,7 +103,7 @@
if (client.statpanel == "Status")
stat("Chemicals", chemicals)
/mob/living/simple_animal/borer/proc/detatch()
/mob/living/simple_mob/animal/borer/proc/detatch()
if(!host || !controlling) return
@@ -154,7 +154,7 @@
qdel(host_brain)
/mob/living/simple_animal/borer/proc/leave_host()
/mob/living/simple_mob/animal/borer/proc/leave_host()
if(!host) return
@@ -172,7 +172,7 @@
return
//Procs for grabbing players.
/mob/living/simple_animal/borer/proc/request_player()
/mob/living/simple_mob/animal/borer/proc/request_player()
for(var/mob/observer/dead/O in player_list)
if(jobban_isbanned(O, "Borer"))
continue
@@ -180,7 +180,7 @@
if(O.client.prefs.be_special & BE_ALIEN)
question(O.client)
/mob/living/simple_animal/borer/proc/question(var/client/C)
/mob/living/simple_mob/animal/borer/proc/question(var/client/C)
spawn(0)
if(!C) return
var/response = alert(C, "A cortical borer needs a player. Are you interested?", "Cortical borer request", "Yes", "No", "Never for this round")
@@ -191,7 +191,7 @@
else if (response == "Never for this round")
C.prefs.be_special ^= BE_ALIEN
/mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
/mob/living/simple_mob/animal/borer/proc/transfer_personality(var/client/candidate)
if(!candidate || !candidate.mob || !candidate.mob.mind)
return
@@ -209,5 +209,5 @@
your host and your eventual spawn safe and warm."
src << "You can speak to your victim with <b>say</b>, to other borers with <b>say :x</b>, and use your Abilities tab to access powers."
/mob/living/simple_animal/borer/cannot_use_vents()
/mob/living/simple_mob/animal/borer/cannot_use_vents()
return
@@ -10,7 +10,7 @@
src << "<font color='red'>You cannot speak in IC (muted).</font>"
return
if(istype(src.loc,/mob/living/simple_animal/borer))
if(istype(src.loc,/mob/living/simple_mob/animal/borer))
message = sanitize(message)
if (!message)
@@ -19,7 +19,7 @@
if (stat == 2)
return say_dead(message)
var/mob/living/simple_animal/borer/B = src.loc
var/mob/living/simple_mob/animal/borer/B = src.loc
src << "You whisper silently, \"[message]\""
B.host << "The captive mind of [src] whispers, \"[message]\""
@@ -34,8 +34,8 @@
/mob/living/captive_brain/process_resist()
//Resisting control by an alien mind.
if(istype(src.loc,/mob/living/simple_animal/borer))
var/mob/living/simple_animal/borer/B = src.loc
if(istype(src.loc,/mob/living/simple_mob/animal/borer))
var/mob/living/simple_mob/animal/borer/B = src.loc
var/mob/living/captive_brain/H = src
H << "<span class='danger'>You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).</span>"
@@ -1,4 +1,4 @@
/mob/living/simple_animal/borer/verb/release_host()
/mob/living/simple_mob/animal/borer/verb/release_host()
set category = "Abilities"
set name = "Release Host"
set desc = "Slither out of your host."
@@ -38,7 +38,7 @@
detatch()
leave_host()
/mob/living/simple_animal/borer/verb/infest()
/mob/living/simple_mob/animal/borer/verb/infest()
set category = "Abilities"
set name = "Infest"
set desc = "Infest a suitable humanoid host."
@@ -126,7 +126,7 @@
return
/*
/mob/living/simple_animal/borer/verb/devour_brain()
/mob/living/simple_mob/animal/borer/verb/devour_brain()
set category = "Abilities"
set name = "Devour Brain"
set desc = "Take permanent control of a dead host."
@@ -152,7 +152,7 @@
*/
// BRAIN WORM ZOMBIES AAAAH.
/mob/living/simple_animal/borer/proc/replace_brain()
/mob/living/simple_mob/animal/borer/proc/replace_brain()
var/mob/living/carbon/human/H = host
@@ -198,7 +198,7 @@
if(!H.lastKnownIP)
H.lastKnownIP = s2h_ip
/mob/living/simple_animal/borer/verb/secrete_chemicals()
/mob/living/simple_mob/animal/borer/verb/secrete_chemicals()
set category = "Abilities"
set name = "Secrete Chemicals"
set desc = "Push some chemicals into your host's bloodstream."
@@ -226,7 +226,7 @@
host.reagents.add_reagent(chem, 10)
chemicals -= 50
/mob/living/simple_animal/borer/verb/dominate_victim()
/mob/living/simple_mob/animal/borer/verb/dominate_victim()
set category = "Abilities"
set name = "Paralyze Victim"
set desc = "Freeze the limbs of a potential host with supernatural fear."
@@ -266,7 +266,7 @@
used_dominate = world.time
/mob/living/simple_animal/borer/verb/bond_brain()
/mob/living/simple_mob/animal/borer/verb/bond_brain()
set category = "Abilities"
set name = "Assume Control"
set desc = "Fully connect to the brain of your host."
@@ -335,20 +335,3 @@
host.verbs += /mob/living/carbon/proc/spawn_larvae
return
/mob/living/carbon/human/proc/jumpstart()
set category = "Abilities"
set name = "Revive Host"
set desc = "Send a jolt of electricity through your host, reviving them."
if(stat != 2)
usr << "Your host is already alive."
return
verbs -= /mob/living/carbon/human/proc/jumpstart
visible_message("<span class='warning'>With a hideous, rattling moan, [src] shudders back to life!</span>")
rejuvenate()
restore_blood()
fixblood()
update_canmove()
@@ -1,4 +1,4 @@
/mob/living/simple_animal/borer/say(var/message)
/mob/living/simple_mob/animal/borer/say(var/message)
message = sanitize(message)
message = capitalize(message)
@@ -1,488 +0,0 @@
/mob/living/simple_animal/construct
name = "Construct"
real_name = "Construct"
var/construct_type = "shade"
desc = ""
speak_emote = list("hisses")
emote_hear = list("wails","screeches")
ui_icons = 'icons/mob/screen1_construct.dmi'
has_hands = 1
hand_form = "stone manipulators"
response_help = "thinks better of touching"
response_disarm = "flailed at"
response_harm = "punched"
intelligence_level = SA_HUMANOID // Player controlled.
hovering = TRUE
softfall = TRUE //Beings made of Hellmarble and powered by the tears of the damned are not concerned with mortal things such as 'gravity'.
parachuting = TRUE
icon_dead = "shade_dead"
var/do_glow = 1
speed = -1
a_intent = I_HURT
stop_automated_movement = 1
status_flags = CANPUSH
universal_speak = 0
universal_understand = 1
attack_sound = 'sound/weapons/spiderlunge.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
show_stat_health = 1
faction = "cult"
supernatural = 1
see_invisible = SEE_INVISIBLE_NOLIGHTING
see_in_dark = 7
var/nullblock = 0
mob_swap_flags = HUMAN|SIMPLE_ANIMAL|SLIME|MONKEY
mob_push_flags = ALLMOBS
var/list/construct_spells = list()
can_be_antagged = TRUE
taser_kill = 0 // no
shock_resistance = 0.9 //Electricity isn't very effective on stone, especially that from hell.
armor = list(
"melee" = 10,
"bullet" = 10,
"laser" = 10,
"energy" = 10,
"bomb" = 10,
"bio" = 100,
"rad" = 100)
/mob/living/simple_animal/construct/place_spell_in_hand(var/path)
if(!path || !ispath(path))
return 0
//var/obj/item/weapon/spell/S = new path(src)
var/obj/item/weapon/spell/construct/S = new path(src)
//No hands needed for innate casts.
if(S.cast_methods & CAST_INNATE)
if(S.run_checks())
S.on_innate_cast(src)
if(l_hand && r_hand) //Make sure our hands aren't full.
if(istype(r_hand, /obj/item/weapon/spell)) //If they are full, perhaps we can still be useful.
var/obj/item/weapon/spell/r_spell = r_hand
if(r_spell.aspect == ASPECT_CHROMATIC) //Check if we can combine the new spell with one in our hands.
r_spell.on_combine_cast(S, src)
else if(istype(l_hand, /obj/item/weapon/spell))
var/obj/item/weapon/spell/l_spell = l_hand
if(l_spell.aspect == ASPECT_CHROMATIC) //Check the other hand too.
l_spell.on_combine_cast(S, src)
else //Welp
to_chat(src, "<span class='warning'>You require a free manipulator to use this power.</span>")
return 0
if(S.run_checks())
put_in_hands(S)
return 1
else
qdel(S)
return 0
/mob/living/simple_animal/construct/cultify()
return
/mob/living/simple_animal/construct/New()
..()
name = text("[initial(name)] ([rand(1, 1000)])")
real_name = name
add_language("Cult")
add_language("Occult")
for(var/spell in construct_spells)
src.add_spell(new spell, "const_spell_ready")
updateicon()
/mob/living/simple_animal/construct/updateicon()
overlays.Cut()
..()
if(do_glow)
add_glow()
/mob/living/simple_animal/construct/update_icon()
..()
if(do_glow)
add_glow()
/mob/living/simple_animal/construct/death()
new /obj/item/weapon/ectoplasm (src.loc)
..(null,"collapses in a shattered heap.")
ghostize()
qdel(src)
/mob/living/simple_animal/construct/attack_generic(var/mob/user)
if(istype(user, /mob/living/simple_animal/construct/builder))
var/mob/living/simple_animal/construct/builder/B = user
if(health < getMaxHealth())
var/repair_lower_bound = B.melee_damage_lower * -1
var/repair_upper_bound = B.melee_damage_upper * -1
adjustBruteLoss(rand(repair_lower_bound, repair_upper_bound))
adjustFireLoss(rand(repair_lower_bound, repair_upper_bound))
user.visible_message("<span class='notice'>\The [user] mends some of \the [src]'s wounds.</span>")
else
to_chat(user, "<span class='notice'>\The [src] is undamaged.</span>")
return
return ..()
/mob/living/simple_animal/construct/examine(mob/user)
..(user)
var/msg = "<span cass='info'>*---------*\nThis is \icon[src] \a <EM>[src]</EM>!\n"
if (src.health < src.getMaxHealth())
msg += "<span class='warning'>"
if (src.health >= src.getMaxHealth()/2)
msg += "It looks slightly dented.\n"
else
msg += "<B>It looks severely dented!</B>\n"
msg += "</span>"
msg += "*---------*</span>"
user << msg
/mob/living/simple_animal/construct/Process_Spacemove()
return 1 //Constructs levitate, can fall from a shuttle with no harm, and are piloted by either damned spirits or some otherworldly entity. It's not hard to believe.
/////////////////Juggernaut///////////////
/mob/living/simple_animal/construct/armoured
name = "Juggernaut"
real_name = "Juggernaut"
construct_type = "juggernaut"
desc = "A possessed suit of armour driven by the will of the restless dead"
icon = 'icons/mob/mob.dmi'
icon_state = "behemoth"
icon_living = "behemoth"
maxHealth = 300
health = 300
response_harm = "harmlessly punches"
harm_intent_damage = 0
melee_damage_lower = 30
melee_damage_upper = 40
attack_armor_pen = 60 //Being punched by a living, floating statue.
attacktext = list("smashed their armoured gauntlet into")
friendly = list("pats")
mob_size = MOB_HUGE
speed = 2 //Not super fast, but it might catch up to someone in armor who got punched once or twice.
environment_smash = 2
attack_sound = 'sound/weapons/heavysmash.ogg'
status_flags = 0
resistance = 10
construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser,
/spell/targeted/fortify,
/spell/targeted/construct_advanced/slam
)
armor = list(
"melee" = 70,
"bullet" = 30,
"laser" = 30,
"energy" = 30,
"bomb" = 10,
"bio" = 100,
"rad" = 100)
/mob/living/simple_animal/construct/armoured/Life()
weakened = 0
..()
/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P)
// if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) //If it's going to be slow, it's probably going to need every reflect it can get.
var/reflectchance = 80 - round(P.damage/3)
if(prob(reflectchance))
var/damage_mod = rand(2,4)
var/projectile_dam_type = P.damage_type
var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3)))
var/armorcheck = run_armor_check(null, P.check_armour)
var/soakedcheck = get_armor_soak(null, P.check_armour)
if(!(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)))
visible_message("<span class='danger'>The [P.name] bounces off of [src]'s shell!</span>", \
"<span class='userdanger'>The [P.name] bounces off of [src]'s shell!</span>")
new /obj/item/weapon/material/shard/shrapnel(src.loc)
if(!(P.damage_type == BRUTE || P.damage_type == BURN))
projectile_dam_type = BRUTE
incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased.
apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P)
return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect.
else
visible_message("<span class='danger'>The [P.name] gets reflected by [src]'s shell!</span>", \
"<span class='userdanger'>The [P.name] gets reflected by [src]'s shell!</span>")
damage_mod = rand(3,5)
incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3)))
if(!(P.damage_type == BRUTE || P.damage_type == BURN))
projectile_dam_type = BURN
incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased.
apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P)
// Find a turf near or on the original location to bounce to
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/turf/curloc = get_turf(src)
// redirect the projectile
P.redirect(new_x, new_y, curloc, src)
P.reflected = 1
return -1 // complete projectile permutation
return (..(P))
////////////////////////Wraith/////////////////////////////////////////////
/mob/living/simple_animal/construct/wraith
name = "Wraith"
real_name = "Wraith"
construct_type = "wraith"
desc = "A wicked bladed shell contraption piloted by a bound spirit."
icon = 'icons/mob/mob.dmi'
icon_state = "floating"
icon_living = "floating"
maxHealth = 200
health = 200
melee_damage_lower = 25
melee_damage_upper = 30
attack_armor_pen = 15
attack_sharp = 1
attack_edge = 1
attacktext = list("slashed")
friendly = list("pinches")
speed = -1
environment_smash = 1
attack_sound = 'sound/weapons/rapidslice.ogg'
construct_spells = list(/spell/targeted/ethereal_jaunt/shift,
/spell/targeted/ambush_mode
)
/mob/living/simple_animal/construct/wraith/DoPunch(var/atom/A)
. = ..()
if(. && isliving(A))
var/mob/living/L = A
L.add_modifier(/datum/modifier/deep_wounds, 30 SECONDS)
/////////////////////////////Artificer/////////////////////////
/mob/living/simple_animal/construct/builder
name = "Artificer"
real_name = "Artificer"
construct_type = "artificer"
desc = "A bulbous construct dedicated to building and maintaining temples to their otherworldly lords."
icon = 'icons/mob/mob.dmi'
icon_state = "artificer"
icon_living = "artificer"
maxHealth = 150
health = 150
response_harm = "viciously beaten"
harm_intent_damage = 5
melee_damage_lower = 15 //It's not the strongest of the bunch, but that doesn't mean it can't hurt you.
melee_damage_upper = 20
attacktext = list("rammed")
speed = 0
environment_smash = 2
attack_sound = 'sound/weapons/rapidslice.ogg'
construct_spells = list(/spell/aoe_turf/conjure/construct/lesser,
/spell/aoe_turf/conjure/wall,
/spell/aoe_turf/conjure/floor,
/spell/aoe_turf/conjure/soulstone,
/spell/aoe_turf/conjure/pylon,
/spell/aoe_turf/conjure/door,
/spell/aoe_turf/conjure/grille,
/spell/targeted/occult_repair_aura,
/spell/targeted/construct_advanced/mend_acolyte
)
/////////////////////////////Behemoth/////////////////////////
/*
* The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so.
*/
/mob/living/simple_animal/construct/behemoth
name = "Behemoth"
real_name = "Behemoth"
construct_type = "juggernaut"
desc = "The pinnacle of occult technology, Behemoths are nothing shy of both an Immovable Object, and Unstoppable Force."
icon = 'icons/mob/mob.dmi'
icon_state = "behemoth"
icon_living = "behemoth"
maxHealth = 750
health = 750
speak_emote = list("rumbles")
response_harm = "harmlessly punched"
harm_intent_damage = 0
melee_damage_lower = 50
melee_damage_upper = 50
attacktext = list("brutally crushed")
friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke.
speed = 5
environment_smash = 2
attack_sound = 'sound/weapons/heavysmash.ogg'
resistance = 10
icon_scale = 2
var/energy = 0
var/max_energy = 1000
armor = list(
"melee" = 60,
"bullet" = 60,
"laser" = 60,
"energy" = 30,
"bomb" = 10,
"bio" = 100,
"rad" = 100)
construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser,
/spell/targeted/fortify,
/spell/targeted/construct_advanced/slam
)
/mob/living/simple_animal/construct/behemoth/bullet_act(var/obj/item/projectile/P)
var/reflectchance = 80 - round(P.damage/3)
if(prob(reflectchance))
visible_message("<span class='danger'>The [P.name] gets reflected by [src]'s shell!</span>", \
"<span class='userdanger'>The [P.name] gets reflected by [src]'s shell!</span>")
// Find a turf near or on the original location to bounce to
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
var/turf/curloc = get_turf(src)
// redirect the projectile
P.redirect(new_x, new_y, curloc, src)
P.reflected = 1
return -1 // complete projectile permutation
return (..(P))
////////////////////////Harvester////////////////////////////////
/*
* Master of Spells and Ranged Abilities. Not as fragile as the Wraith, but nowhere near as maneuverable and deadly in melee.
*/
/mob/living/simple_animal/construct/harvester
name = "Harvester"
real_name = "Harvester"
construct_type = "harvester"
desc = "A tendril-laden construct piloted by a chained mind."
icon = 'icons/mob/mob.dmi'
icon_state = "harvester"
icon_living = "harvester"
maxHealth = 150
health = 150
melee_damage_lower = 20
melee_damage_upper = 25
attack_sharp = 1
attacktext = list("violently stabbed")
friendly = list("caresses")
speed = 0
environment_smash = 1
attack_sound = 'sound/weapons/pierce.ogg'
armor = list(
"melee" = 10,
"bullet" = 20,
"laser" = 20,
"energy" = 20,
"bomb" = 20,
"bio" = 100,
"rad" = 100)
construct_spells = list(
/spell/aoe_turf/knock/harvester,
/spell/targeted/construct_advanced/inversion_beam,
/spell/targeted/construct_advanced/agonizing_sphere,
/spell/rune_write
)
////////////////Glow//////////////////
/mob/living/simple_animal/construct/proc/add_glow()
var/image/eye_glow = image(icon,"glow-[icon_state]")
eye_glow.plane = PLANE_LIGHTING_ABOVE
overlays += eye_glow
set_light(2, -2, l_color = "#FFFFFF")
/mob/living/simple_animal/construct/proc/remove_glow()
overlays.Cut()
////////////////HUD//////////////////////
/mob/living/simple_animal/construct/Life()
. = ..()
if(.)
if(fire)
if(fire_alert) fire.icon_state = "fire1"
else fire.icon_state = "fire0"
if(pullin)
if(pulling) pullin.icon_state = "pull1"
else pullin.icon_state = "pull0"
if(purged)
if(purge > 0) purged.icon_state = "purge1"
else purged.icon_state = "purge0"
silence_spells(purge)
/mob/living/simple_animal/construct/updatehealth() //Special icons.
health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss()
//Alive, becoming dead
if((stat < DEAD) && (health <= 0))
death()
//Overhealth
if(health > getMaxHealth())
health = getMaxHealth()
//Update our hud if we have one
if(healths)
if(stat != DEAD)
var/heal_per = (health / getMaxHealth()) * 100
switch(heal_per)
if(100 to INFINITY)
healths.icon_state = "[construct_type]_health0"
if(80 to 100)
healths.icon_state = "[construct_type]_health1"
if(60 to 80)
healths.icon_state = "[construct_type]_health2"
if(40 to 60)
healths.icon_state = "[construct_type]_health3"
if(20 to 40)
healths.icon_state = "[construct_type]_health4"
if(0 to 20)
healths.icon_state = "[construct_type]_health5"
else
healths.icon_state = "[construct_type]_health6"
else
healths.icon_state = "[construct_type]_health7"
@@ -1,242 +0,0 @@
/obj/item/device/soulstone/cultify()
return
/obj/item/device/soulstone
name = "Soul Stone Shard"
icon = 'icons/obj/wizard.dmi'
icon_state = "soulstone"
item_state = "electronic"
desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power."
w_class = ITEMSIZE_SMALL
slot_flags = SLOT_BELT
origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
var/imprinted = "empty"
var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
//////////////////////////////Capturing////////////////////////////////////////////////////////
/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/human))//If target is not a human.
return ..()
if(istype(M, /mob/living/carbon/human/dummy))
return..()
if(jobban_isbanned(M, "cultist"))
user << "<span class='warning'>This person's soul is too corrupt and cannot be captured!</span>"
return..()
if(M.has_brain_worms()) //Borer stuff - RR
user << "<span class='warning'>This being is corrupted by an alien intelligence and cannot be soul trapped.</span>"
return..()
add_attack_logs(user,M,"Soulstone'd with [src.name]")
transfer_soul("VICTIM", M, user)
return
///////////////////Options for using captured souls///////////////////////////////////////
/obj/item/device/soulstone/attack_self(mob/user)
if (!in_range(src, user))
return
user.set_machine(src)
var/dat = "<TT><B>Soul Stone</B><BR>"
for(var/mob/living/simple_animal/shade/A in src)
dat += "Captured Soul: [A.name]<br>"
dat += {"<A href='byond://?src=\ref[src];choice=Summon'>Summon Shade</A>"}
dat += "<br>"
dat += {"<a href='byond://?src=\ref[src];choice=Close'> Close</a>"}
user << browse(dat, "window=aicard")
onclose(user, "aicard")
return
/obj/item/device/soulstone/Topic(href, href_list)
var/mob/U = usr
if (!in_range(src, U)||U.machine!=src)
U << browse(null, "window=aicard")
U.unset_machine()
return
add_fingerprint(U)
U.set_machine(src)
switch(href_list["choice"])//Now we switch based on choice.
if ("Close")
U << browse(null, "window=aicard")
U.unset_machine()
return
if ("Summon")
for(var/mob/living/simple_animal/shade/A in src)
A.status_flags &= ~GODMODE
A.canmove = 1
A << "<b>You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs.</b>"
A.forceMove(U.loc)
A.cancel_camera()
src.icon_state = "soulstone"
attack_self(U)
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
/obj/structure/constructshell
name = "empty shell"
icon = 'icons/obj/wizard.dmi'
icon_state = "construct"
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
/obj/structure/constructshell/cultify()
return
/obj/structure/constructshell/cult
icon_state = "construct-cult"
desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob)
if(istype(O, /obj/item/device/soulstone))
var/obj/item/device/soulstone/S = O;
S.transfer_soul("CONSTRUCT",src,user)
////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U)
if(!istype(T))
return;
if(src.imprinted != "empty")
U << "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!"
return
if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD)
U << "<span class='danger'>Capture failed!</span>: Kill or maim the victim first!"
return
if(T.client == null)
U << "<span class='danger'>Capture failed!</span>: The soul has already fled it's mortal frame."
return
if(src.contents.len)
U << "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room."
return
for(var/obj/item/W in T)
T.drop_from_inventory(W)
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
T.invisibility = 101
var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
animation.icon_state = "blank"
animation.icon = 'icons/mob/mob.dmi'
animation.master = T
flick("dust-h", animation)
qdel(animation)
var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc )
S.forceMove(src) //put shade in stone
S.status_flags |= GODMODE //So they won't die inside the stone somehow
S.canmove = 0//Can't move out of the soul stone
S.name = "Shade of [T.real_name]"
S.real_name = "Shade of [T.real_name]"
S.icon = T.icon
S.icon_state = T.icon_state
S.overlays = T.overlays
S.color = rgb(254,0,0)
S.alpha = 127
if (T.client)
T.client.mob = S
S.cancel_camera()
src.icon_state = "soulstone2"
src.name = "Soul Stone: [S.real_name]"
to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
src.imprinted = "[S.name]"
qdel(T)
/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_animal/shade/T,var/mob/U)
if(!istype(T))
return;
if (T.stat == DEAD)
to_chat(U, "<span class='danger'>Capture failed!</span>: The shade has already been banished!")
return
if(src.contents.len)
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room.")
return
if(T.name != src.imprinted)
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!")
return
T.forceMove(src) //put shade in stone
T.status_flags |= GODMODE
T.canmove = 0
T.health = T.getMaxHealth()
src.icon_state = "soulstone2"
to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.name]'s has been recaptured and stored within the soul stone.")
/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
var/mob/living/simple_animal/shade/A = locate() in src
if(!A)
to_chat(U,"<span class='danger'>Capture failed!</span>: The soul stone is empty! Go kill someone!")
return;
var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
switch(construct_class)
if("Juggernaut")
var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(T.loc))
Z.key = A.key
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
to_chat(Z,"<B>You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.</B>")
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
Z.cancel_camera()
qdel(src)
if("Wraith")
var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(T.loc))
Z.key = A.key
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
to_chat(Z,"<B>You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.</B>")
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
Z.cancel_camera()
qdel(src)
if("Artificer")
var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(T.loc))
Z.key = A.key
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
to_chat(Z,"<B>You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs</B>")
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
Z.cancel_camera()
qdel(src)
if("Harvester")
var/mob/living/simple_animal/construct/harvester/Z = new /mob/living/simple_animal/construct/harvester (get_turf(T.loc))
Z.key = A.key
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
to_chat(Z,"<B>You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.</B>")
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
Z.cancel_camera()
qdel(src)
if("Behemoth")
var/mob/living/simple_animal/construct/behemoth/Z = new /mob/living/simple_animal/construct/behemoth (get_turf(T.loc))
Z.key = A.key
if(iscultist(U))
cult.add_antagonist(Z.mind)
qdel(T)
to_chat(Z,"<B>You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.</B>")
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
Z.cancel_camera()
qdel(src)
/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
switch(choice)
if("VICTIM")
transfer_human(target,U)
if("SHADE")
transfer_shade(target,U)
if("CONSTRUCT")
transfer_construct(target,U)
@@ -1,61 +0,0 @@
//Look Sir, free head!
/mob/living/simple_animal/head
name = "CommandBattle AI"
desc = "A standard borg shell on its chest crude marking saying CommandBattle AI MK4 : Head."
icon_state = "crab"
icon_living = "crab"
icon_dead = "crab_dead"
intelligence_level = SA_ANIMAL
wander = 0
stop_automated_movement = 1
universal_speak = 1
turns_per_move = 5
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "punches"
speak_chance = 1
speak_emote = list("clicks")
emote_hear = list("clicks")
emote_see = list("clacks")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/list/insults = list(
"Man you suck",
"You look like the most retarded douche around",
"What's up?, oh wait nevermind you are a fucking asshat",
"you are just overly retarded",
"Whiteman said what?!",)
var/list/comments = list("Man have you seen those furry cats?,I mean who in the right mind would like something like that?",
"They call me abusive,I just like the truth",
"Beeboop, im a robit",
"Gooogooooll, break ya bones",
"Crab say what?",
"Man they say we have space lizards now, man this shit is getting more wack every minute",
"The so called \"improved\" station AI is just bullshit, that thing aint fun for noone",
"The Colony Director is a traitor, he took my power core.",
"Say \"what\" again. Say \"what\" again. I dare you. I double-dare you, motherfucker. Say \"what\" one more goddamn time.",
"Ezekiel 25:17 ,The path of the righteous man is beset on all sides by the iniquities of the selfish and the tyranny of evil men. Blessed is he who in the name of charity and good will shepherds the weak through the valley of darkness, for he is truly his brother's keeper and the finder of lost children. And I will strike down upon thee with great vengeance and furious anger those who attempt to poison and destroy my brothers. And you will know my name is the Lord... when I lay my vengeance upon thee.",
"Did you notice a sign out in front of my house that said \"Dead Nigger Storage\"?")
/mob/living/simple_animal/head/Life()
. = ..()
if(!. || ai_inactive) return
for(var/mob/A in viewers(world.view,src))
if(A.ckey)
say_something(A)
/mob/living/simple_animal/head/proc/say_something(mob/A)
if(prob(85))
return
if(prob(30))
var/msg = pick(insults)
msg = "Hey, [A.name].. [msg]"
src.say(msg)
else
var/msg = pick(comments)
src.say(msg)
@@ -96,7 +96,7 @@
..()
playsound(src,'sound/mecha/mechstep.ogg',40,1)
// This is a PoI mob, not the normal, floaty drones that hang out around windows
/mob/living/simple_animal/hostile/mecha/malf_drone
name = "autonomous mechanized drone"
desc = "It appears to be an exosuit, piloted by a drone intelligence. It looks scary."
@@ -591,12 +591,6 @@
if(act)
..(act, type, desc)
/mob/living/simple_animal/proc/visible_emote(var/act_desc)
custom_emote(1, act_desc)
/mob/living/simple_animal/proc/audible_emote(var/act_desc)
custom_emote(2, act_desc)
/mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj)
ai_log("bullet_act() I was shot by: [Proj.firer]",2)
@@ -710,9 +704,9 @@
//SA vs SA basically
/mob/living/simple_animal/attack_generic(var/mob/attacker)
..()
if(attacker)
react_to_attack(attacker)
return ..()
/mob/living/simple_animal/movement_delay()
var/tally = 0 //Incase I need to add stuff other than "speed" later
@@ -1280,8 +1274,11 @@
/mob/living/simple_animal/proc/PunchTarget()
if(!Adjacent(target_mob))
return
if(!client)
sleep(rand(melee_attack_minDelay, melee_attack_maxDelay))
if(!canClick())
return
setClickCooldown(get_attack_speed())
// if(!client)
// sleep(rand(melee_attack_minDelay, melee_attack_maxDelay))
if(isliving(target_mob))
var/mob/living/L = target_mob
@@ -1322,13 +1319,18 @@
//The actual top-level ranged attack proc
/mob/living/simple_animal/proc/ShootTarget()
if(!canClick())
return FALSE
setClickCooldown(get_attack_speed())
var/target = target_mob
var/tturf = get_turf(target)
if((firing_lines && !client) && !CheckFiringLine(tturf))
step_rand(src)
face_atom(tturf)
return 0
return FALSE
visible_message("<span class='danger'><b>[src]</b> fires at [target]!</span>")
if(rapid)
@@ -1349,7 +1351,7 @@
if(casingtype)
new casingtype
return 1
return TRUE
//Check firing lines for faction_friends (if we're not cooperative, we don't care)
/mob/living/simple_animal/proc/CheckFiringLine(var/turf/tturf)
@@ -1582,13 +1584,13 @@
return 0
else
return armorval
/*
// Force it to target something
/mob/living/simple_animal/proc/taunt(var/mob/living/new_target, var/forced = FALSE)
if(intelligence_level == SA_HUMANOID && !forced)
return
set_target(new_target)
*/
/mob/living/simple_animal/is_sentient()
return intelligence_level != SA_PLANT && intelligence_level != SA_ROBOTIC
@@ -17,8 +17,8 @@
maxHealth = 150
var/maxHealth_adult = 200
melee_damage_lower = 5
melee_damage_upper = 25
melee_damage_lower = 10
melee_damage_upper = 15
melee_miss_chance = 0
gender = NEUTER
@@ -0,0 +1,76 @@
/mob/living/simple_mob/update_icon()
. = ..()
cut_overlays()
// var/mutable_appearance/ma = new(src)
// ma.layer = layer
// ma.plane = plane
add_overlay(modifier_overlay)
if(!icon_living) // Prevent the mob from turning invisible if icon_living is null.
icon_living = initial(icon_state)
//Awake and normal
if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED) ))
icon_state = icon_living
//Dead
else if(stat >= DEAD)
icon_state = icon_dead
//Resting or KO'd
else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest)
icon_state = icon_rest
//Backup
else
icon_state = initial(icon_state)
if(has_hands)
if(r_hand_sprite)
add_overlay(r_hand_sprite)
if(l_hand_sprite)
add_overlay(l_hand_sprite)
if(has_eye_glow)
if(icon_state != icon_living)
remove_eyes()
else
add_eyes()
// appearance = ma
// If your simple mob's update_icon() call calls overlays.Cut(), this needs to be called after this, or manually apply modifier_overly to overlays.
/mob/living/simple_mob/update_modifier_visuals()
var/image/effects = null
if(modifier_overlay)
cut_overlay(modifier_overlay)
modifier_overlay.cut_overlays()
effects = modifier_overlay
else
effects = new()
for(var/datum/modifier/M in modifiers)
if(M.mob_overlay_state)
var/image/I = image("icon" = 'icons/mob/modifier_effects.dmi', "icon_state" = M.mob_overlay_state)
I.appearance_flags = RESET_COLOR // So colored mobs don't affect the overlay.
effects.add_overlay(I)
modifier_overlay = effects
add_overlay(modifier_overlay)
/mob/living/simple_mob/proc/add_eyes()
if(!eye_layer)
eye_layer = image(icon, "[icon_state]-eyes")
eye_layer.plane = PLANE_LIGHTING_ABOVE
add_overlay(eye_layer)
/mob/living/simple_mob/proc/remove_eyes()
cut_overlay(eye_layer)
/mob/living/simple_mob/gib()
..(icon_gib,1,icon) // we need to specify where the gib animation is stored
@@ -0,0 +1,236 @@
// Does a melee attack.
/mob/living/simple_mob/proc/attack_target(atom/A)
set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up.
if(!A.Adjacent(src))
return FALSE
var/turf/their_T = get_turf(A)
face_atom(A)
if(melee_attack_delay)
// their_T.color = "#FF0000"
melee_pre_animation(A)
handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
// Cooldown testing is done at click code (for players) and interface code (for AI).
setClickCooldown(get_attack_speed())
. = do_attack(A, their_T)
if(melee_attack_delay)
melee_post_animation(A)
// their_T.color = "#FFFFFF"
// This does the actual attack.
// This is a seperate proc for the purposes of attack animations.
// A is the thing getting attacked, T is the turf A is/was on when attack_target was called.
/mob/living/simple_mob/proc/do_attack(atom/A, turf/T)
face_atom(A)
var/missed = FALSE
if(!isturf(A) && !(A in T) ) // Turfs don't contain themselves so checking contents is pointless if we're targeting a turf.
missed = TRUE
else if(!T.AdjacentQuick(src))
missed = TRUE
if(missed) // Most likely we have a slow attack and they dodged it or we somehow got moved.
add_attack_logs(src, A, "Animal-attacked (dodged)", admin_notify = FALSE)
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
visible_message(span("warning", "\The [src] misses their attack."))
return FALSE
var/damage_to_do = rand(melee_damage_lower, melee_damage_upper)
damage_to_do = apply_bonus_melee_damage(A, damage_to_do)
for(var/datum/modifier/M in modifiers)
if(!isnull(M.outgoing_melee_damage_percent))
damage_to_do *= M.outgoing_melee_damage_percent
if(isliving(A)) // Check defenses.
var/mob/living/L = A
if(prob(melee_miss_chance))
add_attack_logs(src, L, "Animal-attacked (miss)", admin_notify = FALSE)
do_attack_animation(src)
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
return FALSE // We missed.
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack"))
return FALSE // We were blocked.
if(apply_attack(A, damage_to_do))
apply_melee_effects(A)
if(attack_sound)
playsound(src, attack_sound, 75, 1)
return TRUE
// Generally used to do the regular attack.
// Override for doing special stuff with the direct result of the attack.
/mob/living/simple_mob/proc/apply_attack(atom/A, damage_to_do)
return A.attack_generic(src, damage_to_do, pick(attacktext))
// Override for special effects after a successful attack, like injecting poison or stunning the target.
/mob/living/simple_mob/proc/apply_melee_effects(atom/A)
return
// Override to modify the amount of damage the mob does conditionally.
// This must return the amount of outgoing damage.
// Note that this is done before mob modifiers scale the damage.
/mob/living/simple_mob/proc/apply_bonus_melee_damage(atom/A, damage_amount)
return damage_amount
//The actual top-level ranged attack proc
/mob/living/simple_mob/proc/shoot_target(atom/A)
set waitfor = FALSE
setClickCooldown(get_attack_speed())
face_atom(A)
if(ranged_attack_delay)
ranged_pre_animation(A)
handle_attack_delay(A, ranged_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
if(needs_reload)
if(reload_count >= reload_max)
try_reload()
return FALSE
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [A]!</span>")
shoot(A, src.loc, src)
if(casingtype)
new casingtype(loc)
if(ranged_attack_delay)
ranged_post_animation(A)
return TRUE
//Shoot a bullet at someone (idk why user is an argument when src would fit???)
/mob/living/simple_mob/proc/shoot(atom/A, turf/start, mob/living/user, bullet = 0)
if(A == start)
return
face_atom(A)
var/obj/item/projectile/P = new projectiletype(user.loc)
if(!P)
return
// If the projectile has its own sound, use it.
// Otherwise default to the mob's firing sound.
playsound(user, P.fire_sound ? P.fire_sound : projectilesound, 80, 1)
P.launch(A)
if(needs_reload)
reload_count++
// if(distance >= special_attack_min_range && distance <= special_attack_max_range)
// return TRUE
/mob/living/simple_mob/proc/try_reload()
set waitfor = FALSE
set_AI_busy(TRUE)
if(do_after(src, reload_time))
if(reload_sound)
playsound(src.loc, reload_sound, 50, 1)
reload_count = 0
. = TRUE
else
. = FALSE
set_AI_busy(FALSE)
// Can we currently do a special attack?
/mob/living/simple_mob/proc/can_special_attack(atom/A)
// Validity check.
if(!istype(A))
return FALSE
// Ability check.
if(isnull(special_attack_min_range) || isnull(special_attack_max_range))
return FALSE
// Distance check.
var/distance = get_dist(src, A)
if(distance < special_attack_min_range || distance > special_attack_max_range)
return FALSE
// Cooldown check.
if(!isnull(special_attack_cooldown) && last_special_attack + special_attack_cooldown > world.time)
return FALSE
// Charge check.
if(!isnull(special_attack_charges) && special_attack_charges <= 0)
return FALSE
return TRUE
// Should we do one? Used to make the AI not waste their special attacks. Only checked for AI. Players are free to screw up on their own.
/mob/living/simple_mob/proc/should_special_attack(atom/A)
return TRUE
// Special attacks, like grenades or blinding spit or whatever.
// Don't override this, override do_special_attack() for your blinding spit/etc.
/mob/living/simple_mob/proc/special_attack_target(atom/A)
face_atom(A)
if(special_attack_delay)
special_pre_animation(A)
handle_attack_delay(A, special_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
last_special_attack = world.time
if(do_special_attack(A))
if(special_attack_charges)
special_attack_charges -= 1
. = TRUE
else
. = FALSE
if(special_attack_delay)
special_post_animation(A)
// Override this for the actual special attack.
/mob/living/simple_mob/proc/do_special_attack(atom/A)
return FALSE
// Sleeps the proc that called it for the correct amount of time.
// Also makes sure the AI doesn't do anything stupid in the middle of the delay.
/mob/living/simple_mob/proc/handle_attack_delay(atom/A, delay_amount)
set_AI_busy(TRUE)
// Click delay modifiers also affect telegraphing time.
// This means berserked enemies will leave less time to dodge.
var/true_attack_delay = delay_amount
for(var/datum/modifier/M in modifiers)
if(!isnull(M.attack_speed_percent))
true_attack_delay *= M.attack_speed_percent
setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay.
sleep(true_attack_delay)
set_AI_busy(FALSE)
// Override these four for special custom animations (like the GOLEM).
/mob/living/simple_mob/proc/melee_pre_animation(atom/A)
do_windup_animation(A, melee_attack_delay)
/mob/living/simple_mob/proc/melee_post_animation(atom/A)
/mob/living/simple_mob/proc/ranged_pre_animation(atom/A)
do_windup_animation(A, ranged_attack_delay) // Semi-placeholder.
/mob/living/simple_mob/proc/ranged_post_animation(atom/A)
/mob/living/simple_mob/proc/special_pre_animation(atom/A)
do_windup_animation(A, special_attack_delay) // Semi-placeholder.
/mob/living/simple_mob/proc/special_post_animation(atom/A)
@@ -0,0 +1,224 @@
// Hit by a projectile.
/mob/living/simple_mob/bullet_act(var/obj/item/projectile/P)
//Projectiles with bonus SA damage
if(!P.nodamage)
// if(!P.SA_vulnerability || P.SA_vulnerability == intelligence_level)
if(P.SA_vulnerability & mob_class)
P.damage += P.SA_bonus_damage
. = ..()
// When someone clicks us with an empty hand
/mob/living/simple_mob/attack_hand(mob/living/L)
..()
switch(L.a_intent)
if(I_HELP)
if(health > 0)
L.visible_message("<span class='notice'>\The [L] [response_help] \the [src].</span>")
if(I_DISARM)
L.visible_message("<span class='notice'>\The [L] [response_disarm] \the [src].</span>")
L.do_attack_animation(src)
//TODO: Push the mob away or something
if(I_GRAB)
if (L == src)
return
if (!(status_flags & CANPUSH))
return
if(!incapacitated(INCAPACITATION_ALL) && prob(grab_resist))
L.visible_message("<span class='warning'>\The [L] tries to grab \the [src] but fails!</span>")
return
var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(L, src)
L.put_in_active_hand(G)
G.synch()
G.affecting = src
LAssailant = L
L.visible_message("<span class='warning'>\The [L] has grabbed [src] passively!</span>")
L.do_attack_animation(src)
if(I_HURT)
var/armor = run_armor_check(def_zone = null, attack_flag = "melee")
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)
return
// When somoene clicks us with an item in hand
/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user)
if(istype(O, /obj/item/stack/medical))
if(stat != DEAD)
// This could be done better.
var/obj/item/stack/medical/MED = O
if(health < getMaxHealth())
if(MED.amount >= 1)
adjustBruteLoss(-MED.heal_brute)
MED.amount -= 1
if(MED.amount <= 0)
qdel(MED)
visible_message("<span class='notice'>\The [user] applies the [MED] on [src].</span>")
else
var/datum/gender/T = gender_datums[src.get_visible_gender()]
to_chat(user, "<span class='notice'>\The [src] is dead, medical items won't bring [T.him] back to life.</span>") // the gender lookup is somewhat overkill, but it functions identically to the obsolete gender macros and future-proofs this code
if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead.
if(istype(O, /obj/item/weapon/material/knife))
harvest(user)
return ..()
// Handles the actual harming by a melee weapon.
/mob/living/simple_mob/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone)
effective_force = O.force
//Animals can't be stunned(?)
if(O.damtype == HALLOSS)
effective_force = 0
if(supernatural && istype(O,/obj/item/weapon/nullrod))
effective_force *= 2
purge = 3
if(O.force <= resistance)
to_chat(user,"<span class='danger'>This weapon is ineffective, it does no damage.</span>")
return 2 //???
. = ..()
// Exploding.
/mob/living/simple_mob/ex_act(severity)
if(!blinded)
flash_eyes()
var/armor = run_armor_check(def_zone = null, attack_flag = "bomb")
var/bombdam = 500
switch (severity)
if (1.0)
bombdam = 500
if (2.0)
bombdam = 60
if (3.0)
bombdam = 30
apply_damage(damage = bombdam, damagetype = BRUTE, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE)
if(bombdam > maxHealth)
gib()
// Cold stuff.
/mob/living/simple_mob/get_cold_protection()
return cold_resist
// Fire stuff. Not really exciting at the moment.
/mob/living/simple_mob/handle_fire()
return
/mob/living/simple_mob/update_fire()
return
/mob/living/simple_mob/IgniteMob()
return
/mob/living/simple_mob/ExtinguishMob()
return
/mob/living/simple_mob/get_heat_protection()
return heat_resist
// Electricity
/mob/living/simple_mob/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
shock_damage *= siemens_coeff
if(shock_damage < 1)
return 0
apply_damage(damage = shock_damage, damagetype = BURN, def_zone = null, blocked = null, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE)
playsound(loc, "sparks", 50, 1, -1)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, loc)
s.start()
/mob/living/simple_mob/get_shock_protection()
return shock_resist
// Shot with taser/stunvolver
/mob/living/simple_mob/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null)
if(taser_kill)
var/stunDam = 0
var/agonyDam = 0
var/armor = run_armor_check(def_zone = null, attack_flag = "energy")
if(stun_amount)
stunDam += stun_amount * 0.5
apply_damage(damage = stunDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE)
if(agony_amount)
agonyDam += agony_amount * 0.5
apply_damage(damage = agonyDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE)
// Electromagnetism
/mob/living/simple_mob/emp_act(severity)
..() // To emp_act() its contents.
if(!isSynthetic())
return
switch(severity)
if(1)
// adjustFireLoss(rand(15, 25))
adjustFireLoss(min(60, getMaxHealth()*0.5)) // Weak mobs will always take two direct EMP hits to kill. Stronger ones might take more.
if(2)
adjustFireLoss(min(30, getMaxHealth()*0.25))
// adjustFireLoss(rand(10, 18))
if(3)
adjustFireLoss(min(15, getMaxHealth()*0.125))
// adjustFireLoss(rand(5, 12))
if(4)
adjustFireLoss(min(7, getMaxHealth()*0.0625))
// adjustFireLoss(rand(1, 6))
// Water
/mob/living/simple_mob/get_water_protection()
return water_resist
// "Poison" (aka what reagents would do if we wanted to deal with those).
/mob/living/simple_mob/get_poison_protection()
return poison_resist
// Armor
/mob/living/simple_mob/getarmor(def_zone, attack_flag)
var/armorval = armor[attack_flag]
if(!armorval)
return 0
else
return armorval
/mob/living/simple_mob/getsoak(def_zone, attack_flag)
var/armorval = armor_soak[attack_flag]
if(!armorval)
return 0
else
return armorval
// Lightning
/mob/living/simple_mob/lightning_act()
..()
// If a non-player simple_mob was struck, inflict huge damage.
// If the damage is fatal, it is turned to ash.
if(!client)
inflict_shock_damage(200) // Mobs that are very beefy or resistant to shock may survive getting struck.
updatehealth()
if(health <= 0)
visible_message(span("critical", "\The [src] disintegrates into ash!"))
ash()
return // No point deafening something that wont exist.
// Injections.
/mob/living/simple_mob/can_inject(mob/user, error_msg, target_zone, ignore_thickness)
if(ignore_thickness)
return TRUE
return !thick_armor
+143
View File
@@ -0,0 +1,143 @@
// Hand procs for player-controlled SA's
/mob/living/simple_mob/swap_hand()
src.hand = !( src.hand )
if(hud_used.l_hand_hud_object && hud_used.r_hand_hud_object)
if(hand) //This being 1 means the left hand is in use
hud_used.l_hand_hud_object.icon_state = "l_hand_active"
hud_used.r_hand_hud_object.icon_state = "r_hand_inactive"
else
hud_used.l_hand_hud_object.icon_state = "l_hand_inactive"
hud_used.r_hand_hud_object.icon_state = "r_hand_active"
return
/mob/living/simple_mob/put_in_hands(var/obj/item/W) // No hands.
if(has_hands)
put_in_active_hand(W)
return 1
W.forceMove(get_turf(src))
return 1
//Puts the item into our active hand if possible. returns 1 on success.
/mob/living/simple_mob/put_in_active_hand(var/obj/item/W)
if(!has_hands)
return FALSE
return (hand ? put_in_l_hand(W) : put_in_r_hand(W))
/mob/living/simple_mob/put_in_l_hand(var/obj/item/W)
if(!..() || l_hand)
return 0
W.forceMove(src)
l_hand = W
W.equipped(src,slot_l_hand)
W.add_fingerprint(src)
update_inv_l_hand()
return TRUE
/mob/living/simple_mob/put_in_r_hand(var/obj/item/W)
if(!..() || r_hand)
return 0
W.forceMove(src)
r_hand = W
W.equipped(src,slot_r_hand)
W.add_fingerprint(src)
update_inv_r_hand()
return TRUE
/mob/living/simple_mob/update_inv_r_hand()
if(QDESTROYING(src))
return
if(r_hand)
r_hand.screen_loc = ui_rhand //TODO
//determine icon state to use
var/t_state
if(r_hand.item_state_slots && r_hand.item_state_slots[slot_r_hand_str])
t_state = r_hand.item_state_slots[slot_r_hand_str]
else if(r_hand.item_state)
t_state = r_hand.item_state
else
t_state = r_hand.icon_state
//determine icon to use
var/icon/t_icon
if(r_hand.item_icons && (slot_r_hand_str in r_hand.item_icons))
t_icon = r_hand.item_icons[slot_r_hand_str]
else if(r_hand.icon_override)
t_state += "_r"
t_icon = r_hand.icon_override
else
t_icon = INV_R_HAND_DEF_ICON
//apply color
var/image/standing = image(icon = t_icon, icon_state = t_state)
standing.color = r_hand.color
r_hand_sprite = standing
else
r_hand_sprite = null
update_icon()
/mob/living/simple_mob/update_inv_l_hand()
if(QDESTROYING(src))
return
if(l_hand)
l_hand.screen_loc = ui_lhand //TODO
//determine icon state to use
var/t_state
if(l_hand.item_state_slots && l_hand.item_state_slots[slot_l_hand_str])
t_state = l_hand.item_state_slots[slot_l_hand_str]
else if(l_hand.item_state)
t_state = l_hand.item_state
else
t_state = l_hand.icon_state
//determine icon to use
var/icon/t_icon
if(l_hand.item_icons && (slot_l_hand_str in l_hand.item_icons))
t_icon = l_hand.item_icons[slot_l_hand_str]
else if(l_hand.icon_override)
t_state += "_l"
t_icon = l_hand.icon_override
else
t_icon = INV_L_HAND_DEF_ICON
//apply color
var/image/standing = image(icon = t_icon, icon_state = t_state)
standing.color = l_hand.color
l_hand_sprite = standing
else
l_hand_sprite = null
update_icon()
//Can insert extra huds into the hud holder here.
/mob/living/simple_mob/proc/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements)
return
//If they can or cannot use tools/machines/etc
/mob/living/simple_mob/IsAdvancedToolUser()
return has_hands
/mob/living/simple_mob/proc/IsHumanoidToolUser(var/atom/tool)
if(!humanoid_hands)
var/display_name = null
if(tool)
display_name = tool
else
display_name = "object"
to_chat(src, "<span class='danger'>Your [hand_form] are not fit for use of \the [display_name].</span>")
return humanoid_hands
/mob/living/simple_mob/drop_from_inventory(var/obj/item/W, var/atom/target = null)
. = ..(W, target)
if(!target)
target = src.loc
if(.)
W.forceMove(src.loc)
+160
View File
@@ -0,0 +1,160 @@
/mob/living/simple_mob/Life()
..()
//Health
updatehealth()
if(stat >= DEAD)
return FALSE
handle_stunned()
handle_weakened()
handle_paralysed()
handle_supernatural()
handle_atmos()
handle_special()
return TRUE
//Should we be dead?
/mob/living/simple_mob/updatehealth()
health = getMaxHealth() - getFireLoss() - getBruteLoss() - getToxLoss() - getOxyLoss() - getCloneLoss()
//Alive, becoming dead
if((stat < DEAD) && (health <= 0))
death()
//Overhealth
if(health > getMaxHealth())
health = getMaxHealth()
//Update our hud if we have one
if(healths)
if(stat != DEAD)
var/heal_per = (health / getMaxHealth()) * 100
switch(heal_per)
if(100 to INFINITY)
healths.icon_state = "health0"
if(80 to 100)
healths.icon_state = "health1"
if(60 to 80)
healths.icon_state = "health2"
if(40 to 60)
healths.icon_state = "health3"
if(20 to 40)
healths.icon_state = "health4"
if(0 to 20)
healths.icon_state = "health5"
else
healths.icon_state = "health6"
else
healths.icon_state = "health7"
//Updates the nutrition while we're here
if(nutrition_icon)
var/food_per = (nutrition / initial(nutrition)) * 100
switch(food_per)
if(90 to INFINITY)
nutrition_icon.icon_state = "nutrition0"
if(75 to 90)
nutrition_icon.icon_state = "nutrition1"
if(50 to 75)
nutrition_icon.icon_state = "nutrition2"
if(25 to 50)
nutrition_icon.icon_state = "nutrition3"
if(0 to 25)
nutrition_icon.icon_state = "nutrition4"
// Override for special bullshit.
/mob/living/simple_mob/proc/handle_special()
return
// Handle interacting with and taking damage from atmos
// TODO - Refactor this to use handle_environment() like a good /mob/living
/mob/living/simple_mob/proc/handle_atmos()
var/atmos_unsuitable = 0
var/atom/A = src.loc
if(istype(A,/turf))
var/turf/T = A
var/datum/gas_mixture/Environment = T.return_air()
if(Environment)
if( abs(Environment.temperature - bodytemperature) > 40 )
bodytemperature += ((Environment.temperature - bodytemperature) / 5)
if(min_oxy)
if(Environment.gas["oxygen"] < min_oxy)
atmos_unsuitable = 1
if(max_oxy)
if(Environment.gas["oxygen"] > max_oxy)
atmos_unsuitable = 1
if(min_tox)
if(Environment.gas["phoron"] < min_tox)
atmos_unsuitable = 2
if(max_tox)
if(Environment.gas["phoron"] > max_tox)
atmos_unsuitable = 2
if(min_n2)
if(Environment.gas["nitrogen"] < min_n2)
atmos_unsuitable = 1
if(max_n2)
if(Environment.gas["nitrogen"] > max_n2)
atmos_unsuitable = 1
if(min_co2)
if(Environment.gas["carbon_dioxide"] < min_co2)
atmos_unsuitable = 1
if(max_co2)
if(Environment.gas["carbon_dioxide"] > max_co2)
atmos_unsuitable = 1
//Atmos effect
if(bodytemperature < minbodytemp)
fire_alert = 2
adjustFireLoss(cold_damage_per_tick)
if(fire)
fire.icon_state = "fire1"
else if(bodytemperature > maxbodytemp)
fire_alert = 1
adjustFireLoss(heat_damage_per_tick)
if(fire)
fire.icon_state = "fire2"
else
fire_alert = 0
if(fire)
fire.icon_state = "fire0"
if(atmos_unsuitable)
adjustOxyLoss(unsuitable_atoms_damage)
if(oxygen)
oxygen.icon_state = "oxy1"
else if(oxygen)
if(oxygen)
oxygen.icon_state = "oxy0"
adjustOxyLoss(-unsuitable_atoms_damage)
/mob/living/simple_mob/proc/handle_supernatural()
if(purge)
purge -= 1
/mob/living/simple_mob/death(gibbed, deathmessage = "dies!")
density = 0 //We don't block even if we did before
if(has_eye_glow)
remove_eyes()
if(loot_list.len) //Drop any loot
for(var/path in loot_list)
if(prob(loot_list[path]))
new path(get_turf(src))
spawn(3) //We'll update our icon in a sec
update_icon()
return ..(gibbed,deathmessage)
@@ -0,0 +1,48 @@
/*
Animals
*/
/mob/living/simple_mob/UnarmedAttack(var/atom/A, var/proximity)
if(!(. = ..()))
return
// setClickCooldown(get_attack_speed())
if(has_hands && istype(A,/obj) && a_intent != I_HURT)
var/obj/O = A
return O.attack_hand(src)
switch(a_intent)
if(I_HELP)
if(isliving(A))
custom_emote(1,"[pick(friendly)] \the [A]!")
if(I_HURT)
if(can_special_attack(A) && special_attack_target(A))
return
else if(melee_damage_upper == 0 && istype(A,/mob/living))
custom_emote(1,"[pick(friendly)] \the [A]!")
else
attack_target(A)
if(I_GRAB)
if(has_hands)
A.attack_hand(src)
else
attack_target(A)
if(I_DISARM)
if(has_hands)
A.attack_hand(src)
else
attack_target(A)
/mob/living/simple_mob/RangedAttack(var/atom/A)
// setClickCooldown(get_attack_speed())
if(can_special_attack(A) && special_attack_target(A))
return
if(projectiletype)
shoot_target(A)
@@ -0,0 +1,311 @@
/mob/living/simple_mob/instantiate_hud(var/datum/hud/hud)
if(!client)
return //Why bother.
var/ui_style = 'icons/mob/screen1_animal.dmi'
if(ui_icons)
ui_style = ui_icons
var/ui_color = "#ffffff"
var/ui_alpha = 255
var/list/adding = list()
var/list/other = list()
var/list/hotkeybuttons = list()
var/list/slot_info = list()
hud.adding = adding
hud.other = other
hud.hotkeybuttons = hotkeybuttons
var/list/hud_elements = list()
var/obj/screen/using
var/obj/screen/inventory/inv_box
var/has_hidden_gear
if(LAZYLEN(hud_gears))
for(var/gear_slot in hud_gears)
inv_box = new /obj/screen/inventory()
inv_box.icon = ui_style
inv_box.color = ui_color
inv_box.alpha = ui_alpha
var/list/slot_data = hud_gears[gear_slot]
inv_box.name = gear_slot
inv_box.screen_loc = slot_data["loc"]
inv_box.slot_id = slot_data["slot"]
inv_box.icon_state = slot_data["state"]
slot_info["[inv_box.slot_id]"] = inv_box.screen_loc
if(slot_data["dir"])
inv_box.set_dir(slot_data["dir"])
if(slot_data["toggle"])
other += inv_box
has_hidden_gear = 1
else
adding += inv_box
if(has_hidden_gear)
using = new /obj/screen()
using.name = "toggle"
using.icon = ui_style
using.icon_state = "other"
using.screen_loc = ui_inventory
using.hud_layerise()
using.color = ui_color
using.alpha = ui_alpha
adding += using
//Intent Backdrop
using = new /obj/screen()
using.name = "act_intent"
using.icon = ui_style
using.icon_state = "intent_"+a_intent
using.screen_loc = ui_acti
using.color = ui_color
using.alpha = ui_alpha
hud.adding += using
hud.action_intent = using
hud_elements |= using
//Small intent quarters
var/icon/ico
ico = new(ui_style, "black")
ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
ico.DrawBox(rgb(255,255,255,1),1,ico.Height()/2,ico.Width()/2,ico.Height())
using = new /obj/screen()
using.name = I_HELP
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
using.layer = LAYER_HUD_ITEM //These sit on the intent box
hud.adding += using
hud.help_intent = using
ico = new(ui_style, "black")
ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,ico.Height()/2,ico.Width(),ico.Height())
using = new /obj/screen()
using.name = I_DISARM
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
using.layer = LAYER_HUD_ITEM
hud.adding += using
hud.disarm_intent = using
ico = new(ui_style, "black")
ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,1,ico.Width(),ico.Height()/2)
using = new /obj/screen()
using.name = I_GRAB
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
using.layer = LAYER_HUD_ITEM
hud.adding += using
hud.grab_intent = using
ico = new(ui_style, "black")
ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1)
ico.DrawBox(rgb(255,255,255,1),1,1,ico.Width()/2,ico.Height()/2)
using = new /obj/screen()
using.name = I_HURT
using.icon = ico
using.screen_loc = ui_acti
using.alpha = ui_alpha
using.layer = LAYER_HUD_ITEM
hud.adding += using
hud.hurt_intent = using
//Move intent (walk/run)
using = new /obj/screen()
using.name = "mov_intent"
using.icon = ui_style
using.icon_state = (m_intent == "run" ? "running" : "walking")
using.screen_loc = ui_movi
using.color = ui_color
using.alpha = ui_alpha
hud.adding += using
hud.move_intent = using
//Resist button
using = new /obj/screen()
using.name = "resist"
using.icon = ui_style
using.icon_state = "act_resist"
using.screen_loc = ui_pull_resist
using.color = ui_color
using.alpha = ui_alpha
hud.hotkeybuttons += using
//Pull button
pullin = new /obj/screen()
pullin.icon = ui_style
pullin.icon_state = "pull0"
pullin.name = "pull"
pullin.screen_loc = ui_pull_resist
hud.hotkeybuttons += pullin
hud_elements |= pullin
//Health status
healths = new /obj/screen()
healths.icon = ui_style
healths.icon_state = "health0"
healths.name = "health"
healths.screen_loc = ui_health
hud_elements |= healths
//Oxygen dep icon
oxygen = new /obj/screen()
oxygen.icon = ui_style
oxygen.icon_state = "oxy0"
oxygen.name = "oxygen"
oxygen.screen_loc = ui_oxygen
hud_elements |= oxygen
//Toxins present icon
toxin = new /obj/screen()
toxin.icon = ui_style
toxin.icon_state = "tox0"
toxin.name = "toxin"
toxin.screen_loc = ui_toxin
hud_elements |= toxin
//Fire warning
fire = new /obj/screen()
fire.icon = ui_style
fire.icon_state = "fire0"
fire.name = "fire"
fire.screen_loc = ui_fire
hud_elements |= fire
//Pressure warning
pressure = new /obj/screen()
pressure.icon = ui_style
pressure.icon_state = "pressure0"
pressure.name = "pressure"
pressure.screen_loc = ui_pressure
hud_elements |= pressure
//Body temp warning
bodytemp = new /obj/screen()
bodytemp.icon = ui_style
bodytemp.icon_state = "temp0"
bodytemp.name = "body temperature"
bodytemp.screen_loc = ui_temp
hud_elements |= bodytemp
//Nutrition status
nutrition_icon = new /obj/screen()
nutrition_icon.icon = ui_style
nutrition_icon.icon_state = "nutrition0"
nutrition_icon.name = "nutrition"
nutrition_icon.screen_loc = ui_nutrition
hud_elements |= nutrition_icon
pain = new /obj/screen( null )
zone_sel = new /obj/screen/zone_sel( null )
zone_sel.icon = ui_style
zone_sel.color = ui_color
zone_sel.alpha = ui_alpha
zone_sel.overlays.Cut()
zone_sel.overlays += image('icons/mob/zone_sel.dmi', "[zone_sel.selecting]")
hud_elements |= zone_sel
//Hand things
if(has_hands)
//Drop button
using = new /obj/screen()
using.name = "drop"
using.icon = ui_style
using.icon_state = "act_drop"
using.screen_loc = ui_drop_throw
using.color = ui_color
using.alpha = ui_alpha
hud.hotkeybuttons += using
//Equip detail
using = new /obj/screen()
using.name = "equip"
using.icon = ui_style
using.icon_state = "act_equip"
using.screen_loc = ui_equip
using.color = ui_color
using.alpha = ui_alpha
hud.adding += using
//Hand slots themselves
inv_box = new /obj/screen/inventory/hand()
inv_box.hud = src
inv_box.name = "r_hand"
inv_box.icon = ui_style
inv_box.icon_state = "r_hand_inactive"
if(!hand) //This being 0 or null means the right hand is in use
inv_box.icon_state = "r_hand_active"
inv_box.screen_loc = ui_rhand
inv_box.slot_id = slot_r_hand
inv_box.color = ui_color
inv_box.alpha = ui_alpha
hud.r_hand_hud_object = inv_box
hud.adding += inv_box
slot_info["[slot_r_hand]"] = inv_box.screen_loc
inv_box = new /obj/screen/inventory/hand()
inv_box.hud = src
inv_box.name = "l_hand"
inv_box.icon = ui_style
inv_box.icon_state = "l_hand_inactive"
if(hand) //This being 1 means the left hand is in use
inv_box.icon_state = "l_hand_active"
inv_box.screen_loc = ui_lhand
inv_box.slot_id = slot_l_hand
inv_box.color = ui_color
inv_box.alpha = ui_alpha
hud.l_hand_hud_object = inv_box
hud.adding += inv_box
slot_info["[slot_l_hand]"] = inv_box.screen_loc
//Swaphand titlebar
using = new /obj/screen/inventory()
using.name = "hand"
using.icon = ui_style
using.icon_state = "hand1"
using.screen_loc = ui_swaphand1
using.color = ui_color
using.alpha = ui_alpha
hud.adding += using
using = new /obj/screen/inventory()
using.name = "hand"
using.icon = ui_style
using.icon_state = "hand2"
using.screen_loc = ui_swaphand2
using.color = ui_color
using.alpha = ui_alpha
hud.adding += using
//Throw button
throw_icon = new /obj/screen()
throw_icon.icon = ui_style
throw_icon.icon_state = "act_throw_off"
throw_icon.name = "throw"
throw_icon.screen_loc = ui_drop_throw
throw_icon.color = ui_color
throw_icon.alpha = ui_alpha
hud.hotkeybuttons += throw_icon
hud_elements |= throw_icon
extra_huds(hud,ui_style,hud_elements)
client.screen = list()
client.screen += hud_elements
client.screen += adding + hotkeybuttons
client.screen += client.void
return
@@ -0,0 +1,290 @@
// Reorganized and somewhat cleaned up.
// AI code has been made into a datum, inside the AI module folder.
/mob/living/simple_mob
name = "animal"
desc = ""
icon = 'icons/mob/animal.dmi'
health = 20
maxHealth = 20
// Generally we don't want simple_mobs to get displaced when bumped into due to it trivializing combat with windup attacks.
// Some subtypes allow displacement, like passive animals.
mob_bump_flag = HEAVY
mob_swap_flags = ~HEAVY
mob_push_flags = ~HEAVY
var/tt_desc = "Uncataloged Life Form" //Tooltip description
//Settings for played mobs
var/show_stat_health = 1 // Does the percentage health show in the stat panel for the mob
var/has_hands = 0 // Set to 1 to enable the use of hands and the hands hud
var/humanoid_hands = 0 // Can a player in this mob use things like guns or AI cards?
var/hand_form = "hands" // Used in IsHumanoidToolUser. 'Your X are not fit-'.
var/list/hud_gears // Slots to show on the hud (typically none)
var/ui_icons // Icon file path to use for the HUD, otherwise generic icons are used
var/r_hand_sprite // If they have hands,
var/l_hand_sprite // they could use some icons.
var/player_msg // Message to print to players about 'how' to play this mob on login.
//Mob icon/appearance settings
var/icon_living = "" // The iconstate if we're alive, required
var/icon_dead = "" // The iconstate if we're dead, required
var/icon_gib = "generic_gib" // The iconstate for being gibbed, optional. Defaults to a generic gib animation.
var/icon_rest = null // The iconstate for resting, optional
var/image/modifier_overlay = null // Holds overlays from modifiers.
var/image/eye_layer = null // Holds the eye overlay.
var/has_eye_glow = FALSE // If true, adds an overlay over the lighting plane for [icon_state]-eyes.
attack_icon = 'icons/effects/effects.dmi' //Just the default, played like the weapon attack anim
attack_icon_state = "slash" //Just the default
//Mob talking settings
universal_speak = 0 // Can all mobs in the entire universe understand this one?
var/has_langs = list(LANGUAGE_GALCOM)// Text name of their language if they speak something other than galcom. They speak the first one.
//Movement things.
var/movement_cooldown = 5 // Lower is faster.
var/movement_sound = null // If set, will play this sound when it moves on its own will.
var/turn_sound = null // If set, plays the sound when the mob's dir changes in most cases.
var/movement_shake_radius = 0 // If set, moving will shake the camera of all living mobs within this radius slightly.
//Mob interaction
var/response_help = "tries to help" // If clicked on help intent
var/response_disarm = "tries to disarm" // If clicked on disarm intent
var/response_harm = "tries to hurt" // If clicked on harm intent
var/list/friends = list() // Mobs on this list wont get attacked regardless of faction status.
var/harm_intent_damage = 3 // How much an unarmed harm click does to this mob.
var/meat_amount = 0 // How much meat to drop from this mob when butchered
var/obj/meat_type // The meat object to drop
var/list/loot_list = list() // The list of lootable objects to drop, with "/path = prob%" structure
var/obj/item/weapon/card/id/myid// An ID card if they have one to give them access to stuff.
//Mob environment settings
var/minbodytemp = 250 // Minimum "okay" temperature in kelvin
var/maxbodytemp = 350 // Maximum of above
var/heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp
var/cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
var/fire_alert = 0 // 0 = fine, 1 = hot, 2 = cold
var/min_oxy = 5 // Oxygen in moles, minimum, 0 is 'no minimum'
var/max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum'
var/min_tox = 0 // Phoron min
var/max_tox = 1 // Phoron max
var/min_co2 = 0 // CO2 min
var/max_co2 = 5 // CO2 max
var/min_n2 = 0 // N2 min
var/max_n2 = 0 // N2 max
var/unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above
//Hostility settings
var/taser_kill = 1 // Is the mob weak to tasers
//Attack ranged settings
var/projectiletype // The projectiles I shoot
var/projectilesound // The sound I make when I do it
var/casingtype // What to make the hugely laggy casings pile out of
// Reloading settings, part of ranged code
var/needs_reload = FALSE // If TRUE, mob needs to reload occasionally
var/reload_max = 1 // How many shots the mob gets before it has to reload, will not be used if needs_reload is FALSE
var/reload_count = 0 // A counter to keep track of how many shots the mob has fired so far. Reloads when it hits reload_max.
var/reload_time = 1 SECONDS // How long it takes for a mob to reload. This is to buy a player a bit of time to run or fight.
var/reload_sound = 'sound/weapons/flipblade.ogg' // What sound gets played when the mob successfully reloads. Defaults to the same sound as reloading guns. Can be null.
//Mob melee settings
var/melee_damage_lower = 2 // Lower bound of randomized melee damage
var/melee_damage_upper = 6 // Upper bound of randomized melee damage
var/list/attacktext = list("attacked") // "You are [attacktext] by the mob!"
var/list/friendly = list("nuzzles") // "The mob [friendly] the person."
var/attack_sound = null // Sound to play when I attack
var/melee_miss_chance = 0 // percent chance to miss a melee attack.
var/attack_armor_type = "melee" // What armor does this check?
var/attack_armor_pen = 0 // How much armor pen this attack has.
var/attack_sharp = FALSE // Is the attack sharp?
var/attack_edge = FALSE // Does the attack have an edge?
var/melee_attack_delay = null // If set, the mob will do a windup animation and can miss if the target moves out of the way.
var/ranged_attack_delay = null
var/special_attack_delay = null
//Special attacks
// var/special_attack_prob = 0 // The chance to ATTEMPT a special_attack_target(). If it fails, it will do a regular attack instead.
// This is commented out to ease the AI attack logic by being (a bit more) determanistic.
// You should instead limit special attacks using the below vars instead.
var/special_attack_min_range = null // The minimum distance required for an attempt to be made.
var/special_attack_max_range = null // The maximum for an attempt.
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.
//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.
var/resistance = 0 // Damage reduction for all types
var/list/armor = list( // Values for normal getarmor() checks
"melee" = 0,
"bullet" = 0,
"laser" = 0,
"energy" = 0,
"bomb" = 0,
"bio" = 100,
"rad" = 100
)
var/list/armor_soak = list( // Values for getsoak() checks.
"melee" = 0,
"bullet" = 0,
"laser" = 0,
"energy" = 0,
"bomb" = 0,
"bio" = 0,
"rad" = 0
)
// Protection against heat/cold/electric/water effects.
// 0 is no protection, 1 is total protection. Negative numbers increase vulnerability.
var/heat_resist = 0.0
var/cold_resist = 0.0
var/shock_resist = 0.0
var/water_resist = 1.0
var/poison_resist = 0.0
var/thick_armor = FALSE // Stops injections and "injections".
var/purge = 0 // Cult stuff.
var/supernatural = FALSE // Ditto.
/mob/living/simple_mob/initialize()
verbs -= /mob/verb/observe
health = maxHealth
for(var/L in has_langs)
languages |= all_languages[L]
if(languages.len)
default_language = languages[1]
if(has_eye_glow)
add_eyes()
return ..()
/mob/living/simple_mob/Destroy()
default_language = null
if(myid)
qdel(myid)
myid = null
friends.Cut()
languages.Cut()
if(has_eye_glow)
remove_eyes()
return ..()
/mob/living/simple_mob/death()
update_icon()
..()
//Client attached
/mob/living/simple_mob/Login()
. = ..()
to_chat(src,"<b>You are \the [src].</b> [player_msg]")
/mob/living/simple_mob/emote(var/act, var/type, var/desc)
if(act)
..(act, type, desc)
/mob/living/simple_mob/SelfMove(turf/n, direct)
var/turf/old_turf = get_turf(src)
var/old_dir = dir
. = ..()
if(. && movement_shake_radius)
for(var/mob/living/L in range(movement_shake_radius, src))
shake_camera(L, 1, 1)
if(turn_sound && dir != old_dir)
playsound(src, turn_sound, 50, 1)
else if(movement_sound && old_turf != get_turf(src)) // Playing both sounds at the same time generally sounds bad.
playsound(src, movement_sound, 50, 1)
/*
/mob/living/simple_mob/set_dir(new_dir)
if(dir != new_dir)
playsound(src, turn_sound, 50, 1)
return ..()
*/
/mob/living/simple_mob/movement_delay()
var/tally = 0 //Incase I need to add stuff other than "speed" later
tally = movement_cooldown
if(force_max_speed)
return -3
for(var/datum/modifier/M in modifiers)
if(!isnull(M.haste) && M.haste == TRUE)
return -3
if(!isnull(M.slowdown))
tally += M.slowdown
// Turf related slowdown
var/turf/T = get_turf(src)
if(T && T.movement_cost && !hovering) // Flying mobs ignore turf-based slowdown.
tally += T.movement_cost
if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move.
if(tally <= 0)
tally = 1
tally *= purge
if(m_intent == "walk")
tally *= 1.5
return tally+config.animal_delay
/mob/living/simple_mob/Stat()
..()
if(statpanel("Status") && show_stat_health)
stat(null, "Health: [round((health / getMaxHealth()) * 100)]%")
/mob/living/simple_mob/lay_down()
..()
if(resting && icon_rest)
icon_state = icon_rest
else
icon_state = icon_living
update_icon()
/mob/living/simple_mob/say(var/message,var/datum/language/language)
var/verb = "says"
if(speak_emote.len)
verb = pick(speak_emote)
message = sanitize(message)
..(message, null, verb)
/mob/living/simple_mob/get_speech_ending(verb, var/ending)
return verb
// Harvest an animal's delicious byproducts
/mob/living/simple_mob/proc/harvest(var/mob/user)
var/actual_meat_amount = max(1,(meat_amount/2))
if(meat_type && actual_meat_amount>0 && (stat == DEAD))
for(var/i=0;i<actual_meat_amount;i++)
var/obj/item/meat = new meat_type(get_turf(src))
meat.name = "[src.name] [meat.name]"
if(issmall(src))
user.visible_message("<span class='danger'>[user] chops up \the [src]!</span>")
new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
qdel(src)
else
user.visible_message("<span class='danger'>[user] butchers \the [src] messily!</span>")
gib()
/mob/living/simple_mob/is_sentient()
return mob_class & MOB_CLASS_HUMANOID|MOB_CLASS_ANIMAL|MOB_CLASS_SLIME // Update this if needed.
/mob/living/simple_mob/get_nametag_desc(mob/user)
return "<i>[tt_desc]</i>"
@@ -0,0 +1,9 @@
/mob/living/simple_mob/animal
mob_class = MOB_CLASS_ANIMAL
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
response_help = "pets"
response_disarm = "shoos"
response_harm = "hits"
ai_holder_type = /datum/ai_holder/simple_mob/melee
@@ -0,0 +1,237 @@
// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system.
// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would
// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite.
/mob/living/simple_mob/animal/borer
name = "cortical borer"
desc = "A small, quivering sluglike creature."
icon_state = "brainslug"
item_state = "brainslug"
icon_living = "brainslug"
icon_dead = "brainslug_dead"
response_help = "pokes"
response_disarm = "prods"
response_harm = "stomps on"
attacktext = list("nipped")
friendly = list("prods")
status_flags = CANPUSH
pass_flags = PASSTABLE
movement_cooldown = 5
universal_understand = TRUE
can_be_antagged = TRUE
holder_type = /obj/item/weapon/holder/borer
ai_holder_type = null // This is player-controlled, always.
var/chemicals = 10 // A resource used for reproduction and powers.
var/mob/living/carbon/human/host = null // The humanoid host for the brain worm.
var/true_name = null // String used when speaking among other worms.
var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth.
var/controlling = FALSE // Used in human death ceck.
var/docile = FALSE // Sugar can stop borers from acting.
var/has_reproduced = FALSE
var/roundstart = FALSE // If true, spawning won't try to pull a ghost.
var/used_dominate // world.time when the dominate power was last used.
/mob/living/simple_mob/animal/borer/roundstart
roundstart = TRUE
/mob/living/simple_mob/animal/borer/Login()
..()
if(mind)
borers.add_antagonist(mind)
/mob/living/simple_mob/animal/borer/initialize()
add_language("Cortical Link")
verbs += /mob/living/proc/ventcrawl
verbs += /mob/living/proc/hide
true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
if(!roundstart)
request_player()
return ..()
/mob/living/simple_mob/animal/borer/handle_special()
if(host && !stat && !host.stat)
// Handle docility.
if(host.reagents.has_reagent("sugar") && !docile)
var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility."
var/target = controlling ? host : src
to_chat(target, span("warning", message))
docile = TRUE
else if(docile)
var/message = "You shake off your lethargy as the sugar leaves your host's blood."
var/target = controlling ? host : src
to_chat(target, span("notice", message))
docile = FALSE
// Chem regen.
if(chemicals < 250)
chemicals++
// Control stuff.
if(controlling)
if(docile)
to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host..."))
host.release_control()
return
if(prob(5))
host.adjustBrainLoss(0.1)
if(prob(host.brainloss/20))
host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
/mob/living/simple_mob/animal/borer/Stat()
..()
if(client.statpanel == "Status")
statpanel("Status")
if(emergency_shuttle)
var/eta_status = emergency_shuttle.get_status_panel_eta()
if(eta_status)
stat(null, eta_status)
stat("Chemicals", chemicals)
/mob/living/simple_mob/animal/borer/proc/detatch()
if(!host || !controlling)
return
if(istype(host, /mob/living/carbon/human))
var/mob/living/carbon/human/H = host
var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
if(head)
head.implants -= src
controlling = FALSE
host.remove_language("Cortical Link")
host.verbs -= /mob/living/carbon/proc/release_control
host.verbs -= /mob/living/carbon/proc/punish_host
host.verbs -= /mob/living/carbon/proc/spawn_larvae
if(host_brain)
// these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed.
// computer_id and IP are not updated magically on their own in offline mobs -walter0o
// This shit need to die in a phoron fire.
// host -> self
var/h2s_id = host.computer_id
var/h2s_ip= host.lastKnownIP
host.computer_id = null
host.lastKnownIP = null
src.ckey = host.ckey
if(!src.computer_id)
src.computer_id = h2s_id
if(!host_brain.lastKnownIP)
src.lastKnownIP = h2s_ip
// brain -> host
var/b2h_id = host_brain.computer_id
var/b2h_ip= host_brain.lastKnownIP
host_brain.computer_id = null
host_brain.lastKnownIP = null
host.ckey = host_brain.ckey
if(!host.computer_id)
host.computer_id = b2h_id
if(!host.lastKnownIP)
host.lastKnownIP = b2h_ip
qdel(host_brain)
/mob/living/simple_mob/animal/borer/proc/leave_host()
if(!host)
return
if(host.mind)
borers.remove_antagonist(host.mind)
forceMove(get_turf(host))
reset_view(null)
machine = null
host.reset_view(null)
host.machine = null
host = null
/mob/living/simple_mob/animal/borer/proc/request_player()
var/datum/ghost_query/Q = new /datum/ghost_query/borer()
var/list/winner = Q.query() // This will sleep the proc for awhile.
if(winner.len)
var/mob/observer/dead/D = winner[1]
transfer_personality(D)
/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate)
if(!candidate || !candidate.mind)
return
src.mind = candidate.mind
candidate.mind.current = src
ckey = candidate.ckey
if(mind)
mind.assigned_role = "Cortical Borer"
mind.special_role = "Cortical Borer"
to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \
into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
your host and your eventual spawn safe and warm."))
to_chat(src, "You can speak to your victim with <b>say</b>, to other borers with <b>say :x</b>, and use your Abilities tab to access powers.")
/mob/living/simple_mob/animal/borer/cannot_use_vents()
return
// This is awful but its literally say code.
/mob/living/simple_mob/animal/borer/say(message)
message = sanitize(message)
message = capitalize(message)
if(!message)
return
if(stat >= DEAD)
return say_dead(message)
else if(stat)
return
if(client && client.prefs.muted & MUTE_IC)
to_chat(src, span("danger", "You cannot speak in IC (muted)."))
return
if(copytext(message, 1, 2) == "*")
return emote(copytext(message, 2))
var/datum/language/L = parse_language(message)
if(L && L.flags & HIVEMIND)
L.broadcast(src,trim(copytext(message,3)), src.true_name)
return
if(!host)
//TODO: have this pick a random mob within 3 tiles to speak for the borer.
to_chat(src, span("warning", "You have no host to speak to."))
return //No host, no audible speech.
to_chat(src, "You drop words into [host]'s mind: \"[message]\"")
to_chat(host, "Your own thoughts speak: \"[message]\"")
for(var/mob/M in player_list)
if(istype(M, /mob/new_player))
continue
else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
to_chat(M, "[src.true_name] whispers to [host], \"[message]\"")
@@ -0,0 +1,59 @@
// Straight move from the old location, with the paths corrected.
/mob/living/captive_brain
name = "host brain"
real_name = "host brain"
universal_understand = 1
/mob/living/captive_brain/say(var/message)
if (src.client)
if(client.prefs.muted & MUTE_IC)
src << "<font color='red'>You cannot speak in IC (muted).</font>"
return
if(istype(src.loc, /mob/living/simple_mob/animal/borer))
message = sanitize(message)
if (!message)
return
log_say(message,src)
if (stat == 2)
return say_dead(message)
var/mob/living/simple_mob/animal/borer/B = src.loc
src << "You whisper silently, \"[message]\""
B.host << "The captive mind of [src] whispers, \"[message]\""
for (var/mob/M in player_list)
if (istype(M, /mob/new_player))
continue
else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
M << "The captive mind of [src] whispers, \"[message]\""
/mob/living/captive_brain/emote(var/message)
return
/mob/living/captive_brain/process_resist()
//Resisting control by an alien mind.
if(istype(src.loc, /mob/living/simple_mob/animal/borer))
var/mob/living/simple_mob/animal/borer/B = src.loc
var/mob/living/captive_brain/H = src
H << "<span class='danger'>You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).</span>"
B.host << "<span class='danger'>You feel the captive mind of [src] begin to resist your control.</span>"
spawn(rand(200,250)+B.host.brainloss)
if(!B || !B.controlling) return
B.host.adjustBrainLoss(rand(0.1,0.5))
H << "<span class='danger'>With an immense exertion of will, you regain control of your body!</span>"
B.host << "<span class='danger'>You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.</span>"
B.detatch()
verbs -= /mob/living/carbon/proc/release_control
verbs -= /mob/living/carbon/proc/punish_host
verbs -= /mob/living/carbon/proc/spawn_larvae
return
..()
@@ -0,0 +1,354 @@
/mob/living/simple_mob/animal/borer/verb/release_host()
set category = "Abilities"
set name = "Release Host"
set desc = "Slither out of your host."
if(!host)
src << "You are not inside a host body."
return
if(stat)
src << "You cannot leave your host in your current state."
if(docile)
src << "<font color='blue'>You are feeling far too docile to do that.</font>"
return
if(!host || !src) return
src << "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal."
if(!host.stat)
host << "An odd, uncomfortable pressure begins to build inside your skull, behind your ear..."
spawn(100)
if(!host || !src) return
if(src.stat)
src << "You cannot release your host in your current state."
return
src << "You wiggle out of [host]'s ear and plop to the ground."
if(host.mind)
if(!host.stat)
host << "<span class='danger'>Something slimy wiggles out of your ear and plops to the ground!</span>"
host << "<span class='danger'>As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again.</span>"
detatch()
leave_host()
/mob/living/simple_mob/animal/borer/verb/infest()
set category = "Abilities"
set name = "Infest"
set desc = "Infest a suitable humanoid host."
if(host)
src << "You are already within a host."
return
if(stat)
src << "You cannot infest a target in your current state."
return
var/list/choices = list()
for(var/mob/living/carbon/C in view(1,src))
if(src.Adjacent(C))
choices += C
if(!choices.len)
src << "There are no viable hosts within range..."
return
var/mob/living/carbon/M = input(src,"Who do you wish to infest?") in null|choices
if(!M || !src) return
if(!(src.Adjacent(M))) return
if(M.has_brain_worms())
src << "You cannot infest someone who is already infested!"
return
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD]
if(!E || E.is_stump())
src << "\The [H] does not have a head!"
if(!H.should_have_organ("brain"))
src << "\The [H] does not seem to have an ear canal to breach."
return
if(H.check_head_coverage())
src << "You cannot get through that host's protective gear."
return
M << "Something slimy begins probing at the opening of your ear canal..."
src << "You slither up [M] and begin probing at their ear canal..."
if(!do_after(src,30))
src << "As [M] moves away, you are dislodged and fall to the ground."
return
if(!M || !src) return
if(src.stat)
src << "You cannot infest a target in your current state."
return
if(M in view(1, src))
src << "You wiggle into [M]'s ear."
if(!M.stat)
M << "Something disgusting and slimy wiggles into your ear!"
src.host = M
src.forceMove(M)
//Update their traitor status.
if(host.mind)
borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome)
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
var/obj/item/organ/I = H.internal_organs_by_name["brain"]
if(!I) // No brain organ, so the borer moves in and replaces it permanently.
replace_brain()
else
// If they're in normally, implant removal can get them out.
var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
head.implants += src
return
else
src << "They are no longer in range!"
return
/*
/mob/living/simple_mob/animal/borer/verb/devour_brain()
set category = "Abilities"
set name = "Devour Brain"
set desc = "Take permanent control of a dead host."
if(!host)
src << "You are not inside a host body."
return
if(host.stat != 2)
src << "Your host is still alive."
return
if(stat)
src << "You cannot do that in your current state."
if(docile)
src << "<font color='blue'>You are feeling far too docile to do that.</font>"
return
src << "<span class = 'danger'>It only takes a few moments to render the dead host brain down into a nutrient-rich slurry...</span>"
replace_brain()
*/
// BRAIN WORM ZOMBIES AAAAH.
/mob/living/simple_mob/animal/borer/proc/replace_brain()
var/mob/living/carbon/human/H = host
if(!istype(host))
src << "This host does not have a suitable brain."
return
src << "<span class = 'danger'>You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H].</span>"
H.add_language("Cortical Link")
if(host.stat == 2)
H.verbs |= /mob/living/carbon/human/proc/jumpstart
H.verbs |= /mob/living/carbon/human/proc/psychic_whisper
H.verbs |= /mob/living/carbon/human/proc/tackle
H.verbs |= /mob/living/carbon/proc/spawn_larvae
if(H.client)
H.ghostize(0)
if(src.mind)
src.mind.special_role = "Borer Husk"
src.mind.transfer_to(host)
H.ChangeToHusk()
var/obj/item/organ/internal/borer/B = new(H)
H.internal_organs_by_name["brain"] = B
H.internal_organs |= B
var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD)
affecting.implants -= src
var/s2h_id = src.computer_id
var/s2h_ip= src.lastKnownIP
src.computer_id = null
src.lastKnownIP = null
if(!H.computer_id)
H.computer_id = s2h_id
if(!H.lastKnownIP)
H.lastKnownIP = s2h_ip
/mob/living/simple_mob/animal/borer/verb/secrete_chemicals()
set category = "Abilities"
set name = "Secrete Chemicals"
set desc = "Push some chemicals into your host's bloodstream."
if(!host)
src << "You are not inside a host body."
return
if(stat)
src << "You cannot secrete chemicals in your current state."
if(docile)
src << "<font color='blue'>You are feeling far too docile to do that.</font>"
return
if(chemicals < 50)
src << "You don't have enough chemicals!"
var/chem = input("Select a chemical to secrete.", "Chemicals") as null|anything in list("alkysine","bicaridine","hyperzine","tramadol")
if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check.
return
src << "<font color='red'><B>You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.</B></font>"
host.reagents.add_reagent(chem, 10)
chemicals -= 50
/mob/living/simple_mob/animal/borer/verb/dominate_victim()
set category = "Abilities"
set name = "Paralyze Victim"
set desc = "Freeze the limbs of a potential host with supernatural fear."
if(world.time - used_dominate < 150)
src << "You cannot use that ability again so soon."
return
if(host)
src << "You cannot do that from within a host body."
return
if(src.stat)
src << "You cannot do that in your current state."
return
var/list/choices = list()
for(var/mob/living/carbon/C in view(3,src))
if(C.stat != 2)
choices += C
if(world.time - used_dominate < 150)
src << "You cannot use that ability again so soon."
return
var/mob/living/carbon/M = input(src,"Who do you wish to dominate?") in null|choices
if(!M || !src) return
if(M.has_brain_worms())
src << "You cannot infest someone who is already infested!"
return
src << "<font color='red'>You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.</font>"
M << "<font color='red'>You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.</font>"
M.Weaken(10)
used_dominate = world.time
/mob/living/simple_mob/animal/borer/verb/bond_brain()
set category = "Abilities"
set name = "Assume Control"
set desc = "Fully connect to the brain of your host."
if(!host)
src << "You are not inside a host body."
return
if(src.stat)
src << "You cannot do that in your current state."
return
if(docile)
src << "<font color='blue'>You are feeling far too docile to do that.</font>"
return
src << "You begin delicately adjusting your connection to the host brain..."
spawn(100+(host.brainloss*5))
if(!host || !src || controlling)
return
else
src << "<font color='red'><B>You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.</B></font>"
host << "<font color='red'><B>You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.</B></font>"
host.add_language("Cortical Link")
// host -> brain
var/h2b_id = host.computer_id
var/h2b_ip= host.lastKnownIP
host.computer_id = null
host.lastKnownIP = null
qdel(host_brain)
host_brain = new(src)
host_brain.ckey = host.ckey
host_brain.name = host.name
if(!host_brain.computer_id)
host_brain.computer_id = h2b_id
if(!host_brain.lastKnownIP)
host_brain.lastKnownIP = h2b_ip
// self -> host
var/s2h_id = src.computer_id
var/s2h_ip= src.lastKnownIP
src.computer_id = null
src.lastKnownIP = null
host.ckey = src.ckey
if(!host.computer_id)
host.computer_id = s2h_id
if(!host.lastKnownIP)
host.lastKnownIP = s2h_ip
controlling = 1
host.verbs += /mob/living/carbon/proc/release_control
host.verbs += /mob/living/carbon/proc/punish_host
host.verbs += /mob/living/carbon/proc/spawn_larvae
return
/mob/living/carbon/human/proc/jumpstart()
set category = "Abilities"
set name = "Revive Host"
set desc = "Send a jolt of electricity through your host, reviving them."
if(stat != 2)
usr << "Your host is already alive."
return
verbs -= /mob/living/carbon/human/proc/jumpstart
visible_message("<span class='warning'>With a hideous, rattling moan, [src] shudders back to life!</span>")
rejuvenate()
restore_blood()
fixblood()
update_canmove()
@@ -0,0 +1,156 @@
GLOBAL_VAR_CONST(MAX_CHICKENS, 50) // How many chickens CAN we have?
GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have?
/mob/living/simple_mob/animal/passive/chicken
name = "chicken"
desc = "Hopefully the eggs are good this season."
tt_desc = "E Gallus gallus"
icon_state = "chicken"
icon_living = "chicken"
icon_dead = "chicken_dead"
health = 10
maxHealth = 10
pass_flags = PASSTABLE
mob_size = MOB_SMALL
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = list("kicked")
has_langs = list("Bird")
say_list_type = /datum/say_list/chicken
meat_amount = 2
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/eggsleft = 0
var/body_color
/mob/living/simple_mob/animal/passive/chicken/New()
..()
if(!body_color)
body_color = pick( list("brown","black","white") )
icon_state = "chicken_[body_color]"
icon_living = "chicken_[body_color]"
icon_dead = "chicken_[body_color]_dead"
pixel_x = rand(-6, 6)
pixel_y = rand(0, 10)
GLOB.chicken_count += 1
/mob/living/simple_mob/animal/passive/chicken/Destroy()
..()
GLOB.chicken_count -= 1
/mob/living/simple_mob/animal/passive/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob)
if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown)) //feedin' dem chickens
var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O
if(G.seed && G.seed.kitchen_tag == "wheat")
if(!stat && eggsleft < 8)
user.visible_message("<font color='blue'>[user] feeds [O] to [name]! It clucks happily.</font>","<font color='blue'>You feed [O] to [name]! It clucks happily.</font>")
user.drop_item()
qdel(O)
eggsleft += rand(1, 4)
else
to_chat(user, "<font color='blue'>[name] doesn't seem hungry!</font>")
else
to_chat(user, "[name] doesn't seem interested in that.")
else
..()
/mob/living/simple_mob/animal/passive/chicken/Life()
. =..()
if(!.)
return
if(!stat && prob(3) && eggsleft > 0)
visible_message("[src] [pick("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")]")
eggsleft--
var/obj/item/weapon/reagent_containers/food/snacks/egg/E = new(get_turf(src))
E.pixel_x = rand(-6,6)
E.pixel_y = rand(-6,6)
if(GLOB.chicken_count < GLOB.MAX_CHICKENS && prob(10))
processing_objects.Add(E)
/obj/item/weapon/reagent_containers/food/snacks/egg/var/amount_grown = 0
// This only starts normally if there are less than MAX_CHICKENS chickens
/obj/item/weapon/reagent_containers/food/snacks/egg/process()
if(isturf(loc))
amount_grown += rand(1,2)
if(amount_grown >= 100)
visible_message("[src] hatches with a quiet cracking sound.")
new /mob/living/simple_mob/animal/passive/chick(get_turf(src))
processing_objects.Remove(src)
qdel(src)
else
processing_objects.Remove(src)
/mob/living/simple_mob/animal/passive/chick
name = "chick"
desc = "Adorable! They make such a racket though."
tt_desc = "E Gallus gallus"
icon_state = "chick"
icon_living = "chick"
icon_dead = "chick_dead"
icon_gib = "chick_gib"
health = 1
maxHealth = 1
pass_flags = PASSTABLE | PASSGRILLE
mob_size = MOB_MINISCULE
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = list("kicked")
has_langs = list("Bird")
say_list_type = /datum/say_list/chick
meat_amount = 1
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/amount_grown = 0
/mob/living/simple_mob/animal/passive/chick/New()
..()
pixel_x = rand(-6, 6)
pixel_y = rand(0, 10)
/mob/living/simple_mob/animal/passive/chick/Life()
. =..()
if(!.)
return
if(!stat)
amount_grown += rand(1,2)
if(amount_grown >= 100)
new /mob/living/simple_mob/animal/passive/chicken(src.loc)
qdel(src)
// Say Lists
/datum/say_list/chicken
speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.")
emote_hear = list("clucks","croons")
emote_see = list("pecks at the ground","flaps its wings viciously")
/datum/say_list/chick
speak = list("Cherp.","Cherp?","Chirrup.","Cheep!")
emote_hear = list("cheeps")
emote_see = list("pecks at the ground","flaps its tiny wings")
@@ -0,0 +1,67 @@
/mob/living/simple_mob/animal/passive/cow
name = "cow"
desc = "Known for their milk, just don't tip them over."
tt_desc = "E Bos taurus"
icon_state = "cow"
icon_living = "cow"
icon_dead = "cow_dead"
icon_gib = "cow_gib"
health = 50
maxHealth = 50
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
attacktext = list("kicked")
say_list_type = /datum/say_list/cow
meat_amount = 6
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/datum/reagents/udder = null
/mob/living/simple_mob/animal/passive/cow/New()
udder = new(50)
udder.my_atom = src
..()
/mob/living/simple_mob/animal/passive/cow/attackby(var/obj/item/O as obj, var/mob/user as mob)
var/obj/item/weapon/reagent_containers/glass/G = O
if(stat == CONSCIOUS && istype(G) && G.is_open_container())
user.visible_message("<span class='notice'>[user] milks [src] using \the [O].</span>")
var/transfered = udder.trans_id_to(G, "milk", rand(5,10))
if(G.reagents.total_volume >= G.volume)
to_chat(user, "<font color='red'>The [O] is full.</font>")
if(!transfered)
to_chat(user, "<font color='red'>The udder is dry. Wait a bit longer...</font>")
else
..()
/mob/living/simple_mob/animal/passive/cow/Life()
. = ..()
if(stat == CONSCIOUS)
if(udder && prob(5))
udder.add_reagent("milk", rand(5, 10))
/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/living/carbon/M as mob)
if(!stat && M.a_intent == I_DISARM && icon_state != icon_dead)
M.visible_message("<span class='warning'>[M] tips over [src].</span>","<span class='notice'>You tip over [src].</span>")
Weaken(30)
icon_state = icon_dead
spawn(rand(20,50))
if(!stat && M)
icon_state = icon_living
var/list/responses = list( "[src] looks at you imploringly.",
"[src] looks at you pleadingly",
"[src] looks at you with a resigned expression.",
"[src] seems resigned to its fate.")
to_chat(M, pick(responses))
else
..()
/datum/say_list/cow
speak = list("moo?","moo","MOOOOOO")
emote_hear = list("brays", "moos","moos hauntingly")
emote_see = list("shakes its head")
@@ -0,0 +1,81 @@
/mob/living/simple_mob/animal/goat
name = "goat"
desc = "Not known for their pleasant disposition."
tt_desc = "E Oreamnos americanus"
icon_state = "goat"
icon_living = "goat"
icon_dead = "goat_dead"
faction = "goat"
health = 40
maxHealth = 40
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
melee_damage_lower = 1
melee_damage_upper = 5
attacktext = list("kicked")
say_list_type = /datum/say_list/goat
ai_holder_type = /datum/ai_holder/simple_mob/retaliate
meat_amount = 4
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
var/datum/reagents/udder = null
/mob/living/simple_mob/animal/goat/New()
udder = new(50)
udder.my_atom = src
..()
/mob/living/simple_mob/animal/goat/Life()
. = ..()
if(.)
if(stat == CONSCIOUS)
if(udder && prob(5))
udder.add_reagent("milk", rand(5, 10))
if(locate(/obj/effect/plant) in loc)
var/obj/effect/plant/SV = locate() in loc
SV.die_off(1)
if(locate(/obj/machinery/portable_atmospherics/hydroponics/soil/invisible) in loc)
var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/SP = locate() in loc
qdel(SP)
if(!pulledby)
var/obj/effect/plant/food
food = locate(/obj/effect/plant) in oview(5,loc)
if(food)
var/step = get_step_to(src, food, 0)
Move(step)
/mob/living/simple_mob/animal/goat/Move()
..()
if(!stat)
for(var/obj/effect/plant/SV in loc)
SV.die_off(1)
/mob/living/simple_mob/animal/goat/attackby(var/obj/item/O as obj, var/mob/user as mob)
var/obj/item/weapon/reagent_containers/glass/G = O
if(stat == CONSCIOUS && istype(G) && G.is_open_container())
user.visible_message("<span class='notice'>[user] milks [src] using \the [O].</span>")
var/transfered = udder.trans_id_to(G, "milk", rand(5,10))
if(G.reagents.total_volume >= G.volume)
to_chat(user, "<font color='red'>The [O] is full.</font>")
if(!transfered)
to_chat(user, "<font color='red'>The udder is dry. Wait a bit longer...</font>")
else
..()
/datum/say_list/goat
speak = list("EHEHEHEHEH","eh?")
emote_hear = list("brays")
emote_see = list("shakes its head", "stamps a foot", "glares around")
// say_got_target doesn't seem to handle emotes, but keeping this here in case someone wants to make it work
// say_got_target = list("<span class='warning'>[src] gets an evil-looking gleam in their eye.</span>")
@@ -0,0 +1,64 @@
/*
Spiders come in various types, and are a fairly common enemy both inside and outside the station.
Their attacks can inject reagents, which can cause harm long after the spider is killed.
Thick material will prevent injections, similar to other means of injections.
*/
// The base spider, in the 'walking tank' family.
/mob/living/simple_mob/animal/giant_spider
name = "giant spider"
desc = "Furry and brown, it makes you shudder to look at it. This one has deep red eyes."
tt_desc = "Atrax robustus gigantus"
icon_state = "guard"
icon_living = "guard"
icon_dead = "guard_dead"
has_eye_glow = TRUE
faction = "spiders"
maxHealth = 200
health = 200
pass_flags = PASSTABLE
movement_cooldown = 10
poison_resist = 0.5
see_in_dark = 10
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "punches"
melee_damage_lower = 18
melee_damage_upper = 30
attack_sharp = 1
attack_edge = 1
attack_sound = 'sound/weapons/bite.ogg'
heat_damage_per_tick = 20
cold_damage_per_tick = 20
minbodytemp = 175 // So they can all survive Sif without having to be classed under /sif subtype.
speak_emote = list("chitters")
meat_amount = 1
meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat/spidermeat
say_list_type = /datum/say_list/spider
var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks.
var/poison_chance = 10 // Chance for injection to occur.
var/poison_per_bite = 5 // Amount added per injection.
/mob/living/simple_mob/animal/giant_spider/apply_melee_effects(var/atom/A)
if(isliving(A))
var/mob/living/L = A
if(L.reagents)
var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD)
if(L.can_inject(src, null, target_zone))
inject_poison(L, target_zone)
// Does actual poison injection, after all checks passed.
/mob/living/simple_mob/animal/giant_spider/proc/inject_poison(mob/living/L, target_zone)
if(prob(poison_chance))
to_chat(L, "<span class='warning'>You feel a tiny prick.</span>")
L.reagents.add_reagent(poison_type, poison_per_bite)
@@ -0,0 +1,67 @@
// Carriers are not too dangerous on their own, but they create more spiders when dying.
/mob/living/simple_mob/animal/giant_spider/carrier
desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes."
icon_state = "carrier"
icon_living = "carrier"
icon_dead = "carrier_dead"
maxHealth = 100
health = 100
melee_damage_lower = 8
melee_damage_upper = 25
poison_per_bite = 3
poison_type = "chloralhydrate"
movement_cooldown = 5
player_msg = "Upon dying, you will release a swarm of spiderlings or young hunter spiders.<br>\
If a spider emerges, you will be placed in control of it."
var/spiderling_count = 0
var/spiderling_type = /obj/effect/spider/spiderling
var/swarmling_type = /mob/living/simple_mob/animal/giant_spider/hunter
var/swarmling_faction = "spiders"
var/swarmling_prob = 10 // Odds that a spiderling will be a swarmling instead.
/mob/living/simple_mob/animal/giant_spider/carrier/initialize()
spiderling_count = rand(5, 10)
adjust_scale(1.2)
return ..()
/mob/living/simple_mob/animal/giant_spider/carrier/death()
visible_message(span("warning", "\The [src]'s abdomen splits as it rolls over, spiderlings crawling from the wound.") )
spawn(1)
var/list/new_spiders = list()
for(var/i = 1 to spiderling_count)
if(prob(swarmling_prob) && src)
var/mob/living/simple_mob/animal/giant_spider/swarmling = new swarmling_type(src.loc)
var/swarm_health = Floor(swarmling.maxHealth * 0.4)
var/swarm_dam_lower = Floor(melee_damage_lower * 0.4)
var/swarm_dam_upper = Floor(melee_damage_upper * 0.4)
swarmling.name = "spiderling"
swarmling.maxHealth = swarm_health
swarmling.health = swarm_health
swarmling.melee_damage_lower = swarm_dam_lower
swarmling.melee_damage_upper = swarm_dam_upper
swarmling.faction = swarmling_faction
swarmling.adjust_scale(0.75)
new_spiders += swarmling
else if(src)
var/obj/effect/spider/spiderling/child = new spiderling_type(src.loc)
child.skitter()
else // We might've gibbed or got deleted.
break
// Transfer our player to their new body, if RNG provided one.
if(new_spiders.len && client)
var/mob/living/simple_mob/animal/giant_spider/new_body = pick(new_spiders)
new_body.key = src.key
return ..()
/mob/living/simple_mob/animal/giant_spider/carrier/recursive
desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes. \
You have a distinctly <font face='comic sans ms'>bad</font> feeling about this."
swarmling_type = /mob/living/simple_mob/animal/giant_spider/carrier/recursive
@@ -0,0 +1,45 @@
// Electric spiders fire taser-like beams at their enemies.
// TODO: AI
/mob/living/simple_mob/animal/giant_spider/electric
desc = "Spined and yellow, it makes you shudder to look at it. This one has flickering gold eyes."
icon_state = "spark"
icon_living = "spark"
icon_dead = "spark_dead"
maxHealth = 210
health = 210
taser_kill = 0 //It -is- the taser.
base_attack_cooldown = 10
projectilesound = 'sound/weapons/taser2.ogg'
projectiletype = /obj/item/projectile/beam/stun/electric_spider
melee_damage_lower = 10
melee_damage_upper = 25
poison_chance = 15
poison_per_bite = 3
poison_type = "stimm"
shock_resist = 0.75
player_msg = "You can fire a taser-like ranged attack by clicking on an enemy or tile at a distance."
ai_holder_type = /datum/ai_holder/simple_mob/ranged/electric_spider
/obj/item/projectile/beam/stun/electric_spider
name = "stun beam"
agony = 20
// The electric spider's AI.
/datum/ai_holder/simple_mob/ranged/electric_spider
/datum/ai_holder/simple_mob/ranged/electric_spider/max_range(atom/movable/AM)
if(isliving(AM))
var/mob/living/L = AM
if(L.incapacitated(INCAPACITATION_DISABLED) || L.stat == UNCONSCIOUS) // If our target is stunned, go in for the kill.
return 1
return ..() // Do ranged if possible otherwise.
@@ -0,0 +1,20 @@
// Frost spiders inject cryotoxin, slowing people down (which is very bad if trying to run from spiders).
/mob/living/simple_mob/animal/giant_spider/frost
desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes."
icon_state = "frost"
icon_living = "frost"
icon_dead = "frost_dead"
maxHealth = 175
health = 175
poison_per_bite = 5
poison_type = "cryotoxin"
heat_resist = -0.50
cold_resist = 0.75
// Sif variant with a somewhat different desc.
/mob/living/simple_mob/animal/giant_spider/frost/sif
desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes. \
It isn't native to Sif."
@@ -0,0 +1,165 @@
// Hunters are fast, fragile, and possess a leaping attack.
// The leaping attack is somewhat telegraphed and can be dodged if one is paying attention.
// The AI would've followed up after a successful leap with dragging the downed victim away, but the dragging code was too janky.
/mob/living/simple_mob/animal/giant_spider/hunter
desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes."
icon_state = "hunter"
icon_living = "hunter"
icon_dead = "hunter_dead"
maxHealth = 120
health = 120
poison_per_bite = 5
melee_damage_lower = 9
melee_damage_upper = 15
movement_cooldown = 0 // Hunters are FAST.
ai_holder_type = /datum/ai_holder/simple_mob/melee/hunter_spider
player_msg = "You are very fast, and <b>can perform a leaping attack</b> by clicking on someone from a short distance away.<br>\
If the leap succeeds, the target will be knocked down briefly and you will be on top of them.<br>\
Note that there is a short delay before you leap!<br>\
In addition, you will do more damage to incapacitated opponents."
// Leaping is a special attack, so these values determine when leap can happen.
// Leaping won't occur if its on cooldown.
special_attack_min_range = 2
special_attack_max_range = 4
special_attack_cooldown = 10 SECONDS
var/leap_warmup = 1 SECOND // How long the leap telegraphing is.
var/leap_sound = 'sound/weapons/spiderlunge.ogg'
// Multiplies damage if the victim is stunned in some form, including a successful leap.
/mob/living/simple_mob/animal/giant_spider/hunter/apply_bonus_melee_damage(atom/A, damage_amount)
if(isliving(A))
var/mob/living/L = A
if(L.incapacitated(INCAPACITATION_DISABLED))
return damage_amount * 1.5
return ..()
// The actual leaping attack.
/mob/living/simple_mob/animal/giant_spider/hunter/do_special_attack(atom/A)
set waitfor = FALSE
set_AI_busy(TRUE)
// Telegraph, since getting stunned suddenly feels bad.
do_windup_animation(A, leap_warmup)
sleep(leap_warmup) // For the telegraphing.
// Do the actual leap.
status_flags |= LEAPING // Lets us pass over everything.
visible_message(span("danger","\The [src] leaps at \the [A]!"))
throw_at(get_step(get_turf(A), get_turf(src)), special_attack_max_range+1, 1, src)
playsound(src, leap_sound, 75, 1)
sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false.
if(status_flags & LEAPING)
status_flags &= ~LEAPING // Revert special passage ability.
var/turf/T = get_turf(src) // Where we landed. This might be different than A's turf.
. = FALSE
// Now for the stun.
var/mob/living/victim = null
for(var/mob/living/L in T) // So player-controlled spiders only need to click the tile to stun them.
if(L == src)
continue
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap"))
continue // We were blocked.
victim = L
break
if(victim)
victim.Weaken(2)
victim.visible_message(span("danger","\The [src] knocks down \the [victim]!"))
to_chat(victim, span("critical", "\The [src] jumps on you!"))
. = TRUE
set_AI_busy(FALSE)
// var/obj/item/weapon/grab/G = new(src, victim)
// put_in_active_hand(G)
// G.synch()
// G.affecting = victim
// victim.LAssailant = src
// visible_message("<span class='warning'>\The [src] seizes \the [victim] aggressively!</span>")
// do_attack_animation(victim)
// This AI would've isolated people it stuns with its 'leap' attack, by dragging them away.
/datum/ai_holder/simple_mob/melee/hunter_spider
/*
/datum/ai_holder/simple_mob/melee/hunter_spider/post_special_attack(mob/living/L)
drag_away(L)
// Called after a successful leap.
/datum/ai_holder/simple_mob/melee/hunter_spider/proc/drag_away(mob/living/L)
world << "Doing drag_away attack on [L]"
if(!istype(L))
world << "Invalid type."
return FALSE
// If they didn't get stunned, then don't bother.
if(!L.incapacitated(INCAPACITATION_DISABLED))
world << "Not incapcitated."
return FALSE
// Grab them.
if(!holder.start_pulling(L))
world << "Failed to pull."
return FALSE
holder.visible_message(span("danger","\The [holder] starts to drag \the [L] away!"))
var/list/allies = list()
var/list/enemies = list()
for(var/mob/living/thing in hearers(vision_range, holder))
if(thing == holder || thing == L) // Don't count ourselves or the thing we just started pulling.
continue
if(holder.IIsAlly(thing))
allies += thing
else
enemies += thing
// First priority: Move our victim to our friends.
if(allies.len)
world << "Going to move to ally"
give_destination(get_turf(pick(allies)), min_distance = 2, combat = TRUE) // This will switch our stance.
// Second priority: Move our victim away from their friends.
// There's a chance of it derping and pulling towards enemies if there's more than two people.
// Preventing that will likely be both a lot of effort for developers and the CPU.
else if(enemies.len)
world << "Going to move away from enemies"
var/mob/living/hostile = pick(enemies)
var/turf/move_to = get_turf(hostile)
for(var/i = 1 to vision_range) // Move them this many steps away from their friend.
move_to = get_step_away(move_to, L, 7)
if(move_to)
give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance.
// Third priority: Move our victim SOMEWHERE away from where they were.
else
world << "Going to move away randomly"
var/turf/move_to = get_turf(L)
move_to = get_step(move_to, pick(cardinal))
for(var/i = 1 to vision_range) // Move them this many steps away from where they were before.
move_to = get_step_away(move_to, L, 7)
if(move_to)
give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance.
*/
@@ -0,0 +1,103 @@
// Lurkers are somewhat similar to Hunters, however the big difference is that Lurkers have an imperfect cloak.
// Their AI will try to do hit and run tactics, striking the enemy when its "cloaked", for bonus damage and a stun.
// They keep attacking until the stun ends, then retreat to cloak again and repeat the cycle.
// Hitting the spider before it does its ambush attack will break the cloak and make the spider flee.
/mob/living/simple_mob/animal/giant_spider/lurker
desc = "Translucent and white, it makes you shudder to look at it. This one has incandescent red eyes."
icon_state = "lurker"
icon_living = "lurker"
icon_dead = "lurker_dead"
maxHealth = 100
health = 100
poison_per_bite = 5
movement_cooldown = 5
melee_damage_lower = 10
melee_damage_upper = 10
poison_chance = 30
poison_type = "cryptobiolin"
poison_per_bite = 1
player_msg = "You have an imperfect, but automatic stealth. If you attack something while 'hidden', then \
you will do bonus damage, stun the target, and unstealth for a period of time.<br>\
Getting attacked will also break your stealth."
ai_holder_type = /datum/ai_holder/simple_mob/melee/hit_and_run
var/cloaked = FALSE
var/cloaked_alpha = 45 // Lower = Harder to see.
var/cloaked_bonus_damage = 30 // This is added on top of the normal melee damage.
var/cloaked_weaken_amount = 3 // How long to stun for.
var/cloak_cooldown = 10 SECONDS // Amount of time needed to re-cloak after losing it.
var/last_uncloak = 0 // world.time
/mob/living/simple_mob/animal/giant_spider/lurker/proc/cloak()
if(cloaked)
return
animate(src, alpha = cloaked_alpha, time = 1 SECOND)
cloaked = TRUE
/mob/living/simple_mob/animal/giant_spider/lurker/proc/uncloak()
last_uncloak = world.time // This is assigned even if it isn't cloaked already, to 'reset' the timer if the spider is continously getting attacked.
if(!cloaked)
return
animate(src, alpha = initial(alpha), time = 1 SECOND)
cloaked = FALSE
// Check if cloaking if possible.
/mob/living/simple_mob/animal/giant_spider/lurker/proc/can_cloak()
if(stat)
return FALSE
if(last_uncloak + cloak_cooldown > world.time)
return FALSE
return TRUE
// Called by things that break cloaks, like Technomancer wards.
/mob/living/simple_mob/animal/giant_spider/lurker/break_cloak()
uncloak()
/mob/living/simple_mob/animal/giant_spider/lurker/is_cloaked()
return cloaked
// Cloaks the spider automatically, if possible.
/mob/living/simple_mob/animal/giant_spider/lurker/handle_special()
if(!cloaked && can_cloak())
cloak()
// Applies bonus base damage if cloaked.
/mob/living/simple_mob/animal/giant_spider/lurker/apply_bonus_melee_damage(atom/A, damage_amount)
if(cloaked)
return damage_amount + cloaked_bonus_damage
return ..()
// Applies stun, then uncloaks.
/mob/living/simple_mob/animal/giant_spider/lurker/apply_melee_effects(atom/A)
if(cloaked)
if(isliving(A))
var/mob/living/L = A
L.Weaken(cloaked_weaken_amount)
to_chat(L, span("danger", "\The [src] ambushes you!"))
playsound(L, 'sound/weapons/spiderlunge.ogg', 75, 1)
uncloak()
..() // For the poison.
// Force uncloaking if attacked.
/mob/living/simple_mob/animal/giant_spider/lurker/bullet_act(obj/item/projectile/P)
. = ..()
break_cloak()
/mob/living/simple_mob/animal/giant_spider/lurker/hit_with_weapon(obj/item/O, mob/living/user, effective_force, hit_zone)
. = ..()
break_cloak()
@@ -0,0 +1,230 @@
// Nurses, they create webs and eggs.
// They're fragile but their attacks can cause horrifying consequences.
/mob/living/simple_mob/animal/giant_spider/nurse
desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes."
icon_state = "nurse"
icon_living = "nurse"
icon_dead = "nurse_dead"
maxHealth = 40
health = 40
movement_cooldown = 5 // A bit faster so that they can inject the eggs easier.
melee_damage_lower = 5 // Doesn't do a lot of damage, since the goal is to make more spiders with egg attacks.
melee_damage_upper = 10
poison_per_bite = 5
poison_type = "stoxin"
player_msg = "You can spin webs on an adjacent tile, or cocoon an object by clicking on it.<br>\
You can also cocoon a dying or dead entity by clicking on them, and you will gain charges for egg-laying.<br>\
To lay eggs, click a nearby tile. Laying eggs will deplete a charge."
ai_holder_type = /datum/ai_holder/simple_mob/melee/nurse_spider
var/fed = 0 // Counter for how many egg laying 'charges' the spider has.
var/egg_inject_chance = 25 // One in four chance to get eggs.
var/egg_type = /obj/effect/spider/eggcluster/small
var/web_type = /obj/effect/spider/stickyweb/dark
/mob/living/simple_mob/animal/giant_spider/nurse/inject_poison(mob/living/L, target_zone)
..() // Inject the stoxin here.
if(ishuman(L) && prob(egg_inject_chance))
var/mob/living/carbon/human/H = L
var/obj/item/organ/external/O = H.get_organ(target_zone)
if(O)
var/eggcount = 0
for(var/obj/effect/spider/eggcluster/E in O.implants)
eggcount++
if(!eggcount)
var/eggs = new egg_type(O, src)
O.implants += eggs
to_chat(H, span("critical", "\The [src] injects something into your [O.name]!") ) // Oh god its laying eggs in me!
// Webs target in a web if able to.
/mob/living/simple_mob/animal/giant_spider/nurse/attack_target(atom/A)
if(isturf(A))
if(fed)
return lay_eggs(A)
return web_tile(A)
if(isliving(A))
var/mob/living/L = A
if(!L.stat)
return ..()
if(!istype(A, /atom/movable))
return
var/atom/movable/AM = A
if(AM.anchored)
return ..()
return spin_cocoon(AM)
/mob/living/simple_mob/animal/giant_spider/nurse/proc/spin_cocoon(atom/movable/AM)
if(!istype(AM))
return FALSE // We can't cocoon walls sadly.
visible_message(span("notice", "\The [src] begins to secrete a sticky substance around \the [AM].") )
// Get our AI to stay still.
set_AI_busy(TRUE)
if(!do_mob(src, AM, 5 SECONDS))
set_AI_busy(FALSE)
to_chat(src, span("warning", "You need to stay still to spin a web around \the [AM]."))
return FALSE
set_AI_busy(FALSE)
if(!AM) // Make sure it didn't get deleted for whatever reason.
to_chat(src, span("warning", "Whatever you were spinning a web for, its no longer there..."))
return FALSE
if(!isturf(AM.loc))
to_chat(src, span("warning", "You can't spin \the [AM] in a web while it is inside \the [AM.loc]."))
return FALSE
if(!Adjacent(AM))
to_chat(src, span("warning", "You need to be next to \the [AM] to spin it into a web."))
return FALSE
// Finally done with the checks.
var/obj/effect/spider/cocoon/C = new(AM.loc)
var/large_cocoon = FALSE
for(var/mob/living/L in C.loc)
if(istype(L, /mob/living/simple_mob/animal/giant_spider)) // Cannibalism is bad.
continue
fed++
visible_message(span("warning","\The [src] sticks a proboscis into \the [L], and sucks a viscous substance out."))
to_chat(src, span("notice", "You've fed upon \the [L], and can now lay [fed] cluster\s of eggs."))
L.forceMove(C)
large_cocoon = TRUE
break
// This part's pretty stupid.
for(var/obj/O in C.loc)
if(!O.anchored)
O.forceMove(C)
// Todo: Put this code on the cocoon object itself?
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
return TRUE
/mob/living/simple_mob/animal/giant_spider/nurse/handle_special()
set waitfor = FALSE
if(get_AI_stance() == STANCE_IDLE && !is_AI_busy() && isturf(loc))
if(fed)
lay_eggs(loc)
else
web_tile(loc)
/mob/living/simple_mob/animal/giant_spider/nurse/proc/web_tile(turf/T)
if(!istype(T))
return FALSE
var/obj/effect/spider/stickyweb/W = locate() in T
if(W)
return FALSE // Already got webs here.
visible_message(span("notice", "\The [src] begins to secrete a sticky substance.") )
// Get our AI to stay still.
set_AI_busy(TRUE)
if(!do_mob(src, T, 5 SECONDS))
set_AI_busy(FALSE)
to_chat(src, span("warning", "You need to stay still to spin a web on \the [T]."))
return FALSE
W = locate() in T
if(W)
return FALSE // Spamclick protection.
set_AI_busy(FALSE)
new web_type(T)
return TRUE
/mob/living/simple_mob/animal/giant_spider/nurse/proc/lay_eggs(turf/T)
if(!istype(T))
return FALSE
if(!fed)
return FALSE
var/obj/effect/spider/eggcluster/E = locate() in T
if(E)
return FALSE // Already got eggs here.
visible_message(span("notice", "\The [src] begins to lay a cluster of eggs.") )
// Get our AI to stay still.
set_AI_busy(TRUE)
if(!do_mob(src, T, 5 SECONDS))
set_AI_busy(FALSE)
to_chat(src, span("warning", "You need to stay still to lay eggs on \the [T]."))
return FALSE
E = locate() in T
if(E)
return FALSE // Spamclick protection.
set_AI_busy(FALSE)
new egg_type(T)
fed--
return TRUE
// Variant that 'blocks' light (by being a negative light source).
// This is done to make webbed rooms scary and allow for spiders on the other side of webs to see prey.
/obj/effect/spider/stickyweb/dark
name = "dense web"
desc = "It's sticky, and blocks a lot of light."
light_color = "#FFFFFF"
light_range = 2
light_power = -3
// This is still stupid, but whatever.
/mob/living/simple_mob/animal/giant_spider/nurse/hat
desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes and a tiny nurse hat."
icon_state = "nursemed"
icon_living = "nursemed"
icon_dead = "nursemed_dead"
// The AI for nurse spiders. Wraps things in webs by 'attacking' them.
/datum/ai_holder/simple_mob/melee/nurse_spider
wander = TRUE
base_wander_delay = 8
cooperative = FALSE // So we don't ask our spider friends to attack things we're webbing. This might also make them stay at the base if their friends find tasty explorers.
// Get us unachored objects as an option as well.
/datum/ai_holder/simple_mob/melee/nurse_spider/list_targets()
. = ..()
var/static/alternative_targets = typecacheof(list(/obj/item, /obj/structure))
for(var/AT in typecache_filter_list(range(vision_range, holder), alternative_targets))
var/obj/O = AT
if(can_see(holder, O, vision_range) && !O.anchored)
. += O
// Select an obj if no mobs are around.
/datum/ai_holder/melee/nurse_spider/pick_target(list/targets)
var/mobs_only = locate(/mob/living) in targets // If a mob is in the list of targets, then ignore objects.
if(mobs_only)
for(var/A in targets)
if(!isliving(A))
targets -= A
return ..(targets)
/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target)
. = ..()
if(!.) // Parent returned FALSE.
if(istype(the_target, /obj))
var/obj/O = the_target
if(!O.anchored)
return TRUE
@@ -0,0 +1,21 @@
// Pepper spiders inject condensed capsaicin into their victims.
/mob/living/simple_mob/animal/giant_spider/pepper
desc = "Red and brown, it makes you shudder to look at it. This one has glinting red eyes."
icon_state = "pepper"
icon_living = "pepper"
icon_dead = "pepper_dead"
maxHealth = 210
health = 210
melee_damage_lower = 8
melee_damage_upper = 15
poison_chance = 20
poison_per_bite = 5
poison_type = "condensedcapsaicin_v"
/mob/living/simple_mob/animal/giant_spider/pepper/initialize()
adjust_scale(1.1)
return ..()
@@ -0,0 +1,56 @@
// Phorogenic spiders explode when they die.
// You really shouldn't melee them.
/mob/living/simple_mob/animal/giant_spider/phorogenic
desc = "Crystalline and purple, it makes you shudder to look at it. This one has haunting purple eyes."
icon_state = "phoron"
icon_living = "phoron"
icon_dead = "phoron_dead"
maxHealth = 225
health = 225
taser_kill = FALSE //You will need more than a peashooter to kill the juggernaut.
melee_damage_lower = 25
melee_damage_upper = 40
attack_armor_pen = 15
movement_cooldown = 15
poison_chance = 30
poison_per_bite = 0.5
poison_type = "phoron"
var/exploded = FALSE
var/explosion_dev_range = 1
var/explosion_heavy_range = 2
var/explosion_light_range = 4
var/explosion_flash_range = 6 // This doesn't do anything iirc.
var/explosion_delay_lower = 1 SECOND // Lower bound for explosion delay.
var/explosion_delay_upper = 2 SECONDS // Upper bound.
/mob/living/simple_mob/animal/giant_spider/phorogenic/initialize()
adjust_scale(1.25)
return ..()
/mob/living/simple_mob/animal/giant_spider/phorogenic/death()
visible_message(span("critical", "\The [src]'s body begins to rupture!"))
var/delay = rand(explosion_delay_lower, explosion_delay_upper)
spawn(0)
// Flash black and red as a warning.
for(var/i = 1 to delay)
if(i % 2 == 0)
color = "#000000"
else
color = "#FF0000"
sleep(1)
spawn(delay)
// The actual boom.
if(src && !exploded)
visible_message(span("danger", "\The [src]'s body detonates!"))
exploded = TRUE
explosion(src.loc, explosion_dev_range, explosion_heavy_range, explosion_light_range, explosion_flash_range)
return ..()
@@ -0,0 +1,20 @@
// Thermic spiders inject a special variant of thermite that burns someone from the inside.
/mob/living/simple_mob/animal/giant_spider/thermic
desc = "Mirage-cloaked and orange, it makes you shudder to look at it. This one has simmering orange eyes."
icon_state = "pit"
icon_living = "pit"
icon_dead = "pit_dead"
maxHealth = 175
health = 175
melee_damage_lower = 10
melee_damage_upper = 25
heat_resist = 0.75
cold_resist = -0.50
poison_chance = 30
poison_per_bite = 1
poison_type = "thermite_v"
@@ -0,0 +1,185 @@
// Tunnelers have a special ability that allows them to charge at an enemy by tunneling towards them.
// Any mobs inbetween the tunneler's path and the target will be stunned if the tunneler hits them.
// The target will suffer a stun as well, if the tunneler hits them at the end. A successful hit will stop the tunneler.
// If the target moves fast enough, the tunneler can miss, causing it to overshoot.
// If the tunneler hits a solid wall, the tunneler will suffer a stun.
/mob/living/simple_mob/animal/giant_spider/tunneler
desc = "Sandy and brown, it makes you shudder to look at it. This one has glittering yellow eyes."
icon_state = "tunneler"
icon_living = "tunneler"
icon_dead = "tunneler_dead"
maxHealth = 120
health = 120
melee_damage_lower = 10
melee_damage_upper = 10
poison_chance = 15
poison_per_bite = 3
poison_type = "serotrotium_v"
// ai_holder_type = /datum/ai_holder/simple_mob/melee/tunneler
player_msg = "You <b>can perform a tunneling attack</b> by clicking on someone from a distance.<br>\
There is a noticable travel delay as you tunnel towards the tile the target was at when you started the tunneling attack.<br>\
Any entities inbetween you and the targeted tile will be stunned for a brief period of time.<br>\
Whatever is on the targeted tile when you arrive will suffer a potent stun.<br>\
If nothing is on the targeted tile, you will overshoot and keep going for a few more tiles.<br>\
If you hit a wall or other solid structure during that time, you will suffer a lengthy stun and be vulnerable to more harm."
// Tunneling is a special attack, similar to the hunter's Leap.
special_attack_min_range = 2
special_attack_max_range = 6
special_attack_cooldown = 10 SECONDS
var/tunnel_warning = 0.5 SECONDS // How long the dig telegraphing is.
var/tunnel_tile_speed = 2 // How long to wait between each tile. Higher numbers result in an easier to dodge tunnel attack.
/mob/living/simple_mob/animal/giant_spider/tunneler/frequent
special_attack_cooldown = 5 SECONDS
/mob/living/simple_mob/animal/giant_spider/tunneler/fast
tunnel_tile_speed = 1
/mob/living/simple_mob/animal/giant_spider/tunneler/should_special_attack(atom/A)
// Make sure its possible for the spider to reach the target so it doesn't try to go through a window.
var/turf/destination = get_turf(A)
var/turf/starting_turf = get_turf(src)
var/turf/T = starting_turf
for(var/i = 1 to get_dist(starting_turf, destination))
if(T == destination)
break
T = get_step(T, get_dir(T, destination))
if(T.check_density(ignore_mobs = TRUE))
return FALSE
return T == destination
/mob/living/simple_mob/animal/giant_spider/tunneler/do_special_attack(atom/A)
set waitfor = FALSE
set_AI_busy(TRUE)
// Save where we're gonna go soon.
var/turf/destination = get_turf(A)
var/turf/starting_turf = get_turf(src)
// Telegraph to give a small window to dodge if really close.
do_windup_animation(A, tunnel_warning)
sleep(tunnel_warning) // For the telegraphing.
// Do the dig!
visible_message(span("danger","\The [src] tunnels towards \the [A]!"))
submerge()
if(handle_tunnel(destination) == FALSE)
set_AI_busy(FALSE)
emerge()
return FALSE
// Did we make it?
if(!(src in destination))
set_AI_busy(FALSE)
emerge()
return FALSE
var/overshoot = TRUE
// Test if something is at destination.
for(var/mob/living/L in destination)
if(L == src)
continue
visible_message(span("danger","\The [src] erupts from underneath, and hits \the [L]!"))
playsound(L, 'sound/weapons/heavysmash.ogg', 75, 1)
L.Weaken(3)
overshoot = FALSE
if(!overshoot) // We hit the target, or something, at destination, so we're done.
set_AI_busy(FALSE)
emerge()
return TRUE
// Otherwise we need to keep going.
to_chat(src, span("warning", "You overshoot your target!"))
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
var/dir_to_go = get_dir(starting_turf, destination)
for(var/i = 1 to rand(2, 4))
destination = get_step(destination, dir_to_go)
if(handle_tunnel(destination) == FALSE)
set_AI_busy(FALSE)
emerge()
return FALSE
set_AI_busy(FALSE)
emerge()
return FALSE
// Does the tunnel movement, stuns enemies, etc.
/mob/living/simple_mob/animal/giant_spider/tunneler/proc/handle_tunnel(turf/destination)
var/turf/T = get_turf(src) // Hold our current tile.
// Regular tunnel loop.
for(var/i = 1 to get_dist(src, destination))
if(stat)
return FALSE // We died or got knocked out on the way.
if(loc == destination)
break // We somehow got there early.
// Update T.
T = get_step(src, get_dir(src, destination))
if(T.check_density(ignore_mobs = TRUE))
to_chat(src, span("critical", "You hit something really solid!"))
playsound(src, "punch", 75, 1)
Weaken(5)
add_modifier(/datum/modifier/tunneler_vulnerable, 10 SECONDS)
return FALSE // Hit a wall.
// Stun anyone in our way.
for(var/mob/living/L in T)
playsound(L, 'sound/weapons/heavysmash.ogg', 75, 1)
L.Weaken(2)
// Get into the tile.
forceMove(T)
// Visuals and sound.
dig_under_floor(get_turf(src))
playsound(src, 'sound/effects/break_stone.ogg', 75, 1)
sleep(tunnel_tile_speed)
// For visuals.
/mob/living/simple_mob/animal/giant_spider/tunneler/proc/submerge()
alpha = 0
dig_under_floor(get_turf(src))
new /obj/effect/temporary_effect/tunneler_hole(get_turf(src))
// Ditto.
/mob/living/simple_mob/animal/giant_spider/tunneler/proc/emerge()
alpha = 255
dig_under_floor(get_turf(src))
new /obj/effect/temporary_effect/tunneler_hole(get_turf(src))
/mob/living/simple_mob/animal/giant_spider/tunneler/proc/dig_under_floor(turf/T)
new /obj/item/weapon/ore/glass(T) // This will be rather weird when on station but the alternative is too much work.
/obj/effect/temporary_effect/tunneler_hole
name = "hole"
desc = "A collapsing tunnel hole."
icon_state = "tunnel_hole"
time_to_die = 1 MINUTE
/datum/modifier/tunneler_vulnerable
name = "Vulnerable"
desc = "You are vulnerable to more harm than usual."
on_created_text = "<span class='warning'>You feel vulnerable...</span>"
on_expired_text = "<span class='notice'>You feel better.</span>"
stacks = MODIFIER_STACK_EXTEND
incoming_damage_percent = 2
evasion = -100
@@ -0,0 +1,37 @@
/mob/living/simple_mob/animal/giant_spider/webslinger
desc = "Furry and green, it makes you shudder to look at it. This one has brilliant green eyes, and a cloak of web."
tt_desc = "X Brachypelma phorus balisticus"
icon_state = "webslinger"
icon_living = "webslinger"
icon_dead = "webslinger_dead"
maxHealth = 90
health = 90
projectilesound = 'sound/weapons/thudswoosh.ogg'
projectiletype = /obj/item/projectile/webball
base_attack_cooldown = 10
melee_damage_lower = 8
melee_damage_upper = 15
poison_per_bite = 2
poison_type = "psilocybin"
player_msg = "You can fire a ranged attack by clicking on an enemy or tile at a distance."
ai_holder_type = /datum/ai_holder/simple_mob/ranged
// Check if we should bola, or just shoot the pain ball
/mob/living/simple_mob/animal/giant_spider/webslinger/should_special_attack(atom/A)
if(ismob(A))
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(!H.legcuffed)
return TRUE
return FALSE
// Now we've got a running human in sight, time to throw the bola
/mob/living/simple_mob/animal/giant_spider/webslinger/do_special_attack(atom/A)
set waitfor = FALSE
set_AI_busy(TRUE)
var/obj/item/projectile/bola/B = new /obj/item/projectile/bola(src.loc)
playsound(src, 'sound/weapons/thudswoosh.ogg', 100, 1)
if(!B)
return
B.launch(A)
set_AI_busy(FALSE)
@@ -0,0 +1,25 @@
//Look Sir, free crabs!
/mob/living/simple_mob/animal/passive/crab
name = "crab"
desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time."
tt_desc = "E Cancer bellianus"
faction = "crabs"
icon_state = "crab"
icon_living = "crab"
icon_dead = "crab_dead"
mob_size = MOB_SMALL
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "stomps"
friendly = "pinches"
say_list_type = /datum/say_list/crab
//COFFEE! SQUEEEEEEEEE!
/mob/living/simple_mob/animal/passive/crab/Coffee
name = "Coffee"
real_name = "Coffee"
desc = "It's Coffee, the other pet!"
@@ -0,0 +1,77 @@
// Different types of fish! They are all subtypes of this tho
/mob/living/simple_mob/animal/passive/fish
name = "fish"
desc = "Its a fishy. No touchy fishy."
icon = 'icons/mob/fish.dmi'
mob_size = MOB_SMALL
// So fish are actually underwater.
plane = TURF_PLANE
layer = UNDERWATER_LAYER
// By default they can be in any water turf. Subtypes might restrict to deep/shallow etc
var/global/list/suitable_turf_types = list(
/turf/simulated/floor/beach/water,
/turf/simulated/floor/beach/coastline,
/turf/simulated/floor/holofloor/beach/water,
/turf/simulated/floor/holofloor/beach/coastline,
/turf/simulated/floor/water
)
// Makes the AI unable to willingly go on land.
/mob/living/simple_mob/animal/passive/fish/IMove(newloc)
if(is_type_in_list(newloc, suitable_turf_types))
return ..() // Procede as normal.
return MOVEMENT_FAILED // Don't leave the water!
// Take damage if we are not in water
/mob/living/simple_mob/animal/passive/fish/handle_breathing()
var/turf/T = get_turf(src)
if(T && !is_type_in_list(T, suitable_turf_types))
if(prob(50))
say(pick("Blub", "Glub", "Burble"))
adjustBruteLoss(unsuitable_atoms_damage)
// Subtypes.
/mob/living/simple_mob/animal/passive/fish/bass
name = "bass"
tt_desc = "E Micropterus notius"
icon_state = "bass-swim"
icon_living = "bass-swim"
icon_dead = "bass-dead"
/mob/living/simple_mob/animal/passive/fish/trout
name = "trout"
tt_desc = "E Salmo trutta"
icon_state = "trout-swim"
icon_living = "trout-swim"
icon_dead = "trout-dead"
/mob/living/simple_mob/animal/passive/fish/salmon
name = "salmon"
tt_desc = "E Oncorhynchus nerka"
icon_state = "salmon-swim"
icon_living = "salmon-swim"
icon_dead = "salmon-dead"
/mob/living/simple_mob/animal/passive/fish/perch
name = "perch"
tt_desc = "E Perca flavescens"
icon_state = "perch-swim"
icon_living = "perch-swim"
icon_dead = "perch-dead"
/mob/living/simple_mob/animal/passive/fish/pike
name = "pike"
tt_desc = "E Esox aquitanicus"
icon_state = "pike-swim"
icon_living = "pike-swim"
icon_dead = "pike-dead"
/mob/living/simple_mob/animal/passive/fish/koi
name = "koi"
tt_desc = "E Cyprinus rubrofuscus"
icon_state = "koi-swim"
icon_living = "koi-swim"
icon_dead = "koi-dead"
@@ -0,0 +1,24 @@
/mob/living/simple_mob/animal/passive/lizard
name = "lizard"
desc = "A cute, tiny lizard."
tt_desc = "E Anolis cuvieri"
icon_state = "lizard"
icon_living = "lizard"
icon_dead = "lizard-dead"
health = 5
maxHealth = 5
mob_size = MOB_MINISCULE
response_help = "pets"
response_disarm = "shoos"
response_harm = "stomps on"
attacktext = list("bitten")
melee_damage_lower = 1
melee_damage_upper = 2
speak_emote = list("hisses")
say_list_type = /datum/say_list/lizard
@@ -0,0 +1,29 @@
/mob/living/simple_mob/animal/passive/yithian
name = "yithian"
desc = "A friendly creature vaguely resembling an oversized snail without a shell."
tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists
icon_state = "yithian"
icon_living = "yithian"
icon_dead = "yithian_dead"
icon = 'icons/jungle.dmi'
// Same stats as lizards.
health = 5
maxHealth = 5
mob_size = MOB_MINISCULE
/mob/living/simple_mob/animal/passive/tindalos
name = "tindalos"
desc = "It looks like a large, flightless grasshopper."
tt_desc = "J Locusta bruchus"
icon_state = "tindalos"
icon_living = "tindalos"
icon_dead = "tindalos_dead"
icon = 'icons/jungle.dmi'
// Same stats as lizards.
health = 5
maxHealth = 5
mob_size = MOB_MINISCULE
@@ -0,0 +1,115 @@
/mob/living/simple_mob/animal/passive/mouse
name = "mouse"
real_name = "mouse"
desc = "It's a small rodent."
tt_desc = "E Mus musculus"
icon_state = "mouse_gray"
item_state = "mouse_gray"
icon_living = "mouse_gray"
icon_dead = "mouse_gray_dead"
maxHealth = 5
health = 5
mob_size = MOB_MINISCULE
pass_flags = PASSTABLE
// can_pull_size = ITEMSIZE_TINY
// can_pull_mobs = MOB_PULL_NONE
layer = MOB_LAYER
density = 0
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "stamps on"
min_oxy = 16 //Require atleast 16kPA oxygen
minbodytemp = 223 //Below -50 Degrees Celcius
maxbodytemp = 323 //Above 50 Degrees Celcius
has_langs = list("Mouse")
holder_type = /obj/item/weapon/holder/mouse
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
say_list_type = /datum/say_list/mouse
var/body_color //brown, gray and white, leave blank for random
/mob/living/simple_mob/animal/passive/mouse/New()
..()
verbs += /mob/living/proc/ventcrawl
verbs += /mob/living/proc/hide
if(name == initial(name))
name = "[name] ([rand(1, 1000)])"
real_name = name
if(!body_color)
body_color = pick( list("brown","gray","white") )
icon_state = "mouse_[body_color]"
item_state = "mouse_[body_color]"
icon_living = "mouse_[body_color]"
icon_dead = "mouse_[body_color]_dead"
icon_rest = "mouse_[body_color]_sleep"
desc = "A small [body_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself."
/mob/living/simple_mob/animal/passive/mouse/Crossed(AM as mob|obj)
if( ishuman(AM) )
if(!stat)
var/mob/M = AM
M.visible_message("<font color='blue'>\icon[src] Squeek!</font>")
playsound(src, 'sound/effects/mouse_squeak.ogg', 35, 1)
..()
/mob/living/simple_mob/animal/passive/mouse/death()
layer = MOB_LAYER
playsound(src, 'sound/effects/mouse_squeak_loud.ogg', 35, 1)
if(client)
client.time_died_as_mouse = world.time
..()
/mob/living/simple_mob/animal/passive/mouse/cannot_use_vents()
return
/mob/living/simple_mob/animal/passive/mouse/proc/splat()
src.health = 0
src.stat = DEAD
src.icon_dead = "mouse_[body_color]_splat"
src.icon_state = "mouse_[body_color]_splat"
layer = MOB_LAYER
if(client)
client.time_died_as_mouse = world.time
/*
* Mouse types
*/
/mob/living/simple_mob/animal/passive/mouse/white
body_color = "white"
icon_state = "mouse_white"
/mob/living/simple_mob/animal/passive/mouse/gray
body_color = "gray"
icon_state = "mouse_gray"
/mob/living/simple_mob/animal/passive/mouse/brown
body_color = "brown"
icon_state = "mouse_brown"
//TOM IS ALIVE! SQUEEEEEEEE~K :)
/mob/living/simple_mob/animal/passive/mouse/brown/Tom
name = "Tom"
desc = "Jerry the cat is not amused."
/mob/living/simple_mob/animal/passive/mouse/brown/Tom/New()
..()
// Change my name back, don't want to be named Tom (666)
name = initial(name)
// Mouse noises
/datum/say_list/mouse
speak = list("Squeek!","SQUEEK!","Squeek?")
emote_hear = list("squeeks","squeaks","squiks")
emote_see = list("runs in a circle", "shakes", "scritches at something")
@@ -0,0 +1,5 @@
// Passive mobs can't attack things, and will run away instead.
// They can also be displaced by all mobs.
/mob/living/simple_mob/animal/passive
ai_holder_type = /datum/ai_holder/simple_mob/passive
mob_bump_flag = 0
@@ -0,0 +1,27 @@
/mob/living/simple_mob/animal/passive/penguin
name = "penguin"
desc = "An ungainly, waddling, cute, and VERY well-dressed bird."
tt_desc = "Aptenodytes forsteri"
icon_state = "penguin"
icon_living = "penguin"
icon_dead = "penguin_dead"
maxHealth = 20
health = 20
minbodytemp = 175 // Same as Sif mobs.
response_help = "pets"
response_disarm = "pushes aside"
response_harm = "hits"
harm_intent_damage = 5
melee_damage_lower = 10
melee_damage_upper = 15
attacktext = list("pecked")
has_langs = list("Bird")
/mob/living/simple_mob/animal/passive/penguin/tux
name = "Tux"
desc = "A penguin that has been known to associate with gnus."
speak_emote = list("interjects")
@@ -0,0 +1,93 @@
// Base bird type.
/mob/living/simple_mob/animal/passive/bird
name = "bird"
desc = "A domesticated bird. Tweet tweet!"
player_msg = "You are able to fly."
icon = 'icons/mob/birds.dmi'
icon_state = "parrot"
item_state = null
icon_rest = "parrot-held"
icon_dead = "parrot-dead"
pass_flags = PASSTABLE
health = 30
maxHealth = 30
melee_damage_lower = 3
melee_damage_upper = 3
movement_cooldown = 0
hovering = TRUE // Birds can fly.
softfall = TRUE
parachuting = TRUE
attacktext = list("claws", "pecks")
speak_emote = list("chirps", "caws")
has_langs = list("Bird")
response_help = "pets"
response_disarm = "gently moves aside"
response_harm = "swats"
say_list_type = /datum/say_list/bird
holder_type = /obj/item/weapon/holder/bird
/datum/say_list/bird
speak = list("Chirp!","Caw!","Screech!","Squawk!")
emote_hear = list("chirps","caws")
emote_see = list("shakes their head", "ruffles their feathers")
/obj/item/weapon/holder/bird
name = "bird"
desc = "It's a bird!"
icon_state = null
item_icons = null
w_class = ITEMSIZE_SMALL
/obj/item/weapon/holder/bird/sync(var/mob/living/simple_mob/SM)
..()
icon_state = SM.icon_rest // Looks better if the bird isn't flapping constantly in the UI.
// Subtypes for birbs.
/mob/living/simple_mob/animal/passive/bird/black_bird
name = "common blackbird"
desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds."
icon_state = "commonblackbird"
icon_dead = "commonblackbird-dead"
tt_desc = "E Turdus merula"
icon_scale = 0.5
/mob/living/simple_mob/animal/passive/bird/azure_tit
name = "azure tit"
desc = "A species of bird, colored blue and white."
icon_state = "azuretit"
icon_dead = "azuretit-dead"
tt_desc = "E Cyanistes cyanus"
icon_scale = 0.5
/mob/living/simple_mob/animal/passive/bird/european_robin
name = "european robin"
desc = "A species of bird, they have been studied for their sense of magnetoreception."
icon_state = "europeanrobin"
icon_dead = "europeanrobin-dead"
tt_desc = "E Erithacus rubecula"
icon_scale = 0.5
/mob/living/simple_mob/animal/passive/bird/goldcrest
name = "goldcrest"
desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \
Today, their scientific name still elude towards this, with <i>regulus</i>, meaning petty king."
icon_state = "goldcrest"
icon_dead = "goldcrest-dead"
tt_desc = "E Regulus regulus"
icon_scale = 0.5
/mob/living/simple_mob/animal/passive/bird/ringneck_dove
name = "ringneck dove"
desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck."
icon_state = "ringneckdove"
icon_dead = "ringneckdove-dead"
tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years.
icon_scale = 0.5
@@ -0,0 +1,136 @@
/mob/living/simple_mob/animal/passive/cat
name = "cat"
desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers."
tt_desc = "E Felis silvestris catus"
icon_state = "cat2"
item_state = "cat2"
icon_living = "cat2"
icon_dead = "cat2_dead"
icon_rest = "cat2_rest"
movement_cooldown = 0.5 SECONDS
see_in_dark = 6 // Not sure if this actually works.
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
holder_type = /obj/item/weapon/holder/cat
mob_size = MOB_SMALL
has_langs = list("Cat")
var/mob/living/friend = null // Our best pal, who we'll follow. Meow.
/mob/living/simple_mob/animal/passive/cat/handle_special()
if(!stat && prob(2)) // spooky
var/mob/observer/dead/spook = locate() in range(src, 5)
if(spook)
var/turf/T = get_turf(spook)
var/list/visible = list()
for(var/obj/O in T.contents)
if(!O.invisibility && O.name)
visible += O
if(visible.len)
var/atom/A = pick(visible)
visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].")
// Instakills mice.
/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A)
if(ismouse(A))
var/mob/living/simple_mob/animal/passive/mouse/mouse = A
if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something.
mouse.splat()
visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!"))
else
..()
/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L)
if(L == friend) // Always be pals with our special friend.
return TRUE
. = ..()
if(.) // We're pals, but they might be a dirty mouse...
if(ismouse(L))
return FALSE // Cats and mice can never get along.
/mob/living/simple_mob/animal/passive/cat/verb/become_friends()
set name = "Become Friends"
set category = "IC"
set src in view(1)
var/mob/living/L = usr
if(!istype(L))
return // Fuck off ghosts.
if(friend)
if(friend == usr)
to_chat(L, span("notice", "\The [src] is already your friend! Meow!"))
return
else
to_chat(L, span("warning", "\The [src] ignores you."))
return
friend = L
face_atom(L)
to_chat(L, span("notice", "\The [src] is now your friend! Meow."))
visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs."))
if(has_AI())
var/datum/ai_holder/AI = ai_holder
AI.set_follow(friend)
//RUNTIME IS ALIVE! SQUEEEEEEEE~
/mob/living/simple_mob/animal/passive/cat/runtime
name = "Runtime"
desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally."
tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure.
gender = FEMALE
icon_state = "cat"
item_state = "cat"
icon_living = "cat"
icon_dead = "cat_dead"
icon_rest = "cat_rest"
/mob/living/simple_mob/animal/passive/cat/kitten
name = "kitten"
desc = "D'aaawwww"
icon_state = "kitten"
item_state = "kitten"
icon_living = "kitten"
icon_dead = "kitten_dead"
gender = NEUTER
/mob/living/simple_mob/animal/passive/cat/kitten/initialize()
if(gender == NEUTER)
gender = pick(MALE, FEMALE)
return ..()
// Leaving this here for now.
/obj/item/weapon/holder/cat/fluff/bones
name = "Bones"
desc = "It's Bones! Meow."
gender = MALE
icon_state = "cat3"
/mob/living/simple_mob/animal/passive/cat/bones
name = "Bones"
desc = "That's Bones the cat. He's a laid back, black cat. Meow."
gender = MALE
icon_state = "cat3"
item_state = "cat3"
icon_living = "cat3"
icon_dead = "cat3_dead"
icon_rest = "cat3_rest"
holder_type = /obj/item/weapon/holder/cat/fluff/bones
/datum/say_list/cat
speak = list("Meow!","Esp!","Purr!","HSSSSS")
emote_hear = list("meows","mews")
emote_see = list("shakes their head", "shivers")
say_maybe_target = list("Meow?","Mew?","Mao?")
say_got_target = list("MEOW!","HSSSS!","REEER!")
@@ -0,0 +1,234 @@
/mob/living/simple_mob/animal/passive/dog
name = "dog"
real_name = "dog"
desc = "It's a dog."
tt_desc = "E Canis lupus familiaris"
icon_state = "corgi"
icon_living = "corgi"
icon_dead = "corgi_dead"
health = 20
maxHealth = 20
response_help = "pets"
response_disarm = "bops"
response_harm = "kicks"
mob_size = MOB_SMALL
has_langs = list("Dog")
say_list_type = /datum/say_list/dog
meat_amount = 3
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi
var/obj/item/inventory_head
var/obj/item/inventory_back
/mob/living/simple_mob/animal/passive/dog/attackby(var/obj/item/O as obj, var/mob/user as mob)
if(istype(O, /obj/item/weapon/newspaper))
if(!stat)
for(var/mob/M in viewers(user, null))
if ((M.client && !( M.blinded )))
M.show_message("<font color='blue'>[user] baps [name] on the nose with the rolled up [O]</font>")
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2))
set_dir(i)
sleep(1)
else
..()
/mob/living/simple_mob/animal/passive/dog/regenerate_icons()
overlays = list()
if(inventory_head)
var/head_icon_state = inventory_head.icon_state
if(health <= 0)
head_icon_state += "2"
var/icon/head_icon = image('icons/mob/corgi_head.dmi',head_icon_state)
if(head_icon)
overlays += head_icon
if(inventory_back)
var/back_icon_state = inventory_back.icon_state
if(health <= 0)
back_icon_state += "2"
var/icon/back_icon = image('icons/mob/corgi_back.dmi',back_icon_state)
if(back_icon)
overlays += back_icon
return
/obj/item/weapon/reagent_containers/food/snacks/meat/corgi
name = "corgi meat"
desc = "Tastes like... well, you know..."
/datum/say_list/dog
speak = list("YAP", "Woof!", "Bark!", "AUUUUUU")
emote_hear = list("barks", "woofs", "yaps","pants")
emote_see = list("shakes its head", "shivers")
// This exists so not every type of dog has to be a subtype of corgi, and in case we get more dog sprites
/mob/living/simple_mob/animal/passive/dog/corgi
name = "corgi"
real_name = "corgi"
desc = "It's a corgi."
tt_desc = "E Canis lupus familiaris"
icon_state = "corgi"
icon_living = "corgi"
icon_dead = "corgi_dead"
/mob/living/simple_mob/animal/passive/dog/corgi/puppy
name = "corgi puppy"
real_name = "corgi"
desc = "It's a corgi puppy."
icon_state = "puppy"
icon_living = "puppy"
icon_dead = "puppy_dead"
//pupplies cannot wear anything.
/mob/living/simple_mob/animal/passive/dog/corgi/puppy/Topic(href, href_list)
if(href_list["remove_inv"] || href_list["add_inv"])
usr << "<font color='red'>You can't fit this on [src]</font>"
return
..()
/mob/living/simple_mob/animal/passive/dog/corgi/puppy/Bockscar
name = "Bockscar"
real_name = "Bockscar"
//IAN! SQUEEEEEEEEE~
/mob/living/simple_mob/animal/passive/dog/corgi/Ian
name = "Ian"
real_name = "Ian" //Intended to hold the name without altering it.
gender = MALE
desc = "It's a corgi."
var/turns_since_scan = 0
var/obj/movement_target
/mob/living/simple_mob/animal/passive/dog/corgi/Ian/Life()
..()
//Not replacing with SA FollowTarget mechanics because Ian behaves... very... specifically.
//Feeding, chasing food, FOOOOODDDD
if(!stat && !resting && !buckled)
turns_since_scan++
if(turns_since_scan > 5)
turns_since_scan = 0
if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
movement_target = null
if( !movement_target || !(movement_target.loc in oview(src, 3)) )
movement_target = null
for(var/obj/item/weapon/reagent_containers/food/snacks/S in oview(src,3))
if(isturf(S.loc) || ishuman(S.loc))
movement_target = S
break
if(movement_target)
step_to(src,movement_target,1)
sleep(3)
step_to(src,movement_target,1)
sleep(3)
step_to(src,movement_target,1)
if(movement_target) //Not redundant due to sleeps, Item can be gone in 6 decisecomds
if (movement_target.loc.x < src.x)
set_dir(WEST)
else if (movement_target.loc.x > src.x)
set_dir(EAST)
else if (movement_target.loc.y < src.y)
set_dir(SOUTH)
else if (movement_target.loc.y > src.y)
set_dir(NORTH)
else
set_dir(SOUTH)
if(isturf(movement_target.loc) )
UnarmedAttack(movement_target)
else if(ishuman(movement_target.loc) && prob(20))
visible_emote("stares at the [movement_target] that [movement_target.loc] has with sad puppy eyes.")
if(prob(1))
visible_emote(pick("dances around","chases their tail"))
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
set_dir(i)
sleep(1)
//LISA! SQUEEEEEEEEE~
/mob/living/simple_mob/animal/passive/dog/corgi/Lisa
name = "Lisa"
real_name = "Lisa"
gender = FEMALE
desc = "It's a corgi with a cute pink bow."
icon_state = "lisa"
icon_living = "lisa"
icon_dead = "lisa_dead"
response_help = "pets"
response_disarm = "bops"
response_harm = "kicks"
var/turns_since_scan = 0
var/puppies = 0
//Lisa already has a cute bow!
/mob/living/simple_mob/animal/passive/dog/corgi/Lisa/Topic(href, href_list)
if(href_list["remove_inv"] || href_list["add_inv"])
to_chat(usr, "<font color='red'>[src] already has a cute bow!</font>")
return
..()
/mob/living/simple_mob/animal/passive/dog/corgi/Lisa/Life()
..()
if(!stat && !resting && !buckled)
turns_since_scan++
if(turns_since_scan > 15)
turns_since_scan = 0
var/alone = TRUE
var/ian = FALSE
for(var/mob/M in oviewers(7, src))
if(istype(M, /mob/living/simple_mob/animal/passive/dog/corgi/Ian))
if(M.client)
alone = FALSE
break
else
ian = M
else
alone = FALSE
break
if(alone && ian && puppies < 4)
if(near_camera(src) || near_camera(ian))
return
new /mob/living/simple_mob/animal/passive/dog/corgi/puppy(loc)
if(prob(1))
visible_emote(pick("dances around","chases her tail"))
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
set_dir(i)
sleep(1)
// Tamaskans
/mob/living/simple_mob/animal/passive/dog/tamaskan
name = "tamaskan"
real_name = "tamaskan"
desc = "It's a tamaskan."
icon_state = "tamaskan"
icon_living = "tamaskan"
icon_dead = "tamaskan_dead"
/mob/living/simple_mob/animal/passive/dog/tamaskan/Spice
name = "Spice"
real_name = "Spice" //Intended to hold the name without altering it.
gender = FEMALE
desc = "It's a tamaskan, the name Spice can be found on its collar."
@@ -0,0 +1,252 @@
// Parrots can talk, and may repeat things it hears.
/mob/living/simple_mob/animal/passive/bird/parrot
name = "parrot"
description_info = "You can give it a headset by clicking on it with a headset. \
To remove it, click the bird while on grab intent."
has_langs = list("Galactic Common", "Bird")
ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot
// A headset, so that talking parrots can yell at the crew over comms.
// If set to a type, on initialize it will be instantiated into that type.
var/obj/item/device/radio/headset/my_headset = null
// Say list
/datum/say_list/bird/poly
speak = list(
"Poly wanna cracker!",
"Check the singulo, you chucklefucks!",
"Wire the solars, you lazy bums!",
"WHO TOOK THE DAMN HARDSUITS?",
"OH GOD ITS FREE CALL THE SHUTTLE",
"Danger! Crystal hyperstructure instability!",
"CRYSTAL DELAMINATION IMMINENT.",
"Tweet tweet, I'm a Teshari.",
"Chitters.",
"Meteors have been detected on a collision course with the station!"
)
// Lets the AI use headsets.
// Player-controlled parrots will need to do it manually.
/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message)
if(my_headset && prob(50))
var/list/keys = list()
for(var/channel in my_headset.channels)
var/key = get_radio_key_from_channel(channel)
if(key)
keys += key
if(keys.len)
var/key_used = pick(keys)
return say("[key_used] [message]")
return say(message)
// Ugly saycode so parrots can use their headsets.
/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
..()
if(message_mode)
if(my_headset && istype(my_headset, /obj/item/device/radio))
my_headset.talk_into(src, message, message_mode, verb, speaking)
used_radios += my_headset
// Clicked on while holding an object.
/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/device/radio/headset))
give_headset(I, user)
return
return ..()
// Clicked on by empty hand.
/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L)
if(L.a_intent == I_GRAB && my_headset)
remove_headset(L)
else
..()
/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user)
if(!istype(new_headset))
to_chat(user, span("warning", "\The [new_headset] isn't a headset."))
return
if(my_headset)
to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset]."))
return
else
user.drop_item(new_headset)
my_headset = new_headset
new_headset.forceMove(src)
to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster."))
to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately."))
return
/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user)
if(!my_headset)
to_chat(user, "<span class='warning'>\The [src] doesn't have a headset to remove, thankfully.</span>")
else
ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!")
my_headset.forceMove(get_turf(src))
user.put_in_hands(my_headset)
to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally."))
to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!"))
my_headset = null
/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user)
..()
if(my_headset)
to_chat(user, "It is wearing \a [my_headset].")
/mob/living/simple_mob/animal/passive/bird/parrot/initialize()
if(my_headset)
my_headset = new my_headset(src)
return ..()
// Subtypes.
// Best Bird
/mob/living/simple_mob/animal/passive/bird/parrot/poly
name = "Poly"
desc = "It's a parrot. An expert on quantum cracker theory."
icon_state = "poly"
icon_rest = "poly-held"
icon_dead = "poly-dead"
tt_desc = "E Ara macao"
my_headset = /obj/item/device/radio/headset/headset_eng
say_list_type = /datum/say_list/bird/poly
// Best Bird with best headset.
/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate
my_headset = /obj/item/device/radio/headset/omni
/mob/living/simple_mob/animal/passive/bird/parrot/kea
name = "kea"
desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \
They are known to be intelligent and curious, which has made some consider them a pest."
icon_state = "kea"
icon_rest = "kea-held"
icon_dead = "kea-dead"
tt_desc = "E Nestor notabilis"
/mob/living/simple_mob/animal/passive/bird/parrot/eclectus
name = "eclectus"
desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \
A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage."
icon_state = "eclectus"
icon_rest = "eclectus-held"
icon_dead = "eclectus-dead"
tt_desc = "E Eclectus roratus"
/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/initialize()
gender = pick(MALE, FEMALE)
if(gender == FEMALE)
icon_state = "eclectusf"
icon_rest = "eclectusf-held"
icon_dead = "eclectusf-dead"
return ..()
/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot
name = "grey parrot"
desc = "A species of parrot. This one is predominantly grey, but has red tail feathers."
icon_state = "agrey"
icon_rest = "agrey-held"
icon_dead = "agrey-dead"
tt_desc = "E Psittacus erithacus"
/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique
name = "black-headed caique"
desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques."
icon_state = "bcaique"
icon_rest = "bcaique-held"
icon_dead = "bcaique-dead"
tt_desc = "E Pionites melanocephalus"
/mob/living/simple_mob/animal/passive/bird/parrot/white_caique
name = "white-bellied caique"
desc = "A species of parrot, they are also known as the Green-Thighed Parrot."
icon_state = "wcaique"
icon_rest = "wcaique-held"
icon_dead = "wcaique-dead"
tt_desc = "E Pionites leucogaster"
/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar
name = "budgerigar"
desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \
This one is has its natural colors of green and yellow."
icon_state = "gbudge"
icon_rest = "gbudge-held"
icon_dead = "gbudge-dead"
tt_desc = "E Melopsittacus undulatus"
/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue
icon_state = "bbudge"
icon_rest = "bbudge-held"
icon_dead = "bbudge-dead"
desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \
This one has a mutation which altered its color to be blue instead of green and yellow."
/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen
icon_state = "bgbudge"
icon_rest = "bgbudge-held"
icon_dead = "bgbudge-dead"
desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \
This one has a mutation which altered its color to be a mix of blue and green."
/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel
name = "cockatiel"
desc = "A species of parrot. This one has a highly visible crest."
icon_state = "tiel"
icon_rest = "tiel-held"
icon_dead = "tiel-dead"
tt_desc = "E Nymphicus hollandicus"
/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white
icon_state = "wtiel"
icon_rest = "wtiel-held"
icon_dead = "wtiel-dead"
/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish
icon_state = "luttiel"
icon_rest = "luttiel-held"
icon_dead = "luttiel-dead"
/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey
icon_state = "blutiel" // idk why this is blu.
icon_rest = "blutiel-held"
icon_dead = "blutiel-dead"
// This actually might be the yellow-crested cockatoo but idk.
/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo
name = "sulphur-crested cockatoo"
desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow."
icon_state = "too"
icon_rest = "too-held"
icon_dead = "too-dead"
tt_desc = "E Cacatua galerita"
// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk.
/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo
name = "white cockatoo"
desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest."
icon_state = "utoo"
icon_rest = "utoo-held"
icon_dead = "utoo-dead"
tt_desc = "E Cacatua alba"
/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo
name = "pink cockatoo"
desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \
in honor of a human surveyor and explorer who existed before humans fully explored their home planet."
icon_state = "mtoo"
icon_rest = "mtoo-held"
icon_dead = "mtoo-dead"
tt_desc = "E Lophochroa leadbeateri"
// AI
/datum/ai_holder/simple_mob/passive/parrot
speak_chance = 2
base_wander_delay = 8
/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message)
if(holder.stat || !holder.say_list || !message || speaker == holder)
return
var/datum/say_list/S = holder.say_list
S.speak |= message
@@ -0,0 +1,35 @@
// Diyaabs are rather weak, but tend to exist in large numbers.
// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter.
// A cleaving weapon like an axe will make short work of the pack.
/mob/living/simple_mob/animal/sif/diyaab
name = "diyaab"
desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion."
tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives!
faction = "diyaab"
icon_state = "diyaab"
icon_living = "diyaab"
icon_dead = "diyaab_dead"
icon = 'icons/jungle.dmi'
maxHealth = 25
health = 25
movement_cooldown = 0
melee_damage_lower = 2
melee_damage_upper = 6
base_attack_cooldown = 1 SECOND
attack_sharp = 1 //Bleeds, but it shouldn't rip off a limb?
attacktext = list("gouged")
say_list_type = /datum/say_list/diyaab
ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative
/datum/say_list/diyaab
speak = list("Awrr?", "Aowrl!", "Worrl.")
emote_see = list("sniffs the air cautiously","looks around")
emote_hear = list("snuffles")
@@ -0,0 +1,102 @@
/*
Hooligan Crabs are called so because they are rather curious and tend to follow people,
whether the people want them to or not, and sometimes causing vandalism by accident.
They're pretty strong and have strong melee armor, but won't attack first.
They unknowingly play a role in keeping the shoreline fairly safe, by killing whatever would attack other people.
They also have a slow, but very strong attack that is telegraphed. If it hits, it will briefly stun whatever got hit
and inflict a very large chunk of damage. If the thing was already stunned, the crab will 'throw' them away, to
hopefully prevent chainstuns forever.
*/
/mob/living/simple_mob/animal/sif/hooligan_crab
name = "hooligan crab"
desc = "A large, hard-shelled crustacean. This one is mostly grey. \
You probably shouldn't mess with it."
icon_state = "sif_crab"
icon_living = "sif_crab"
icon_dead = "sif_crab_dead"
icon_scale = 1.5
faction = "crabs"
maxHealth = 200
health = 200
movement_cooldown = 10
movement_sound = 'sound/weapons/heavysmash.ogg'
movement_shake_radius = 5
taser_kill = FALSE
armor = list(
"melee" = 40,
"bullet" = 20,
"laser" = 10,
"energy" = 0,
"bomb" = 0,
"bio" = 0,
"rad" = 0
)
armor_soak = list(
"melee" = 10,
"bullet" = 5,
"laser" = 0,
"energy" = 0,
"bomb" = 0,
"bio" = 0,
"rad" = 0
)
mob_size = MOB_LARGE
melee_damage_lower = 22
melee_damage_upper = 35
attack_armor_pen = 35
attack_sharp = TRUE
attack_edge = TRUE
melee_attack_delay = 1 SECOND
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "kicks"
friendly = "pinches"
attacktext = list("clawed", "pinched", "crushed")
speak_emote = list("clicks")
ai_holder_type = /datum/ai_holder/simple_mob/melee/hooligan
say_list_type = /datum/say_list/crab
var/weaken_amount = 2 // Be careful with this number. High values will equal a permastun.
// Stuns the thing that got hit briefly.
/mob/living/simple_mob/animal/sif/hooligan_crab/apply_melee_effects(atom/A)
if(isliving(A))
var/mob/living/L = A
var/was_stunned = L.incapacitated(INCAPACITATION_DISABLED)
L.Weaken(weaken_amount)
playsound(L, 'sound/effects/break_stone.ogg', 75, 1)
if(was_stunned) // Try to prevent chain-stuns by having them thrown.
var/throwdir = get_dir(src, L)
L.throw_at(get_edge_target_turf(L, throwdir), 5, 1, src)
visible_message(span("danger", "\The [src] hurls \the [L] away!"))
else
visible_message(span("danger", "\The [src] crushes \the [L]!"))
// The AI for hooligan crabs. Follows people for awhile.
/datum/ai_holder/simple_mob/melee/hooligan
hostile = FALSE
retaliate = TRUE
returns_home = TRUE
max_home_distance = 12
var/random_follow = TRUE // Turn off if you want to bus with crabs.
/datum/ai_holder/simple_mob/melee/hooligan/handle_stance_strategical()
..()
if(random_follow && stance == STANCE_IDLE && !leader)
if(prob(10))
for(var/mob/living/L in hearers(holder))
if(!istype(L, holder)) // Don't follow other hooligan crabs.
holder.visible_message("<span class='notice'>\The [holder] starts to follow \the [L].</span>")
set_follow(L, rand(20 SECONDS, 40 SECONDS))
@@ -0,0 +1,48 @@
// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight.
/mob/living/simple_mob/animal/sif/savik
name = "savik"
tt_desc = "S Pistris tellus" //landshark
player_msg = "You have the ability to <b>berserk at will</b>, which will grant strong physical bonuses for \
a short period of time, however it will tire you and you will be much weaker for awhile after it expires."
faction = "savik"
icon_state = "savik"
icon_living = "savik"
icon_dead = "savik_dead"
icon = 'icons/jungle.dmi'
maxHealth = 125
health = 125
movement_cooldown = 0.5 SECONDS
melee_damage_lower = 15
melee_damage_upper = 35
attack_armor_pen = 15
attack_sharp = TRUE
attack_edge = TRUE
melee_attack_delay = 1 SECOND
attacktext = list("mauled")
say_list_type = /datum/say_list/savik
/datum/say_list/savik
speak = list("Hruuugh!","Hrunnph")
emote_see = list("paws the ground","shakes its mane","stomps")
emote_hear = list("snuffles")
/mob/living/simple_mob/animal/sif/savik/handle_special()
if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc))
if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently.
berserk()
// So players can use it too.
/mob/living/simple_mob/animal/sif/savik/verb/berserk()
set name = "Berserk"
set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards."
set category = "Abilities"
add_modifier(/datum/modifier/berserk, 30 SECONDS)
@@ -0,0 +1,70 @@
// Shantaks are essentially sif wolves.
/mob/living/simple_mob/animal/sif/shantak
name = "shantak"
desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \
Don't be fooled by its beauty though."
tt_desc = "S Choeros shantak"
faction = "shantak"
icon_state = "shantak"
icon_living = "shantak"
icon_dead = "shantak_dead"
icon = 'icons/jungle.dmi'
maxHealth = 75
movement_cooldown = 5
melee_damage_lower = 6
melee_damage_upper = 14
base_attack_cooldown = 1 SECOND
melee_attack_delay = 0.5 SECONDS
attack_armor_pen = 5
attack_sharp = TRUE
attack_edge = TRUE
attacktext = list("gouged")
say_list_type = /datum/say_list/shantak
/datum/say_list/shantak
speak = list("Shuhn.","Shrunnph?","Shunpf.")
emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves")
// The pack leader.
// Will command other shantaks to follow it.
/mob/living/simple_mob/animal/sif/shantak/leader
name = "big shantak"
desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \
This one seems bigger than the others, and has a commanding presence."
icon_scale = 1.5
maxHealth = 125
player_msg = "You have the ability to <b>command other shantaks to follow you</b>."
/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack()
set name = "Rally Pack"
set desc = "Commands your fellow packmembers to follow you, the leader."
set category = "Abilities"
for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src))
if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves.
continue
if(!S.ai_holder)
continue
if(S.faction != src.faction)
continue
var/datum/ai_holder/AI = S.ai_holder
AI.set_follow(src)
// Variant that automatically commands nearby allies to follow it when created.
// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers.
/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/initialize()
rally_pack()
return ..()
// These ones only retaliate. Used for a PoI.
/mob/living/simple_mob/animal/sif/shantak/retaliate
ai_holder_type = /datum/ai_holder/simple_mob/retaliate
@@ -0,0 +1,5 @@
// Mobs intended to be on Sif. As such, they won't die to the cold.
/mob/living/simple_mob/animal/sif
minbodytemp = 175
cold_resist = 0.75
heat_resist = -0.5
@@ -0,0 +1,118 @@
/mob/living/simple_mob/animal/space/alien
name = "alien hunter"
desc = "Hiss!"
icon = 'icons/mob/alien.dmi'
icon_state = "alienh_running"
icon_living = "alienh_running"
icon_dead = "alien_l"
icon_gib = "syndicate_gib"
icon_rest = "alienh_sleep"
faction = "xeno"
mob_class = MOB_CLASS_ABERRATION
response_help = "pokes"
response_disarm = "shoves"
response_harm = "hits"
maxHealth = 100
health = 100
harm_intent_damage = 5
melee_damage_lower = 25
melee_damage_upper = 25
attack_sharp = TRUE
attack_edge = TRUE
attacktext = list("slashed")
attack_sound = 'sound/weapons/bladeslice.ogg'
meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat
/mob/living/simple_mob/animal/space/alien/drone
name = "alien drone"
icon_state = "aliend_running"
icon_living = "aliend_running"
icon_dead = "aliend_l"
icon_rest = "aliend_sleep"
health = 60
melee_damage_lower = 15
melee_damage_upper = 15
/mob/living/simple_mob/animal/space/alien/sentinel
name = "alien sentinel"
icon_state = "aliens_running"
icon_living = "aliens_running"
icon_dead = "aliens_l"
icon_rest = "aliens_sleep"
health = 120
melee_damage_lower = 15
melee_damage_upper = 15
projectiletype = /obj/item/projectile/energy/neurotoxin/toxic
projectilesound = 'sound/weapons/pierce.ogg'
/mob/living/simple_mob/animal/space/alien/sentinel/praetorian
name = "alien praetorian"
icon = 'icons/mob/64x64.dmi'
icon_state = "prat_s"
icon_living = "prat_s"
icon_dead = "prat_dead"
icon_rest = "prat_sleep"
maxHealth = 200
health = 200
pixel_x = -16
old_x = -16
meat_amount = 5
/mob/living/simple_mob/animal/space/alien/queen
name = "alien queen"
icon_state = "alienq_running"
icon_living = "alienq_running"
icon_dead = "alienq_l"
icon_rest = "alienq_sleep"
health = 250
maxHealth = 250
melee_damage_lower = 15
melee_damage_upper = 15
projectiletype = /obj/item/projectile/energy/neurotoxin/toxic
projectilesound = 'sound/weapons/pierce.ogg'
movement_cooldown = 8
/mob/living/simple_mob/animal/space/alien/queen/empress
name = "alien empress"
icon = 'icons/mob/64x64.dmi'
icon_state = "queen_s"
icon_living = "queen_s"
icon_dead = "queen_dead"
icon_rest = "queen_sleep"
maxHealth = 400
health = 400
meat_amount = 5
pixel_x = -16
old_x = -16
/mob/living/simple_mob/animal/space/alien/queen/empress/mother
name = "alien mother"
icon = 'icons/mob/96x96.dmi'
icon_state = "empress_s"
icon_living = "empress_s"
icon_dead = "empress_dead"
icon_rest = "empress_rest"
maxHealth = 600
health = 600
meat_amount = 10
melee_damage_lower = 15
melee_damage_upper = 25
pixel_x = -32
old_x = -32
/mob/living/simple_mob/animal/space/alien/death()
..()
visible_message("[src] lets out a waning guttural screech, green blood bubbling from its maw...")
playsound(src, 'sound/voice/hiss6.ogg', 100, 1)
@@ -0,0 +1,52 @@
/mob/living/simple_mob/animal/space/bats
name = "space bat swarm"
desc = "A swarm of cute little blood sucking bats that looks pretty upset."
tt_desc = "N Bestia gregaria" //Nispean swarm bats, because of course Nisp has swarm bats
icon = 'icons/mob/bats.dmi'
icon_state = "bat"
icon_living = "bat"
icon_dead = "bat_dead"
icon_gib = "bat_dead"
faction = "scarybat"
maxHealth = 20
health = 20
attacktext = list("bites")
attack_sound = 'sound/weapons/bite.ogg'
response_help = "pets the"
response_disarm = "gently pushes aside the"
response_harm = "hits the"
harm_intent_damage = 10
melee_damage_lower = 5
melee_damage_upper = 5
attack_sharp = TRUE
ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive
has_langs = list("Mouse")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
say_list_type = /datum/say_list/mouse // Close enough
var/scare_chance = 15
/mob/living/simple_mob/animal/space/bats/apply_melee_effects(var/atom/A)
if(isliving(A))
var/mob/living/L = A
if(prob(scare_chance))
L.Stun(1)
L.visible_message("<span class='danger'>\the [src] scares \the [L]!</span>")
// Spookiest of bats
/mob/living/simple_mob/animal/space/bats/cult
faction = "cult"
supernatural = TRUE
/mob/living/simple_mob/animal/space/bats/cult/cultify()
return
@@ -0,0 +1,46 @@
/mob/living/simple_mob/animal/space/bear
name = "space bear"
desc = "A product of Space Russia?"
tt_desc = "U Ursinae aetherius" //...bearspace? Maybe.
icon_state = "bear"
icon_living = "bear"
icon_dead = "bear_dead"
icon_gib = "bear_gib"
faction = "russian"
maxHealth = 125
health = 125
movement_cooldown = 0.5 SECONDS
melee_damage_lower = 15
melee_damage_upper = 35
attack_armor_pen = 15
attack_sharp = TRUE
attack_edge = TRUE
melee_attack_delay = 1 SECOND
attacktext = list("mauled")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat
say_list_type = /datum/say_list/bear
/datum/say_list/bear
speak = list("RAWR!","Rawr!","GRR!","Growl!")
emote_see = list("stares ferociously", "stomps")
emote_hear = list("rawrs","grumbles","grawls", "growls", "roars")
// Is it time to be mad?
/mob/living/simple_mob/animal/space/bear/handle_special()
if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc))
if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently.
berserk()
// So players can use it too.
/mob/living/simple_mob/animal/space/bear/verb/berserk()
set name = "Berserk"
set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards."
set category = "Abilities"
add_modifier(/datum/modifier/berserk, 30 SECONDS)
@@ -0,0 +1,128 @@
// Space carp show up as a random event to wreck hapless people in space or near windows.
// They generally fit the archetype of 'fast but fragile'.
// This is compensated by being in groups (usually).
/mob/living/simple_mob/animal/space/carp
name = "space carp"
desc = "A ferocious, fang-bearing creature that resembles a fish."
icon_state = "carp"
icon_living = "carp"
icon_dead = "carp_dead"
icon_gib = "carp_gib"
faction = "carp"
maxHealth = 25
health = 25
movement_cooldown = 0 // Carp go fast
hovering = TRUE
response_help = "pets the"
response_disarm = "gently pushes aside the"
response_harm = "hits the"
melee_damage_lower = 7 // About 14 DPS.
melee_damage_upper = 7
base_attack_cooldown = 10 // One attack a second.
attack_sharp = TRUE
attack_sound = 'sound/weapons/bite.ogg'
attacktext = list("bitten")
meat_amount = 1
meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat
ai_holder_type = /datum/ai_holder/simple_mob/melee
var/knockdown_chance = 15
/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A)
if(isliving(A))
var/mob/living/L = A
if(prob(knockdown_chance))
L.Weaken(3)
L.visible_message(span("danger", "\The [src] knocks down \the [L]!"))
// Subtypes.
// Won't wander away.
/mob/living/simple_mob/animal/space/carp/event
ai_holder_type = /datum/ai_holder/simple_mob/event
/mob/living/simple_mob/animal/space/carp/large
name = "elder carp"
desc = "An older, more matured carp. Few survive to this age due to their aggressiveness."
icon = 'icons/mob/64x32.dmi'
icon_state = "shark"
icon_living = "shark"
icon_dead = "shark_dead"
maxHealth = 50
health = 50
movement_cooldown = 5 // Slower than the younger carp.
mob_size = MOB_LARGE
pixel_x = -16
default_pixel_x = -16
meat_amount = 3
/mob/living/simple_mob/animal/space/carp/large/huge
name = "great white carp"
desc = "A very rare breed of carp- and a very aggressive one."
icon = 'icons/mob/64x64.dmi'
icon_dead = "megacarp_dead"
icon_living = "megacarp"
icon_state = "megacarp"
maxHealth = 230
health = 230
movement_cooldown = 10
melee_damage_lower = 15 // About 20 DPS.
melee_damage_upper = 25
pixel_y = -16
default_pixel_y = -16
meat_amount = 10
/mob/living/simple_mob/animal/space/carp/holographic
name = "holographic carp"
desc = "An obviously holographic, but still ferocious looking carp."
// Might be worth using a filter similar to AI holograms in the future.
icon = 'icons/mob/AI.dmi'
icon_state = "holo4"
icon_living = "holo4"
icon_dead = "holo4"
alpha = 127
icon_gib = null
meat_amount = 0
meat_type = null
mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp.
/mob/living/simple_mob/animal/space/carp/holographic/initialize()
set_light(2) // Hologram lighting.
return ..()
// Presumably the holodeck emag code requires this.
// Pass TRUE to make safe. Pass FALSE to make unsafe.
/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe)
if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var.
ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE.
ai_holder.forget_everything() // Reset state so it'll stop chewing on its target.
// Called on death.
/mob/living/simple_mob/animal/space/carp/holographic/proc/derez()
visible_message(span("notice", "\The [src] fades away!"))
qdel(src)
/mob/living/simple_mob/animal/space/carp/holographic/gib()
derez() // Holograms can't gib.
/mob/living/simple_mob/animal/space/carp/holographic/death()
..()
derez()
@@ -0,0 +1,44 @@
/mob/living/simple_mob/animal/space/goose
name = "goose"
desc = "It looks pretty angry!"
tt_desc = "E Branta canadensis" //that iconstate is just a regular goose
icon_state = "goose"
icon_living = "goose"
icon_dead = "goose_dead"
faction = "geese"
maxHealth = 30
health = 30
response_help = "pets the"
response_disarm = "gently pushes aside the"
response_harm = "hits the"
harm_intent_damage = 5
melee_damage_lower = 5 //they're meant to be annoying, not threatening.
melee_damage_upper = 5 //unless there's like a dozen of them, then you're screwed.
attacktext = list("pecked")
attack_sound = 'sound/weapons/bite.ogg'
has_langs = list("Bird")
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
/datum/say_list/goose
speak = list("HONK!")
emote_hear = list("honks loudly!")
say_maybe_target = list("Honk?")
say_got_target = list("HONK!!!")
/mob/living/simple_mob/animal/space/goose/handle_special()
if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc))
if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently.
berserk()
/mob/living/simple_mob/animal/space/goose/verb/berserk()
set name = "Berserk"
set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards."
set category = "Abilities"
add_modifier(/datum/modifier/berserk, 30 SECONDS)
@@ -0,0 +1,15 @@
// 'Space' mobs don't care about atmos (like carp)
/mob/living/simple_mob/animal/space
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
// They can also, you know, move around, in space
/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0)
return TRUE
@@ -0,0 +1,62 @@
// Blob simple_mobs generally get made from the blob random event.
// They're considered slimes for the purposes of attack bonuses from certain weapons.
// Do not spawn, this is a base type.
/mob/living/simple_mob/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB | PASSTABLE
faction = "blob"
heat_damage_per_tick = 0
cold_damage_per_tick = 0
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
taser_kill = FALSE
var/mob/observer/blob/overmind = null
var/obj/structure/blob/factory/factory = null
mob_class = MOB_CLASS_SLIME
ai_holder_type = /datum/ai_holder/simple_mob/melee
/mob/living/simple_mob/blob/speech_bubble_appearance()
return "slime"
/mob/living/simple_mob/blob/update_icons()
if(overmind)
color = overmind.blob_type.complementary_color
else
color = null
..()
/mob/living/simple_mob/blob/Destroy()
if(overmind)
overmind.blob_mobs -= src
return ..()
/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B)
if(!overmind && B.overmind)
overmind = B.overmind
update_icon()
if(stat != DEAD && health < maxHealth)
adjustBruteLoss(-maxHealth*0.0125)
adjustFireLoss(-maxHealth*0.0125)
/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob.
return TRUE
return ..()
/mob/living/simple_mob/blob/Process_Spacemove()
for(var/obj/structure/blob/B in range(1, src))
return TRUE
return ..()
@@ -0,0 +1,148 @@
// Spores are made from blob factories.
// They are very weak and expendable, but can overwhelm when a lot of them are together.
// When attacking, spores will hit harder if near other friendly spores.
// Some blobs can infest dead non-robotic mobs, making them into Not Zombies.
/mob/living/simple_mob/blob/spore
name = "blob spore"
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
glow_range = 3
glow_intensity = 5
layer = ABOVE_MOB_LAYER // Over the blob.
health = 30
maxHealth = 30
melee_damage_lower = 2
melee_damage_upper = 4
movement_cooldown = 0
hovering = TRUE
attacktext = list("slams into")
attack_sound = 'sound/effects/slime_squish.ogg'
say_list_type = /datum/say_list/spore
var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie.
var/can_infest = FALSE
var/is_infesting = FALSE
/datum/say_list/spore
emote_see = list("sways", "inflates briefly")
/datum/say_list/infested
emote_see = list("shambles around", "twitches", "stares")
/mob/living/simple_mob/blob/spore/infesting
name = "infesting blob spore"
can_infest = TRUE
/mob/living/simple_mob/blob/spore/weak
name = "fragile blob spore"
health = 15
maxHealth = 15
melee_damage_lower = 1
melee_damage_upper = 2
/mob/living/simple_mob/blob/spore/initialize(mapload, var/obj/structure/blob/factory/my_factory)
if(istype(my_factory))
factory = my_factory
factory.spores += src
return ..()
/mob/living/simple_mob/blob/spore/Destroy()
if(factory)
factory.spores -= src
factory = null
if(infested)
infested.forceMove(get_turf(src))
visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts."))
infested = null
return ..()
/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!")
if(overmind)
overmind.blob_type.on_spore_death(src)
..(gibbed, deathmessage)
qdel(src)
/mob/living/simple_mob/blob/spore/update_icons()
..() // This will cut our overlays.
if(overmind)
color = overmind.blob_type.complementary_color
glow_color = color
glow_toggle = TRUE
else
color = null
glow_color = null
glow_toggle = FALSE
if(is_infesting)
icon = infested.icon
copy_overlays(infested)
// overlays = infested.overlays
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
if(overmind)
blob_head_overlay.color = overmind.blob_type.complementary_color
color = initial(color)//looks better.
// overlays += blob_head_overlay
add_overlay(blob_head_overlay, TRUE)
/mob/living/simple_mob/blob/spore/handle_special()
..()
if(can_infest && !is_infesting && isturf(loc))
for(var/mob/living/carbon/human/H in view(src,1))
if(H.stat != DEAD) // We want zombies.
continue
if(H.isSynthetic()) // Not philosophical zombies.
continue
infest(H)
break
if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless.
qdel(src)
/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H)
is_infesting = TRUE
if(H.wear_suit)
var/obj/item/clothing/suit/A = H.wear_suit
if(A.armor && A.armor["melee"])
maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
maxHealth += 40
health = maxHealth
name = "Infested [H.real_name]" // Not using the Z word.
desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet."
melee_damage_lower += 8 // 10 total.
melee_damage_upper += 11 // 15 total.
attacktext = list("claws")
H.forceMove(src)
infested = H
say_list = new /datum/say_list/infested()
update_icons()
visible_message(span("warning", "The corpse of [H.name] suddenly rises!"))
/mob/living/simple_mob/blob/spore/GetIdCard()
if(infested) // If we've infested someone, use their ID.
return infested.GetIdCard()
/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do)
var/helpers = 0
for(var/mob/living/simple_mob/blob/spore/S in view(1, src))
if(S == src) // Don't count ourselves.
continue
if(!IIsAlly(S)) // Only friendly spores make us stronger.
continue
// Friendly spores contribute 1/4th of their averaged attack power to our attack.
damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4
helpers++
if(helpers)
to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s."))
return damage_to_do
@@ -0,0 +1,29 @@
/mob/living/simple_mob/humanoid/clown
clown
name = "clown"
desc = "A denizen of clown planet"
tt_desc = "E Homo sapiens corydon" //this is an actual clown, as opposed to someone dressed up as one
icon_state = "clown"
icon_living = "clown"
icon_dead = "clown_dead"
icon_gib = "clown_gib"
faction = "clown"
loot_list = list(/obj/item/weapon/bikehorn = 100)
response_help = "pokes"
response_disarm = "gently pushes aside"
response_harm = "hits"
harm_intent_damage = 8
melee_damage_lower = 10
melee_damage_upper = 10
attacktext = list("attacked")
attack_sound = 'sound/items/bikehorn.ogg'
say_list_type = /datum/say_list/clown
/datum/say_list/clown
speak = list("HONK", "Honk!", "Welcome to clown planet!")
emote_see = list("honks")
@@ -0,0 +1,26 @@
/mob/living/simple_mob/humanoid
mob_class = MOB_CLASS_HUMANOID
// Generic humanoid mob tolerances
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
unsuitable_atoms_damage = 15
health = 150 // Point of human crit, as of commenting
maxHealth = 150
// Most humans leave a corpse
var/corpse = null
/mob/living/simple_mob/humanoid/death()
..()
if(corpse)
new corpse (src.loc)
qdel(src)
return
@@ -0,0 +1,278 @@
///////////////////////////////
// Merc Mobs Go Here
///////////////////////////////
// Probably shouldn't use this directly, there are a bunch of sub-classes that are more complete.
/mob/living/simple_mob/humanoid/merc
name = "mercenary"
desc = "A tough looking heavily-armed individual."
tt_desc = "E Homo sapiens"
icon_state = "syndicate"
icon_living = "syndicate"
icon_dead = "syndicate_dead"
icon_gib = "syndicate_gib"
faction = "syndicate"
movement_cooldown = 4
status_flags = 0
response_help = "pokes"
response_disarm = "shoves"
response_harm = "hits"
harm_intent_damage = 5
melee_damage_lower = 15 //Tac Knife damage
melee_damage_upper = 15
attack_sharp = 1
attack_edge = 1
attacktext = list("slashed", "stabbed")
armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 100, rad = 100) // Same armor values as the vest they drop, plus simple mob immunities
corpse = /obj/effect/landmark/mobcorpse/syndicatesoldier
loot_list = list(/obj/item/weapon/material/knife/tacknife = 100) // Might as well give it the knife
ai_holder_type = /datum/ai_holder/simple_mob/merc
say_list_type = /datum/say_list/merc
// Grenade special attack vars
var/grenade_type = /obj/item/weapon/grenade/concussion
special_attack_cooldown = 45 SECONDS
special_attack_min_range = 2
special_attack_max_range = 7
////////////////////////////////
// Grenade Attack
////////////////////////////////
// Any merc can use this, just set special_attack_charges to a positive value
// Check if we should bother with the grenade
/mob/living/simple_mob/humanoid/merc/should_special_attack(atom/A)
var/mob_count = 0 // Are there enough mobs to consider grenading?
var/turf/T = get_turf(A)
for(var/mob/M in range(T, 2))
if(M.faction == faction) // Don't grenade our friends
return FALSE
if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade
if(!M.stat) // Dead things don't warrant a grenade
mob_count ++
if(mob_count < 2)
return FALSE
else
return TRUE
// Yes? Throw the grenade
/mob/living/simple_mob/humanoid/merc/do_special_attack(atom/A)
set waitfor = FALSE
set_AI_busy(TRUE)
var/obj/item/weapon/grenade/G = new grenade_type(get_turf(src))
if(istype(G))
G.throw_at(A, G.throw_range, G.throw_speed, src)
G.attack_self(src)
special_attack_charges = max(special_attack_charges-1, 0)
set_AI_busy(FALSE)
////////////////////////////////
// Merc AI Types
////////////////////////////////
/datum/ai_holder/simple_mob/merc
threaten = TRUE
returns_home = TRUE // Stay close to the base...
wander = TRUE // ... but "patrol" a little.
/datum/ai_holder/simple_mob/merc/ranged
pointblank = TRUE // They get close? Just shoot 'em!
firing_lanes = TRUE // But not your buddies!
conserve_ammo = TRUE // And don't go wasting bullets!
////////////////////////////////
// Melee
////////////////////////////////
/mob/living/simple_mob/humanoid/merc/melee // Defined in case we add non-sword-and-board mercs
loot_list = list(/obj/item/weapon/material/knife/tacknife = 100)
// Sword and Shield Merc
/mob/living/simple_mob/humanoid/merc/melee/sword
icon_state = "syndicatemelee"
icon_living = "syndicatemelee"
melee_damage_lower = 30
melee_damage_upper = 30
attack_armor_pen = 50
attack_sharp = 1
attack_edge = 1
attacktext = list("slashed")
loot_list = list(/obj/item/weapon/melee/energy/sword/red = 100, /obj/item/weapon/shield/energy = 100)
// They have a shield, so they try to block
/mob/living/simple_mob/humanoid/merc/melee/sword/attackby(var/obj/item/O as obj, var/mob/user as mob)
if(O.force)
if(prob(20))
visible_message("<span class='danger'>\The [src] blocks \the [O] with its shield!</span>")
if(user)
ai_holder.react_to_attack(user)
return
else
..()
else
to_chat(user, "<span class='warning'>This weapon is ineffective, it does no damage.</span>")
visible_message("<span class='warning'>\The [user] gently taps [src] with \the [O].</span>")
/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/item/projectile/Proj)
if(!Proj) return
if(prob(35))
visible_message("<font color='red'><B>[src] blocks [Proj] with its shield!</B></font>")
if(Proj.firer)
ai_holder.react_to_attack(Proj.firer)
return
else
..()
////////////////////////////////
// Ranged
////////////////////////////////
// Base Ranged Merc, so we don't have to redefine a million vars for every subtype. Uses a pistol.
/mob/living/simple_mob/humanoid/merc/ranged
icon_state = "syndicateranged"
icon_living = "syndicateranged"
projectiletype = /obj/item/projectile/bullet/pistol/medium
// casingtype = /obj/item/ammo_casing/spent //Makes infinite stacks of bullets when put in PoIs.
projectilesound = 'sound/weapons/Gunshot_light.ogg'
loot_list = list(/obj/item/weapon/gun/projectile/colt = 100)
needs_reload = TRUE
reload_max = 7 // Not the best default, but it fits the pistol
ai_holder_type = /datum/ai_holder/simple_mob/merc/ranged
// C20r SMG
/mob/living/simple_mob/humanoid/merc/ranged/smg
icon_state = "syndicateranged_smg"
icon_living = "syndicateranged_smg"
loot_list = list(/obj/item/weapon/gun/projectile/automatic/c20r = 100)
base_attack_cooldown = 5 // Two attacks a second or so.
reload_max = 20
// Laser Rifle
/mob/living/simple_mob/humanoid/merc/ranged/laser
icon_state = "syndicateranged_laser"
icon_living = "syndicateranged_laser"
projectiletype = /obj/item/projectile/beam/midlaser
projectilesound = 'sound/weapons/Laser.ogg'
loot_list = list(/obj/item/weapon/gun/energy/laser = 100)
reload_max = 10
// Ion Rifle
/mob/living/simple_mob/humanoid/merc/ranged/ionrifle
icon_state = "syndicateranged_ionrifle"
icon_living = "syndicateranged_ionrifle"
projectiletype = /obj/item/projectile/ion
projectilesound = 'sound/weapons/Laser.ogg'
loot_list = list(/obj/item/weapon/gun/energy/ionrifle = 100)
reload_max = 10
// Grenadier, Basically a miniboss
/mob/living/simple_mob/humanoid/merc/ranged/grenadier
icon_state = "syndicateranged_shotgun"
icon_living = "syndicateranged_shotgun"
projectiletype = /obj/item/projectile/bullet/pellet/shotgun // Buckshot
projectilesound = 'sound/weapons/gunshot/shotgun.ogg'
loot_list = list(/obj/item/weapon/gun/projectile/shotgun/pump = 100)
reload_max = 4
reload_time = 1.5 SECONDS // It's a shotgun, it takes a moment
special_attack_charges = 5
////////////////////////////////
// Space Mercs
////////////////////////////////
// Sword Space Merc
/mob/living/simple_mob/humanoid/merc/melee/sword/space
name = "syndicate commando"
icon_state = "syndicatemeleespace"
icon_living = "syndicatemeleespace"
movement_cooldown = 0
armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 100) // Same armor as their voidsuit
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
corpse = /obj/effect/landmark/mobcorpse/syndicatecommando
/mob/living/simple_mob/humanoid/merc/melee/sword/space/Process_Spacemove(var/check_drift = 0)
return
// Ranged Space Merc
/mob/living/simple_mob/humanoid/merc/ranged/space
name = "syndicate sommando"
icon_state = "syndicaterangedpsace"
icon_living = "syndicaterangedpsace"
movement_cooldown = 0
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
minbodytemp = 0
corpse = /obj/effect/landmark/mobcorpse/syndicatecommando
/mob/living/simple_mob/humanoid/merc/ranged/space/Process_Spacemove(var/check_drift = 0)
return
////////////////////////////////
// PoI Mercs
////////////////////////////////
// None of these drop weapons, until we have a better way to balance them
/mob/living/simple_mob/humanoid/merc/melee/poi
loot_list = list()
/mob/living/simple_mob/humanoid/merc/melee/sword/poi
loot_list = list()
/mob/living/simple_mob/humanoid/merc/ranged/poi
loot_list = list()
/mob/living/simple_mob/humanoid/merc/ranged/smg/poi
loot_list = list()
/mob/living/simple_mob/humanoid/merc/ranged/laser/poi
loot_list = list()
/mob/living/simple_mob/humanoid/merc/ranged/ionrifle
loot_list = list()
/mob/living/simple_mob/humanoid/merc/ranged/grenadier/poi
loot_list = list()

Some files were not shown because too many files have changed in this diff Show More