Merge pull request #4519 from Neerti/12/15/2017_berserking
Adds Berserk
@@ -33,7 +33,8 @@
|
||||
#define ARCHAEO_REMAINS_ROBOT 33
|
||||
#define ARCHAEO_REMAINS_XENO 34
|
||||
#define ARCHAEO_GASMASK 35
|
||||
#define MAX_ARCHAEO 35
|
||||
#define ARCHAEO_ALIEN_ITEM 36
|
||||
#define MAX_ARCHAEO 36
|
||||
|
||||
#define DIGSITE_GARDEN 1
|
||||
#define DIGSITE_ANIMAL 2
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
name = ""
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
layer = 20.0
|
||||
appearance_flags = TILE_BOUND|PIXEL_SCALE|NO_CLIENT_COLOR
|
||||
unacidable = 1
|
||||
var/obj/master = null //A reference to the object in the slot. Grabs or items, generally.
|
||||
var/datum/hud/hud = null // A reference to the owner HUD, if any.
|
||||
|
||||
33
code/game/gamemodes/changeling/powers/enrage.dm
Normal file
@@ -0,0 +1,33 @@
|
||||
/datum/power/changeling/enrage
|
||||
name = "Enrage"
|
||||
desc = "We evolve modifications to our mind and body, allowing us to call on intense periods of rage for our benefit."
|
||||
helptext = "Berserks us, giving massive bonuses to fighting in close quarters for thirty seconds, and losing the ability to \
|
||||
be accurate at ranged while active. Afterwards, we will suffer extreme amounts of exhaustion for a period of two minutes, \
|
||||
during which we will be much weaker and slower than before. We cannot berserk again while exhausted. This ability requires \
|
||||
a significant amount of nutrition to use, and cannot be used if too hungry. Using this ability will end most forms of disables."
|
||||
enhancedtext = "The length of exhaustion after berserking is reduced to one minute, from two, and requires half as much nutrition."
|
||||
ability_icon_state = "ling_berserk"
|
||||
genomecost = 2
|
||||
allowduringlesserform = 1
|
||||
verbpath = /mob/living/proc/changeling_berserk
|
||||
|
||||
// Makes the ling very upset.
|
||||
/mob/living/proc/changeling_berserk()
|
||||
set category = "Changeling"
|
||||
set name = "Enrage (30)"
|
||||
set desc = "Causes you to go Berserk."
|
||||
|
||||
var/datum/changeling/changeling = changeling_power(30,0,100)
|
||||
if(!changeling)
|
||||
return 0
|
||||
|
||||
var/modifier_to_use = /datum/modifier/berserk/changeling
|
||||
if(src.mind.changeling.recursive_enhancement)
|
||||
modifier_to_use = /datum/modifier/berserk/changeling/recursive
|
||||
to_chat(src, "<span class='notice'>We optimize our levels of anger, which will avoid excessive stress on ourselves.</span>")
|
||||
|
||||
if(add_modifier(modifier_to_use, 30 SECONDS))
|
||||
changeling.chem_charges -= 30
|
||||
|
||||
feedback_add_details("changeling_powers","EN")
|
||||
return 1
|
||||
@@ -234,7 +234,9 @@
|
||||
// apparently called whenever an item is removed from a slot, container, or anything else.
|
||||
/obj/item/proc/dropped(mob/user as mob)
|
||||
..()
|
||||
if(zoom) zoom() //binoculars, scope, etc
|
||||
if(zoom)
|
||||
zoom() //binoculars, scope, etc
|
||||
appearance_flags &= ~NO_CLIENT_COLOR
|
||||
|
||||
// called just as an item is picked up (loc is not yet changed)
|
||||
/obj/item/proc/pickup(mob/user)
|
||||
@@ -259,8 +261,11 @@
|
||||
// note this isn't called during the initial dressing of a player
|
||||
/obj/item/proc/equipped(var/mob/user, var/slot)
|
||||
layer = 20
|
||||
if(user.client) user.client.screen |= src
|
||||
if(user.pulling == src) user.stop_pulling()
|
||||
if(user.client)
|
||||
user.client.screen |= src
|
||||
if(user.pulling == src)
|
||||
user.stop_pulling()
|
||||
appearance_flags |= NO_CLIENT_COLOR
|
||||
return
|
||||
|
||||
//Defines which slots correspond to which slot flags
|
||||
|
||||
@@ -31,6 +31,12 @@
|
||||
return
|
||||
var/weapon_attack_speed = user.get_attack_speed(I) / 10
|
||||
var/weapon_damage = I.force
|
||||
var/modified_damage_percent = 1
|
||||
|
||||
for(var/datum/modifier/M in user.modifiers)
|
||||
if(!isnull(M.outgoing_melee_damage_percent))
|
||||
weapon_damage *= M.outgoing_melee_damage_percent
|
||||
modified_damage_percent *= M.outgoing_melee_damage_percent
|
||||
|
||||
if(istype(I, /obj/item/weapon/gun))
|
||||
var/obj/item/weapon/gun/G = I
|
||||
@@ -53,7 +59,7 @@
|
||||
qdel(P)
|
||||
|
||||
var/DPS = weapon_damage / weapon_attack_speed
|
||||
to_chat(user, "<span class='notice'>Damage: [weapon_damage]</span>")
|
||||
to_chat(user, "<span class='notice'>Damage: [weapon_damage][modified_damage_percent != 1 ? " (Modified by [modified_damage_percent*100]%)":""]</span>")
|
||||
to_chat(user, "<span class='notice'>Attack Speed: [weapon_attack_speed]/s</span>")
|
||||
to_chat(user, "<span class='notice'>\The [I] does <b>[DPS]</b> damage per second.</span>")
|
||||
if(DPS > 0)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Pumpkin head
|
||||
* Kitty ears
|
||||
* Holiday hats
|
||||
Crown of Wrath
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -208,3 +209,67 @@
|
||||
icon_state = "santahatgreen"
|
||||
item_state_slots = list(slot_r_hand_str = "santahatgreen", slot_l_hand_str = "santahatgreen")
|
||||
body_parts_covered = 0
|
||||
|
||||
/*
|
||||
* Xenoarch/Surface Loot Hats
|
||||
*/
|
||||
|
||||
// Triggers an effect when the wearer is 'in grave danger'.
|
||||
// Causes brainloss when it happens.
|
||||
/obj/item/clothing/head/psy_crown
|
||||
name = "broken crown"
|
||||
desc = "A crown-of-thorns with a missing gem."
|
||||
var/tension_threshold = 150
|
||||
var/cooldown = null // world.time of when this was last triggered.
|
||||
var/cooldown_duration = 3 MINUTES // How long the cooldown should be.
|
||||
var/flavor_equip = null // Message displayed to someone who puts this on their head. Drones don't get a message.
|
||||
var/flavor_unequip = null // Ditto, but for taking it off.
|
||||
var/flavor_drop = null // Ditto, but for dropping it.
|
||||
var/flavor_activate = null // Ditto, for but activating.
|
||||
var/brainloss_cost = 3 // Whenever it activates, inflict this much brainloss on the wearer, as its not good for the mind to wear things that manipulate it.
|
||||
|
||||
/obj/item/clothing/head/psy_crown/proc/activate_ability(var/mob/living/wearer)
|
||||
cooldown = world.time + cooldown_duration
|
||||
to_chat(wearer, flavor_activate)
|
||||
to_chat(wearer, "<span class='danger'>The inside of your head hurts...</span>")
|
||||
wearer.adjustBrainLoss(brainloss_cost)
|
||||
|
||||
/obj/item/clothing/head/psy_crown/equipped(var/mob/living/carbon/human/H)
|
||||
..()
|
||||
if(istype(H) && H.head == src && H.is_sentient())
|
||||
processing_objects += src
|
||||
to_chat(H, flavor_equip)
|
||||
|
||||
/obj/item/clothing/head/psy_crown/dropped(var/mob/living/carbon/human/H)
|
||||
..()
|
||||
processing_objects -= src
|
||||
if(H.is_sentient())
|
||||
if(loc == H) // Still inhand.
|
||||
to_chat(H, flavor_unequip)
|
||||
else
|
||||
to_chat(H, flavor_drop)
|
||||
|
||||
/obj/item/clothing/head/psy_crown/Destroy()
|
||||
processing_objects -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/head/psy_crown/process()
|
||||
if(isliving(loc))
|
||||
var/mob/living/L = loc
|
||||
if(world.time >= cooldown && L.is_sentient() && L.get_tension() >= tension_threshold)
|
||||
activate_ability(L)
|
||||
|
||||
|
||||
/obj/item/clothing/head/psy_crown/wrath
|
||||
name = "red crown"
|
||||
desc = "A crown-of-thorns set with a red gemstone that seems to glow unnaturally. It feels rather disturbing to touch."
|
||||
description_info = "This has a chance to cause the wearer to become extremely angry when in extreme danger."
|
||||
icon_state = "wrathcrown"
|
||||
flavor_equip = "<span class='warning'>You feel a bit angrier after putting on this crown.</span>"
|
||||
flavor_unequip = "<span class='notice'>You feel calmer after removing the crown.</span>"
|
||||
flavor_drop = "<span class='notice'>You feel much calmer after letting go of the crown.</span>"
|
||||
flavor_activate = "<span class='danger'>An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!</span>"
|
||||
|
||||
/obj/item/clothing/head/psy_crown/wrath/activate_ability(var/mob/living/wearer)
|
||||
..()
|
||||
wearer.add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
@@ -19,6 +19,7 @@
|
||||
var/light_range = null // How far the light for the above var goes. Not implemented yet.
|
||||
var/light_intensity = null // Ditto. Not implemented yet.
|
||||
var/mob_overlay_state = null // Icon_state for an overlay to apply to a (human) mob while this exists. This is actually implemented.
|
||||
var/client_color = null // If set, the client will have the world be shown in this color, from their perspective.
|
||||
|
||||
// Now for all the different effects.
|
||||
// Percentage modifiers are expressed as a multipler. (e.g. +25% damage should be written as 1.25)
|
||||
@@ -43,6 +44,7 @@
|
||||
var/metabolism_percent // Adjusts the mob's metabolic rate, which affects reagent processing. Won't affect mobs without reagent processing.
|
||||
var/icon_scale_percent // Makes the holder's icon get scaled up or down.
|
||||
var/attack_speed_percent // Makes the holder's 'attack speed' (click delay) shorter or longer.
|
||||
var/pain_immunity // Makes the holder not care about pain while this is on. Only really useful to human mobs.
|
||||
|
||||
/datum/modifier/New(var/new_holder, var/new_origin)
|
||||
holder = new_holder
|
||||
@@ -52,6 +54,11 @@
|
||||
origin = weakref(holder)
|
||||
..()
|
||||
|
||||
// Checks if the modifier should be allowed to be applied to the mob before attaching it.
|
||||
// Override for special criteria, e.g. forbidding robots from receiving it.
|
||||
/datum/modifier/proc/can_apply(var/mob/living/L)
|
||||
return TRUE
|
||||
|
||||
// Checks to see if this datum should continue existing.
|
||||
/datum/modifier/proc/check_if_valid()
|
||||
if(expire_at && expire_at < world.time) // Is our time up?
|
||||
@@ -66,8 +73,14 @@
|
||||
holder.update_modifier_visuals()
|
||||
if(icon_scale_percent) // Correct the scaling.
|
||||
holder.update_transform()
|
||||
if(client_color)
|
||||
holder.update_client_color()
|
||||
qdel(src)
|
||||
|
||||
// Override this for special effects when it gets added to the mob.
|
||||
/datum/modifier/proc/on_applied()
|
||||
return
|
||||
|
||||
// Override this for special effects when it gets removed.
|
||||
/datum/modifier/proc/on_expire()
|
||||
return
|
||||
@@ -114,15 +127,21 @@
|
||||
|
||||
// If we're at this point, the mob doesn't already have it, or it does but stacking is allowed.
|
||||
var/datum/modifier/mod = new modifier_type(src, origin)
|
||||
if(!mod.can_apply(src))
|
||||
qdel(mod)
|
||||
return
|
||||
if(expire_at)
|
||||
mod.expire_at = world.time + expire_at
|
||||
if(mod.on_created_text)
|
||||
to_chat(src, mod.on_created_text)
|
||||
modifiers.Add(mod)
|
||||
mod.on_applied()
|
||||
if(mod.mob_overlay_state)
|
||||
update_modifier_visuals()
|
||||
if(mod.icon_scale_percent)
|
||||
update_transform()
|
||||
if(mod.client_color)
|
||||
update_client_color()
|
||||
|
||||
return mod
|
||||
|
||||
|
||||
182
code/modules/mob/_modifiers/modifiers_misc.dm
Normal file
@@ -0,0 +1,182 @@
|
||||
// File for modifier defines that don't fit anywhere else. Since this is a misc file, it's doomed to get filled with everything like all the others.
|
||||
|
||||
|
||||
/*
|
||||
Berserk is a modifier that grants vastly increased melee capability for a short period of time, easily allowing the
|
||||
holder to do more than twice the damage they would normally do, as it both increases outgoing melee damage, and
|
||||
reduces their attack delay. The screen will also turn deep red and the holder will get larger in size.
|
||||
|
||||
The modifier also gives some defenses, in that it gives additional max health (this can bite them in the ass later since
|
||||
its not permanent), makes them move slightly faster, cancels disabling effects when it triggers, and reduces the power of
|
||||
future disables by a very large amount. It also suppresses pain until it expires, however berserking while under massive pain
|
||||
is likely to cause them to pass out when exhaustion hits.
|
||||
|
||||
Due to the intense rage felt by the holder, the focus needed to use ranged weapons is lost, making accuracy with them
|
||||
massively reduced. The holder also feels less of a need to evade attacks and will be easier to hit.
|
||||
|
||||
Berserk can be extended by having another instance try to affect the holder while in the middle of a berserk.
|
||||
|
||||
After the modifier expires, a second modifier representing exhaustion is placed, which inflicts massive maluses and prevents
|
||||
further berserks until it expires. This generally means that someone getting caught while exhausted will be much easier to fight,
|
||||
as they will be much slower, attack slower, do less melee damage, be easier to hit, and disabling effects will affect them harder.
|
||||
|
||||
Berserking causes the holder to feel hungrier. If they are starving, this modifier cannot be applied. Diona cannot
|
||||
be berserked, or those who are suffering from exhaustion. Non-Drone Synthetics that receive the berserk modifier will
|
||||
instead get a version that has no benefits, but will not cost nutrition or cause exhaustion. Drones cannot receive berserk, as
|
||||
they are emotionless automatrons.
|
||||
|
||||
Berserk is a somewhat rare modifier to obtain freely (and for good reason), however here are ways to see it in action;
|
||||
- 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.
|
||||
- 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.
|
||||
Will cause three brainloss in those affected due to the artifact meddling with their mind.
|
||||
- A rare alien artifact might be found on the Surface, or obtained from Xenoarch, that causes berserking when it thinks
|
||||
the wearer is in danger, however in addition to the usual drawbacks, each use causes three brainloss in the user, due to how
|
||||
the artifact triggers the rage.
|
||||
|
||||
*/
|
||||
|
||||
/datum/modifier/berserk
|
||||
name = "berserk"
|
||||
desc = "You are filled with an overwhelming rage."
|
||||
client_color = "#FF0000" // Make everything red!
|
||||
mob_overlay_state = "berserk"
|
||||
|
||||
on_created_text = "<span class='critical'>You feel an intense and overwhelming rage overtake you as you go berserk!</span>"
|
||||
on_expired_text = "<span class='notice'>The blaze of rage inside you has ran out.</span>"
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
// The good stuff.
|
||||
slowdown = -1 // Move a bit faster.
|
||||
attack_speed_percent = 0.66 // Attack at 2/3 the normal delay.
|
||||
outgoing_melee_damage_percent = 1.5 // 50% more damage from melee.
|
||||
max_health_percent = 1.5 // More health as a buffer, however the holder might fall into crit after this expires if they're mortally wounded.
|
||||
disable_duration_percent = 0.25 // Disables only last 25% as long.
|
||||
icon_scale_percent = 1.2 // Look scarier.
|
||||
pain_immunity = TRUE // Avoid falling over from shock (at least until it expires).
|
||||
|
||||
// The less good stuff.
|
||||
accuracy = -5 // Aiming requires focus.
|
||||
accuracy_dispersion = 3 // Ditto.
|
||||
evasion = -3 // Too angry to dodge.
|
||||
|
||||
var/nutrition_cost = 150
|
||||
var/exhaustion_duration = 2 MINUTES // How long the exhaustion modifier lasts after it expires. Set to 0 to not apply one.
|
||||
var/last_shock_stage = 0
|
||||
|
||||
|
||||
// For changelings.
|
||||
/datum/modifier/berserk/changeling
|
||||
on_created_text = "<span class='critical'>We feel an intense and overwhelming rage overtake us as we go berserk!</span>"
|
||||
on_expired_text = "<span class='notice'>The blaze of rage inside us has ran out.</span>"
|
||||
|
||||
// For changelings who bought the Recursive Enhancement evolution.
|
||||
/datum/modifier/berserk/changeling/recursive
|
||||
exhaustion_duration = 1 MINUTE
|
||||
nutrition_cost = 75
|
||||
|
||||
|
||||
/datum/modifier/berserk/on_applied()
|
||||
if(ishuman(holder)) // Most other mobs don't really use nutrition and can't get it back.
|
||||
holder.nutrition = max(0, holder.nutrition - nutrition_cost)
|
||||
holder.visible_message("<span class='critical'>\The [holder] descends into an all consuming rage!</span>")
|
||||
|
||||
// End all stuns.
|
||||
holder.SetParalysis(0)
|
||||
holder.SetStunned(0)
|
||||
holder.SetWeakened(0)
|
||||
holder.setHalLoss(0)
|
||||
holder.lying = 0
|
||||
holder.update_canmove()
|
||||
|
||||
// Temporarily end pain.
|
||||
if(ishuman(holder))
|
||||
var/mob/living/carbon/human/H = holder
|
||||
last_shock_stage = H.shock_stage
|
||||
H.shock_stage = 0
|
||||
|
||||
/datum/modifier/berserk/on_expire()
|
||||
if(exhaustion_duration > 0 && holder.stat != DEAD)
|
||||
holder.add_modifier(/datum/modifier/berserk_exhaustion, exhaustion_duration)
|
||||
|
||||
if(prob(last_shock_stage))
|
||||
to_chat(holder, "<span class='warning'>You pass out from the pain you were suppressing.</span>")
|
||||
holder.Paralyse(5)
|
||||
|
||||
if(ishuman(holder))
|
||||
var/mob/living/carbon/human/H = holder
|
||||
H.shock_stage = last_shock_stage
|
||||
|
||||
/datum/modifier/berserk/can_apply(var/mob/living/L)
|
||||
if(L.stat)
|
||||
to_chat(L, "<span class='warning'>You can't be unconscious or dead to berserk.</span>")
|
||||
return FALSE // It would be weird to see a dead body get angry all of a sudden.
|
||||
|
||||
if(!L.is_sentient())
|
||||
return FALSE // Drones don't feel anything.
|
||||
|
||||
if(L.has_modifier_of_type(/datum/modifier/berserk_exhaustion))
|
||||
to_chat(L, "<span class='warning'>You recently berserked, and cannot do so again while exhausted.</span>")
|
||||
return FALSE // On cooldown.
|
||||
|
||||
if(L.isSynthetic())
|
||||
L.add_modifier(/datum/modifier/berserk_synthetic, 30 SECONDS)
|
||||
return FALSE // Borgs can get angry but their metal shell can't be pushed harder by just being mad. Same for Posibrains.
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.species.name == "Diona")
|
||||
to_chat(L, "<span class='warning'>You feel strange for a moment, but it passes.</span>")
|
||||
return FALSE // Happy trees aren't affected by blood rages.
|
||||
|
||||
if(L.nutrition < nutrition_cost)
|
||||
to_chat(L, "<span class='warning'>You are too hungry to berserk.</span>")
|
||||
return FALSE // Too hungry to enrage.
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/modifier/berserk/tick()
|
||||
if(holder.stat == DEAD)
|
||||
expire(silent = TRUE)
|
||||
|
||||
|
||||
// Applied when berserk expires. Acts as a downside as well as the cooldown for berserk.
|
||||
/datum/modifier/berserk_exhaustion
|
||||
name = "exhaustion"
|
||||
desc = "You recently exerted yourself extremely hard, and need a rest."
|
||||
|
||||
on_created_text = "<span class='warning'>You feel extremely exhausted.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel less exhausted now.</span>"
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
slowdown = 2
|
||||
attack_speed_percent = 1.5
|
||||
outgoing_melee_damage_percent = 0.6
|
||||
disable_duration_percent = 1.5
|
||||
evasion = -2
|
||||
|
||||
/datum/modifier/berserk_exhaustion/on_applied()
|
||||
holder.visible_message("<span class='warning'>\The [holder] looks exhausted.</span>")
|
||||
|
||||
|
||||
// Synth version with no benefits due to a loss of focus inside a metal shell, which can't be pushed harder just be being mad.
|
||||
// Fortunately there is no exhaustion or nutrition cost.
|
||||
/datum/modifier/berserk_synthetic
|
||||
name = "recklessness"
|
||||
desc = "You are filled with an overwhelming rage, however your metal shell prevents taking advantage of this."
|
||||
client_color = "#FF0000" // Make everything red!
|
||||
mob_overlay_state = "berserk"
|
||||
|
||||
on_created_text = "<span class='danger'>You feel an intense and overwhelming rage overtake you as you go berserk! \
|
||||
Unfortunately, your lifeless body cannot benefit from this. You feel reckless...</span>"
|
||||
on_expired_text = "<span class='notice'>The blaze of rage inside your mind has ran out.</span>"
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
// Just being mad isn't gonna overclock your body when you're a beepboop.
|
||||
accuracy = -5 // Aiming requires focus.
|
||||
accuracy_dispersion = 3 // Ditto.
|
||||
evasion = -3 // Too angry to dodge.
|
||||
@@ -296,6 +296,9 @@
|
||||
/mob/living/bot/proc/explode()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/bot/is_sentient()
|
||||
return FALSE
|
||||
|
||||
/******************************************************************/
|
||||
// Navigation procs
|
||||
// Used for A-star pathfinding
|
||||
|
||||
@@ -1506,12 +1506,20 @@
|
||||
/mob/living/carbon/human/can_feel_pain(var/obj/item/organ/check_organ)
|
||||
if(isSynthetic())
|
||||
return 0
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(M.pain_immunity == TRUE)
|
||||
return 0
|
||||
if(check_organ)
|
||||
if(!istype(check_organ))
|
||||
return 0
|
||||
return check_organ.organ_can_feel_pain()
|
||||
return !(species.flags & NO_PAIN)
|
||||
|
||||
/mob/living/carbon/human/is_sentient()
|
||||
if(get_FBP_type() == FBP_DRONE)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/is_muzzled()
|
||||
return (wear_mask && (istype(wear_mask, /obj/item/clothing/mask/muzzle) || istype(src.wear_mask, /obj/item/weapon/grenade)))
|
||||
|
||||
|
||||
@@ -1013,6 +1013,9 @@ default behaviour is:
|
||||
/mob/living/proc/equip_post_job()
|
||||
return
|
||||
|
||||
// Used to check if something is capable of thought, in the traditional sense.
|
||||
/mob/living/proc/is_sentient()
|
||||
return TRUE
|
||||
|
||||
/mob/living/update_transform()
|
||||
// First, get the correct size.
|
||||
@@ -1025,4 +1028,40 @@ default behaviour is:
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(desired_scale)
|
||||
M.Translate(0, 16*(desired_scale-1))
|
||||
src.transform = M
|
||||
animate(src, transform = M, time = 10)
|
||||
|
||||
// This handles setting the client's color variable, which makes everything look a specific color.
|
||||
// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
|
||||
/mob/living/update_client_color()
|
||||
if(!client)
|
||||
return
|
||||
|
||||
var/list/colors_to_blend = list()
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.client_color))
|
||||
colors_to_blend += M.client_color
|
||||
|
||||
if(colors_to_blend.len)
|
||||
var/final_color
|
||||
if(colors_to_blend.len == 1) // If it's just one color we can skip all of this work.
|
||||
final_color = colors_to_blend[1]
|
||||
|
||||
else // Otherwise we need to do some messy additive blending.
|
||||
var/R = 0
|
||||
var/G = 0
|
||||
var/B = 0
|
||||
|
||||
for(var/C in colors_to_blend)
|
||||
var/RGB = hex2rgb(C)
|
||||
R = between(0, R + RGB[1], 255)
|
||||
G = between(0, G + RGB[2], 255)
|
||||
B = between(0, B + RGB[3], 255)
|
||||
final_color = rgb(R,G,B)
|
||||
|
||||
if(final_color)
|
||||
var/old_color = client.color // Don't know if BYOND has an internal optimization to not care about animate() calls that effectively do nothing.
|
||||
if(final_color != old_color) // Gonna do a check just incase.
|
||||
animate(client, color = final_color, time = 10)
|
||||
|
||||
else // No colors, so remove the client's color.
|
||||
animate(client, color = null, time = 10)
|
||||
|
||||
@@ -791,5 +791,10 @@ var/list/ai_verbs_default = list(
|
||||
if(rig)
|
||||
rig.force_rest(src)
|
||||
|
||||
/mob/living/silicon/ai/is_sentient()
|
||||
// AI cores don't store what brain was used to build them so we're just gonna assume they can think to some degree.
|
||||
// If that is ever fixed please update this proc.
|
||||
return TRUE
|
||||
|
||||
#undef AI_CHECK_WIRELESS
|
||||
#undef AI_CHECK_RADIO
|
||||
|
||||
@@ -67,6 +67,9 @@ var/list/mob_hat_cache = list()
|
||||
hat.loc = get_turf(src)
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/drone/is_sentient()
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/robot/drone/construction
|
||||
icon_state = "constructiondrone"
|
||||
law_type = /datum/ai_laws/construction_drone
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
handle_actions()
|
||||
handle_instability()
|
||||
// For some reason borg Life() doesn't call ..()
|
||||
handle_modifiers()
|
||||
handle_light()
|
||||
|
||||
if(client)
|
||||
|
||||
@@ -1090,3 +1090,6 @@
|
||||
src << "Hack attempt detected."
|
||||
return 1
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/is_sentient()
|
||||
return braintype != "Drone"
|
||||
@@ -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 *40
|
||||
#define LASERS_TO_KILL *30
|
||||
|
||||
// Default hivebot is melee, and a bit more meaty, so it can meatshield for their ranged friends.
|
||||
/mob/living/simple_animal/hostile/hivebot
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
intelligence_level = SA_ANIMAL
|
||||
cooperative = 1
|
||||
|
||||
maxHealth = 60
|
||||
health = 60
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
turns_per_move = 5
|
||||
see_in_dark = 6
|
||||
stop_when_pulled = 0
|
||||
@@ -43,9 +43,20 @@
|
||||
|
||||
meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat
|
||||
|
||||
var/stance_step = 0
|
||||
// var/stance_step = 0
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/handle_stance()
|
||||
/mob/living/simple_animal/hostile/bear/handle_stance(var/new_stance)
|
||||
// Below was a bunch of code that made this specific mob be 'alert' and will hurt you when it gets closer.
|
||||
// It's commented out because it made infinite loops and the AI is going to be moved/rewritten sometime soon (famous last words)
|
||||
// and it would be better if this 'alert before attacking' behaviour was on the parent instead of a specific type of mob anyways.
|
||||
|
||||
// Instead we're just gonna get angry if we're dying.
|
||||
..(new_stance)
|
||||
if(stance == STANCE_ATTACK || stance == STANCE_ATTACKING)
|
||||
if((health / maxHealth) <= 0.5) // At half health, and fighting someone currently.
|
||||
add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
|
||||
/*
|
||||
switch(stance)
|
||||
if(STANCE_TIRED)
|
||||
stop_automated_movement = 1
|
||||
@@ -87,6 +98,7 @@
|
||||
return
|
||||
else
|
||||
..()
|
||||
*/
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/update_icons()
|
||||
..()
|
||||
@@ -103,7 +115,7 @@
|
||||
. = ..()
|
||||
if(.)
|
||||
custom_emote(1,"stares alertly at [.]")
|
||||
handle_stance(STANCE_ALERT)
|
||||
// handle_stance(STANCE_ALERT)
|
||||
|
||||
/mob/living/simple_animal/hostile/bear/PunchTarget()
|
||||
if(!Adjacent(target_mob))
|
||||
|
||||
@@ -1474,6 +1474,9 @@
|
||||
return
|
||||
set_target(new_target)
|
||||
|
||||
/mob/living/simple_animal/is_sentient()
|
||||
return intelligence_level != SA_PLANT && intelligence_level != SA_ROBOTIC
|
||||
|
||||
//Commands, reactions, etc
|
||||
/mob/living/simple_animal/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)
|
||||
..()
|
||||
|
||||
@@ -469,6 +469,10 @@
|
||||
enrage() // How dare you try to control the red slime.
|
||||
say("Grrr...!")
|
||||
|
||||
/mob/living/simple_animal/slime/red/enrage()
|
||||
..()
|
||||
add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
|
||||
|
||||
/mob/living/simple_animal/slime/green
|
||||
desc = "This slime is radioactive."
|
||||
|
||||
@@ -46,5 +46,6 @@
|
||||
client.perspective = MOB_PERSPECTIVE
|
||||
reload_fullscreen() // Reload any fullscreen overlays this mob has.
|
||||
add_click_catcher()
|
||||
update_client_color()
|
||||
//set macro to normal incase it was overriden (like cyborg currently does)
|
||||
winset(src, null, "mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#D3B5B5")
|
||||
|
||||
@@ -1118,3 +1118,11 @@ mob/proc/yank_out_object()
|
||||
return
|
||||
var/obj/screen/zone_sel/selector = mob.zone_sel
|
||||
selector.set_selected_zone(next_in_list(mob.zone_sel.selecting,zones))
|
||||
|
||||
// This handles setting the client's color variable, which makes everything look a specific color.
|
||||
// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
|
||||
// This is for inheritence since /mob/living will serve most cases. If you need ghosts to use this you'll have to implement that yourself.
|
||||
/mob/proc/update_client_color()
|
||||
if(client && client.color)
|
||||
animate(client, color = null, time = 10)
|
||||
return
|
||||
131
code/modules/tension/tension.dm
Normal file
@@ -0,0 +1,131 @@
|
||||
// This code is used to give a very rough estimate of how screwed an individual player might be at any given moment when fighting monsters.
|
||||
// You could use this to have an effect trigger when someone is in serious danger, or as a means for an AI to guess which mob needs to die first.
|
||||
// The idea and the code structure was taken from Dungeon Crawl Stone Soup.
|
||||
|
||||
/atom/movable/proc/get_threat(var/mob/living/threatened)
|
||||
return 0
|
||||
|
||||
|
||||
/atom/movable/proc/guess_threat_level(var/mob/living/threatened)
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal
|
||||
var/threat_level = null // Set this if you want an explicit danger rating.
|
||||
|
||||
/mob/living/simple_animal/guess_threat_level(var/mob/living/threatened)
|
||||
if(threat_level) // If they have a predefined number, use it.
|
||||
return threat_level
|
||||
// Otherwise we need to guess how scary this thing is.
|
||||
var/threat_guess = 0
|
||||
|
||||
// First lets consider their attack ability.
|
||||
var/potential_damage = 0
|
||||
if(!ranged) //Melee damage.
|
||||
potential_damage = (melee_damage_lower + melee_damage_upper) / 2
|
||||
else
|
||||
if(projectiletype)
|
||||
var/obj/item/projectile/P = new projectiletype(src)
|
||||
if(P.nodamage || P.taser_effect) // Tasers are somewhat less scary.
|
||||
potential_damage = P.agony / 2
|
||||
else
|
||||
potential_damage = P.damage
|
||||
if(P.damage_type == HALLOSS) // Not sure if any projectiles do this, but can't be too safe.
|
||||
potential_damage /= 2
|
||||
// Rubber bullets, I guess.
|
||||
potential_damage += P.agony / 2
|
||||
|
||||
if(rapid) // This makes them shoot three times per cycle.
|
||||
potential_damage *= 3
|
||||
|
||||
qdel(P)
|
||||
threat_guess += potential_damage
|
||||
|
||||
// Then consider their defense.
|
||||
threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat.
|
||||
|
||||
return threat_guess
|
||||
|
||||
/mob/living/get_threat(var/mob/living/threatened)
|
||||
if(stat)
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/simple_animal/get_threat(var/mob/living/threatened)
|
||||
. = ..()
|
||||
|
||||
if(!hostile)
|
||||
return 0 // Can't hurt anyone.
|
||||
|
||||
if(incapacitated(INCAPACITATION_DISABLED))
|
||||
return 0 // Can't currently hurt you if it's stunned.
|
||||
|
||||
var/friendly = threatened.faction == faction
|
||||
|
||||
var/threat = guess_threat_level()
|
||||
|
||||
// Hurt entities contribute less tension.
|
||||
threat *= health
|
||||
threat /= getMaxHealth()
|
||||
|
||||
// Allies reduce tension instead of adding.
|
||||
if(friendly)
|
||||
threat = -threat
|
||||
|
||||
else
|
||||
if(threatened.invisibility > see_invisible)
|
||||
threat /= 2 // Target cannot be seen by src.
|
||||
if(invisibility > threatened.see_invisible)
|
||||
threat *= 2 // Target cannot see src.
|
||||
|
||||
// Handle statuses.
|
||||
if(confused)
|
||||
threat /= 2
|
||||
|
||||
if(has_modifier_of_type(/datum/modifier/berserk))
|
||||
threat *= 2
|
||||
|
||||
// Handle ability to harm.
|
||||
// Being five tiles away from some spiders is a lot less scary than being in melee range of five spiders at once.
|
||||
if(!ranged)
|
||||
threat /= max(get_dist(src, threatened), 1)
|
||||
|
||||
return threat
|
||||
|
||||
|
||||
|
||||
// Gives a rough idea of how much danger someone is in. Meant to be used for PvE things since PvP has too many unknown variables.
|
||||
/mob/living/proc/get_tension()
|
||||
var/tension = 0
|
||||
var/list/potential_threats = list()
|
||||
|
||||
// First, get everything threatening to us.
|
||||
for(var/thing in view(src))
|
||||
if(isliving(thing))
|
||||
potential_threats += thing
|
||||
if(istype(thing, /obj/machinery/porta_turret))
|
||||
potential_threats += thing
|
||||
|
||||
var/danger = FALSE
|
||||
// Now to get all the threats.
|
||||
for(var/atom/movable/AM in potential_threats)
|
||||
var/tension_from_AM = AM.get_threat(src)
|
||||
tension += tension_from_AM
|
||||
if(tension_from_AM > 0)
|
||||
danger = TRUE
|
||||
|
||||
if(!danger)
|
||||
return 0
|
||||
|
||||
// Tension is roughly doubled when about to fall into crit.
|
||||
var/max_health = getMaxHealth()
|
||||
tension *= 2 * max_health / (health + max_health)
|
||||
|
||||
// Being unable to act is really tense.
|
||||
if(incapacitated(INCAPACITATION_DISABLED) && !lying)
|
||||
tension *= 10
|
||||
return tension
|
||||
|
||||
if(confused)
|
||||
tension *= 2
|
||||
|
||||
return tension
|
||||
@@ -48,6 +48,17 @@
|
||||
if(prob(25))
|
||||
my_effect.trigger = pick(TRIGGER_WATER, TRIGGER_ACID, TRIGGER_VOLATILE, TRIGGER_TOXIN)
|
||||
|
||||
/obj/machinery/artifact/proc/choose_effect()
|
||||
var/effect_type = input(usr, "What type do you want?", "Effect Type") as null|anything in typesof(/datum/artifact_effect) - /datum/artifact_effect
|
||||
if(effect_type)
|
||||
my_effect = new effect_type(src)
|
||||
if(alert(usr, "Do you want a secondary effect?", "Second Effect", "No", "Yes") == "Yes")
|
||||
var/second_effect_type = input(usr, "What type do you want as well?", "Second Effect Type") as null|anything in typesof(/datum/artifact_effect) - list(/datum/artifact_effect, effect_type)
|
||||
secondary_effect = new second_effect_type(src)
|
||||
else
|
||||
secondary_effect = null
|
||||
|
||||
|
||||
/obj/machinery/artifact/process()
|
||||
var/turf/L = loc
|
||||
if(!istype(L)) // We're inside a container or on null turf, either way stop processing effects
|
||||
|
||||
43
code/modules/xenoarcheaology/effects/berserk.dm
Normal file
@@ -0,0 +1,43 @@
|
||||
/datum/artifact_effect/berserk
|
||||
name = "berserk"
|
||||
effect_type = EFFECT_PSIONIC
|
||||
|
||||
/datum/artifact_effect/berserk/proc/apply_berserk(var/mob/living/L)
|
||||
if(!istype(L))
|
||||
return FALSE
|
||||
|
||||
if(!L.is_sentient())
|
||||
return FALSE // Drons are presumably deaf to any psionic things.
|
||||
|
||||
if(L.add_modifier(/datum/modifier/berserk, 30 SECONDS))
|
||||
to_chat(L, "<span class='danger'>An otherworldly feeling seems to enter your mind, and it ignites your mind in fury!</span>")
|
||||
L.adjustBrainLoss(3) // Playing with berserking alien psychic artifacts isn't good for the mind.
|
||||
to_chat(L, "<span class='danger'>The inside of your head hurts...</span>")
|
||||
return TRUE
|
||||
else
|
||||
if(L.has_modifier_of_type(/datum/modifier/berserk)) // Already angry.
|
||||
to_chat(L, "<span class='warning'>An otherworldly feeling seems to enter your mind again, and it fans your inner flame, extending your rage.</span>")
|
||||
else // Exhausted or something.
|
||||
to_chat(L, "<span class='warning'>An otherworldly feeling seems to enter your mind, and you briefly feel an intense anger, but \
|
||||
it quickly passes.</span>")
|
||||
return FALSE
|
||||
|
||||
/datum/artifact_effect/berserk/DoEffectTouch(var/mob/toucher)
|
||||
if(toucher && isliving(toucher))
|
||||
apply_berserk(toucher)
|
||||
return TRUE
|
||||
|
||||
/datum/artifact_effect/berserk/DoEffectAura()
|
||||
if(holder)
|
||||
var/turf/T = get_turf(holder)
|
||||
for(var/mob/living/L in range(src.effectrange,T))
|
||||
if(prob(10))
|
||||
apply_berserk(L)
|
||||
return TRUE
|
||||
|
||||
/datum/artifact_effect/berserk/DoEffectPulse()
|
||||
if(holder)
|
||||
var/turf/T = get_turf(holder)
|
||||
for(var/mob/living/L in range(src.effectrange,T))
|
||||
apply_berserk(L)
|
||||
return TRUE
|
||||
@@ -409,6 +409,37 @@
|
||||
new_item = new /obj/item/clothing/mask/gas/poltergeist(src.loc)
|
||||
else
|
||||
new_item = new /obj/item/clothing/mask/gas(src.loc)
|
||||
if(36)
|
||||
// Alien stuff.
|
||||
apply_prefix = FALSE
|
||||
apply_material_decorations = FALSE
|
||||
|
||||
var/list/alien_stuff = list(
|
||||
/obj/item/device/multitool/alien,
|
||||
/obj/item/stack/cable_coil/alien,
|
||||
/obj/item/weapon/crowbar/alien,
|
||||
/obj/item/weapon/screwdriver/alien,
|
||||
/obj/item/weapon/weldingtool/alien,
|
||||
/obj/item/weapon/wirecutters/alien,
|
||||
/obj/item/weapon/wrench/alien,
|
||||
/obj/item/weapon/surgical/FixOVein/alien,
|
||||
/obj/item/weapon/surgical/bone_clamp/alien,
|
||||
/obj/item/weapon/surgical/cautery/alien,
|
||||
/obj/item/weapon/surgical/circular_saw/alien,
|
||||
/obj/item/weapon/surgical/hemostat/alien,
|
||||
/obj/item/weapon/surgical/retractor/alien,
|
||||
/obj/item/weapon/surgical/scalpel/alien,
|
||||
/obj/item/weapon/surgical/surgicaldrill/alien,
|
||||
/obj/item/weapon/cell/device/weapon/recharge/alien,
|
||||
/obj/item/clothing/suit/armor/alien,
|
||||
/obj/item/clothing/head/helmet/alien,
|
||||
/obj/item/clothing/head/psy_crown/wrath
|
||||
)
|
||||
|
||||
var/new_type = pick(alien_stuff)
|
||||
new_item = new new_type(src.loc)
|
||||
item_type = new_item.name
|
||||
|
||||
var/decorations = ""
|
||||
if(apply_material_decorations)
|
||||
source_material = pick("cordite","quadrinium",DEFAULT_WALL_MATERIAL,"titanium","aluminium","ferritic-alloy","plasteel","duranium")
|
||||
|
||||
@@ -76,6 +76,7 @@ var/global/list/finds_as_strings = list(
|
||||
100;ARCHAEO_TELEBEACON,
|
||||
100;ARCHAEO_TOOL,
|
||||
100;ARCHAEO_STOCKPARTS,
|
||||
100;ARCHAEO_ALIEN_ITEM,
|
||||
75;ARCHAEO_SHARD,
|
||||
75;ARCHAEO_RODS,
|
||||
75;ARCHAEO_UNKNOWN,
|
||||
@@ -111,6 +112,7 @@ var/global/list/finds_as_strings = list(
|
||||
50;ARCHAEO_CULTROBES,
|
||||
50;ARCHAEO_CULTBLADE,
|
||||
50;ARCHAEO_GASMASK,
|
||||
50;ARCHAEO_ALIEN_ITEM,
|
||||
25;ARCHAEO_HANDCUFFS,
|
||||
25;ARCHAEO_BEARTRAP,
|
||||
25;ARCHAEO_TOOL)
|
||||
|
||||
@@ -564,12 +564,19 @@
|
||||
if(S.stat || S.docile || S.rabid)
|
||||
continue
|
||||
|
||||
S.add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
|
||||
if(S.client) // Player slimes always have free will.
|
||||
to_chat(S, "<span class='warning'>An intense wave of rage almost overcomes you, but you remain in control of yourself.</span>")
|
||||
to_chat(S, "<span class='warning'>An intense wave of rage is felt from inside, but you remain in control of yourself.</span>")
|
||||
continue
|
||||
|
||||
S.enrage()
|
||||
|
||||
for(var/mob/living/carbon/human/H in view(get_turf(holder.my_atom)))
|
||||
if(H.species.name == "Promethean")
|
||||
H.add_modifier(/datum/modifier/berserk, 30 SECONDS)
|
||||
to_chat(H, "<span class='warning'>An intense wave of rage is felt from inside, but you remain in control of yourself.</span>")
|
||||
|
||||
log_and_message_admins("Red extract reaction (enrage) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.fingerprintslast]")
|
||||
|
||||
playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 75, 1)
|
||||
|
||||
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 97 KiB |
@@ -405,6 +405,7 @@
|
||||
#include "code\game\gamemodes\changeling\powers\endoarmor.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\enfeebling_string.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\engorged_glands.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\enrage.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\epinephrine_overdose.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\escape_restraints.dm"
|
||||
#include "code\game\gamemodes\changeling\powers\extract_dna_sting.dm"
|
||||
@@ -1642,6 +1643,7 @@
|
||||
#include "code\modules\mob\update_icons.dm"
|
||||
#include "code\modules\mob\_modifiers\cloning.dm"
|
||||
#include "code\modules\mob\_modifiers\modifiers.dm"
|
||||
#include "code\modules\mob\_modifiers\modifiers_misc.dm"
|
||||
#include "code\modules\mob\_modifiers\traits.dm"
|
||||
#include "code\modules\mob\_modifiers\traits_phobias.dm"
|
||||
#include "code\modules\mob\dead\death.dm"
|
||||
@@ -2259,6 +2261,7 @@
|
||||
#include "code\modules\tables\rack.dm"
|
||||
#include "code\modules\tables\tables.dm"
|
||||
#include "code\modules\tables\update_triggers.dm"
|
||||
#include "code\modules\tension\tension.dm"
|
||||
#include "code\modules\turbolift\_turbolift.dm"
|
||||
#include "code\modules\turbolift\turbolift.dm"
|
||||
#include "code\modules\turbolift\turbolift_areas.dm"
|
||||
@@ -2301,6 +2304,7 @@
|
||||
#include "code\modules\xenoarcheaology\artifacts\gigadrill.dm"
|
||||
#include "code\modules\xenoarcheaology\artifacts\replicator.dm"
|
||||
#include "code\modules\xenoarcheaology\effects\badfeeling.dm"
|
||||
#include "code\modules\xenoarcheaology\effects\berserk.dm"
|
||||
#include "code\modules\xenoarcheaology\effects\cellcharge.dm"
|
||||
#include "code\modules\xenoarcheaology\effects\celldrain.dm"
|
||||
#include "code\modules\xenoarcheaology\effects\cold.dm"
|
||||
|
||||