mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-23 20:17:15 +01:00
Merges AI Branch into Master
This commit is contained in:
@@ -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.
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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,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.
|
||||
@@ -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 .
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
Reference in New Issue
Block a user