mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-08-30 00:30:06 +01:00
Hivebots
This commit is contained in:
@@ -127,6 +127,18 @@
|
||||
usr << "<span class='notice'>Right Mouse Button on turf/obj/mob = Reset glowing</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button on buildmode button = Change glow properties</span>"
|
||||
usr << "<span class='notice'>***********************************************************</span>"
|
||||
if(9) // Control mobs with ai_holders.
|
||||
usr << "<span class='notice'>***********************************************************</span>"
|
||||
usr << "<span class='notice'>Left Mouse Button on AI mob = Select/Deselect mob</span>"
|
||||
usr << "<span class='notice'>Left Mouse Button + alt on AI mob = Toggle hostility on mob</span>"
|
||||
usr << "<span class='notice'>Left Mouse Button + ctrl on AI mob = Reset target/following/movement</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button on enemy mob = Command selected mobs to attack mob</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button on allied mob = Command selected mobs to follow mob</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be inturrupted by enemies)</span>"
|
||||
usr << "<span class='notice'>Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf</span>"
|
||||
usr << "<span class='notice'>***********************************************************</span>"
|
||||
return 1
|
||||
|
||||
/obj/effect/bmode/buildquit
|
||||
@@ -146,6 +158,7 @@
|
||||
var/obj/effect/bmode/buildmode/buildmode = null
|
||||
var/obj/effect/bmode/buildquit/buildquit = null
|
||||
var/atom/movable/throw_atom = null
|
||||
var/list/selected_mobs = list()
|
||||
|
||||
/obj/effect/bmode/buildholder/Destroy()
|
||||
qdel(builddir)
|
||||
@@ -158,6 +171,7 @@
|
||||
buildquit = null
|
||||
throw_atom = null
|
||||
cl = null
|
||||
selected_mobs.Cut()
|
||||
return ..()
|
||||
|
||||
/obj/effect/bmode/buildmode
|
||||
@@ -210,6 +224,9 @@
|
||||
master.cl.buildmode = 8
|
||||
src.icon_state = "buildmode8"
|
||||
if(8)
|
||||
master.cl.buildmode = 9
|
||||
src.icon_state = "buildmode9"
|
||||
if(9)
|
||||
master.cl.buildmode = 1
|
||||
src.icon_state = "buildmode1"
|
||||
|
||||
@@ -416,6 +433,74 @@
|
||||
if(pa.Find("right"))
|
||||
if(object)
|
||||
object.set_light(0, 0, "#FFFFFF")
|
||||
if(9) // AI control
|
||||
if(pa.Find("left"))
|
||||
if(isliving(object))
|
||||
var/mob/living/L = object
|
||||
// Reset processes.
|
||||
if(pa.Find("ctrl"))
|
||||
if(!isnull(L.get_AI_stance())) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on.
|
||||
var/datum/ai_holder/AI = L.ai_holder
|
||||
AI.forget_everything()
|
||||
to_chat(user, span("notice", "\The [L]'s AI has forgotten its target/movement destination/leader."))
|
||||
else
|
||||
to_chat(user, span("warning", "\The [L] is not AI controlled."))
|
||||
return
|
||||
|
||||
// Toggle hostility
|
||||
if(pa.Find("alt"))
|
||||
if(!isnull(L.get_AI_stance()))
|
||||
var/datum/ai_holder/AI = L.ai_holder
|
||||
AI.hostile = !AI.hostile
|
||||
to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"]."))
|
||||
else
|
||||
to_chat(user, span("warning", "\The [L] is not AI controlled."))
|
||||
return
|
||||
|
||||
// Select/Deselect
|
||||
if(!isnull(L.get_AI_stance()))
|
||||
if(L in holder.selected_mobs)
|
||||
// Todo: Select graphic only the admin can see?
|
||||
holder.selected_mobs -= L
|
||||
to_chat(user, span("notice", "Deselected \the [L]."))
|
||||
else
|
||||
holder.selected_mobs += L
|
||||
to_chat(user, span("notice", "Selected \the [L]."))
|
||||
else
|
||||
to_chat(user, span("warning", "\The [L] is not AI controlled."))
|
||||
|
||||
if(pa.Find("right"))
|
||||
if(istype(object, /atom/movable)) // Force attack.
|
||||
var/atom/movable/AM = object
|
||||
|
||||
if(pa.Find("alt"))
|
||||
for(var/thing in holder.selected_mobs)
|
||||
var/mob/living/unit = thing
|
||||
var/datum/ai_holder/AI = unit.ai_holder
|
||||
AI.give_target(AM)
|
||||
to_chat(user, span("notice", "Commanded [holder.selected_mobs.len] mob\s to attack \the [AM]."))
|
||||
|
||||
if(isliving(object)) // Follow or attack.
|
||||
var/mob/living/L = object
|
||||
|
||||
for(var/thing in holder.selected_mobs)
|
||||
var/mob/living/unit = thing
|
||||
var/datum/ai_holder/AI = unit.ai_holder
|
||||
if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift"))
|
||||
AI.set_follow(L)
|
||||
else
|
||||
AI.give_target(L)
|
||||
to_chat(user, span("notice", "Commanded [holder.selected_mobs.len] mob\s to attack or follow \the [L]."))
|
||||
|
||||
if(isturf(object)) // Move or reposition.
|
||||
var/turf/T = object
|
||||
|
||||
for(var/thing in holder.selected_mobs)
|
||||
var/mob/living/unit = thing
|
||||
var/datum/ai_holder/AI = unit.ai_holder
|
||||
AI.give_destination(T, pa.Find("shift"))
|
||||
to_chat(user, span("notice", "Commanded [holder.selected_mobs.len] mob\s to move to \the [T]."))
|
||||
|
||||
|
||||
/obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path)
|
||||
var/desired_path = input("Enter full or partial typepath.","Typepath","[default_path]")
|
||||
|
||||
@@ -194,4 +194,9 @@
|
||||
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.
|
||||
*/
|
||||
*/
|
||||
|
||||
/datum/ai_holder/simple_mob/hivebot
|
||||
conserve_ammo = TRUE
|
||||
firing_lanes = TRUE
|
||||
can_flee = FALSE // Fearless dumb machines.
|
||||
|
||||
@@ -327,4 +327,9 @@
|
||||
return null
|
||||
if(client && !ai_holder.autopilot)
|
||||
return null
|
||||
return ai_holder.stance
|
||||
return ai_holder.stance
|
||||
|
||||
// 'Taunts' the AI into attacking the taunter.
|
||||
/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE)
|
||||
if(ai_holder)
|
||||
ai_holder.receive_taunt(taunter, force_target_switch)
|
||||
@@ -20,7 +20,8 @@
|
||||
ai_log("engage_target() : Entering.", AI_LOG_DEBUG)
|
||||
|
||||
// Can we still see them?
|
||||
if(!target || !can_attack(target) || (!(target in list_targets())) )
|
||||
// if(!target || !can_attack(target) || (!(target in list_targets())) )
|
||||
if(!target || !can_attack(target))
|
||||
ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE)
|
||||
lose_target() // We lost them.
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@
|
||||
return FALSE // Turrets won't get hurt if they're still in their cover.
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
return TRUE
|
||||
// return FALSE
|
||||
|
||||
// Override this for special targeting criteria.
|
||||
// If it returns true, the mob will always select it as the target.
|
||||
@@ -210,9 +211,11 @@
|
||||
// Causes targeting to prefer targeting the taunter if possible.
|
||||
// This generally occurs if more than one option is within striking distance, including the taunter.
|
||||
// Otherwise the default filter will prefer the closest target.
|
||||
/datum/ai_holder/proc/receive_taunt(atom/movable/taunter)
|
||||
/datum/ai_holder/proc/receive_taunt(atom/movable/taunter, force_target_switch = FALSE)
|
||||
ai_log("receive_taunt() : Was taunted by [taunter].", AI_LOG_INFO)
|
||||
preferred_target = taunter
|
||||
if(force_target_switch)
|
||||
give_target(taunter)
|
||||
|
||||
/datum/ai_holder/proc/lose_taunt()
|
||||
ai_log("lose_taunt() : Resetting preferred_target.", AI_LOG_INFO)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/ICheckSpecialAttack(atom/A)
|
||||
return can_special_attack(A)
|
||||
return can_special_attack(A) && should_special_attack(A) // Just because we can doesn't mean we should.
|
||||
|
||||
/mob/living/proc/ISay(message)
|
||||
|
||||
|
||||
@@ -80,4 +80,17 @@
|
||||
emote_see = list("clacks")
|
||||
|
||||
/datum/say_list/spider
|
||||
emote_hear = list("chitters")
|
||||
emote_hear = list("chitters")
|
||||
|
||||
/datum/say_list/hivebot
|
||||
speak = list(
|
||||
"Resuming task: Protect area.",
|
||||
"No threats found.",
|
||||
"Error: No targets found."
|
||||
)
|
||||
emote_hear = list("hums ominously", "whirrs softly", "grinds a gear")
|
||||
emote_see = list("looks around the area", "turns from side to side")
|
||||
say_understood = list("Affirmative.", "Positive.")
|
||||
say_cannot = list("Denied.", "Negative.")
|
||||
say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.")
|
||||
say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.")
|
||||
@@ -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()
|
||||
expire() // Source got deleted or something.
|
||||
|
||||
@@ -180,3 +180,22 @@ 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.adjustFireLoss(damage_per_tick)
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -540,6 +561,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(!..())
|
||||
|
||||
@@ -499,6 +499,28 @@ 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, 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)
|
||||
|
||||
/mob/proc/get_contents()
|
||||
|
||||
|
||||
@@ -1015,7 +1037,7 @@ default behaviour is:
|
||||
if(lying != lying_prev)
|
||||
lying_prev = lying
|
||||
update_transform()
|
||||
|
||||
|
||||
return canmove
|
||||
|
||||
// Adds overlays for specific modifiers.
|
||||
|
||||
@@ -426,6 +426,12 @@
|
||||
/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.
|
||||
|
||||
//Finds the effective temperature that the mob is burning at.
|
||||
/mob/living/proc/fire_burn_temperature()
|
||||
if (fire_stacks <= 0)
|
||||
|
||||
@@ -237,3 +237,5 @@
|
||||
/obj/item/projectile/bullet/hivebot
|
||||
damage = 10
|
||||
damage_type = BRUTE
|
||||
|
||||
#undef LASERS_TO_KILL
|
||||
@@ -1547,13 +1547,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
|
||||
|
||||
|
||||
@@ -128,12 +128,17 @@
|
||||
|
||||
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)
|
||||
last_special_attack = world.time
|
||||
if(do_special_attack(A))
|
||||
if(special_attack_charges)
|
||||
special_attack_charges -= 1
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -113,6 +113,10 @@
|
||||
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()
|
||||
@@ -124,6 +128,8 @@
|
||||
/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)
|
||||
@@ -138,6 +144,8 @@
|
||||
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)
|
||||
@@ -174,6 +182,9 @@
|
||||
adjustFireLoss(min(7, getMaxHealth()*0.0625))
|
||||
// adjustFireLoss(rand(1, 6))
|
||||
|
||||
// Water
|
||||
/mob/living/simple_mob/get_water_protection()
|
||||
return water_resist
|
||||
|
||||
// Armor
|
||||
/mob/living/simple_mob/getarmor(def_zone, attack_flag)
|
||||
|
||||
@@ -122,6 +122,12 @@
|
||||
"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/purge = 0 // Cult stuff.
|
||||
var/supernatural = FALSE // Ditto.
|
||||
|
||||
@@ -165,9 +171,10 @@
|
||||
..(act, type, desc)
|
||||
|
||||
|
||||
/mob/living/simple_mob/SelfMove()
|
||||
/mob/living/simple_mob/SelfMove(turf/n, direct)
|
||||
var/turf/old_turf = get_turf(src)
|
||||
. = ..()
|
||||
if(movement_sound)
|
||||
if(movement_sound && old_turf != get_turf(src))
|
||||
playsound(src, movement_sound, 50, 1)
|
||||
|
||||
/mob/living/simple_mob/movement_delay()
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
health = 120
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
melee_damage_upper = 10
|
||||
|
||||
poison_chance = 15
|
||||
poison_per_bite = 3
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
// 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 while on natural terrain.<br>\
|
||||
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."
|
||||
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
|
||||
@@ -43,6 +43,21 @@
|
||||
/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)
|
||||
@@ -57,14 +72,17 @@
|
||||
|
||||
// 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
|
||||
@@ -75,11 +93,13 @@
|
||||
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.
|
||||
@@ -91,9 +111,11 @@
|
||||
|
||||
if(handle_tunnel(destination) == FALSE)
|
||||
set_AI_busy(FALSE)
|
||||
emerge()
|
||||
return FALSE
|
||||
|
||||
set_AI_busy(FALSE)
|
||||
emerge()
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -115,94 +137,49 @@
|
||||
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.
|
||||
new /obj/item/weapon/ore/glass(T)
|
||||
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))
|
||||
|
||||
/*
|
||||
// The actual leaping attack.
|
||||
/mob/living/simple_mob/animal/giant_spider/hunter/do_special_attack(atom/A)
|
||||
set waitfor = FALSE
|
||||
set_AI_busy(TRUE)
|
||||
/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.
|
||||
|
||||
// Telegraph, since getting stunned suddenly feels bad.
|
||||
do_windup_animation(A, leap_warmup)
|
||||
sleep(leap_warmup) // For the telegraphing.
|
||||
/obj/effect/temporary_effect/tunneler_hole
|
||||
name = "hole"
|
||||
desc = "A collapsing tunnel hole."
|
||||
icon_state = "tunnel_hole"
|
||||
time_to_die = 1 MINUTE
|
||||
|
||||
// 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, 'sound/weapons/spiderlunge.ogg', 75, 1)
|
||||
/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
|
||||
|
||||
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)
|
||||
*/
|
||||
|
||||
/*
|
||||
/mob/living/simple_animal/hostile/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
|
||||
move_to_delay = 4
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
|
||||
poison_chance = 15
|
||||
poison_per_bite = 3
|
||||
poison_type = "serotrotium_v"
|
||||
|
||||
/mob/living/simple_animal/hostile/giant_spider/tunneler/death()
|
||||
spawn(1)
|
||||
for(var/I = 1 to rand(3,6))
|
||||
if(src)
|
||||
new/obj/item/weapon/ore/glass(src.loc)
|
||||
else
|
||||
break
|
||||
return ..()
|
||||
*/
|
||||
incoming_damage_percent = 2
|
||||
evasion = -100
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot
|
||||
name = "hivebot"
|
||||
desc = "A robot. It appears to be somewhat resilient, but lacks a true weapon."
|
||||
icon = 'icons/mob/hivebot.dmi'
|
||||
icon_state = "basic"
|
||||
icon_living = "basic"
|
||||
icon_dead = "basic"
|
||||
|
||||
faction = "hivebot"
|
||||
|
||||
maxHealth = 3 LASERS_TO_KILL
|
||||
health = 3 LASERS_TO_KILL
|
||||
water_resist = 0.5
|
||||
movement_sound = 'sound/effects/servostep.ogg'
|
||||
|
||||
attacktext = list("clawed")
|
||||
projectilesound = 'sound/weapons/Gunshot.ogg'
|
||||
projectiletype = /obj/item/projectile/bullet/hivebot
|
||||
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/hivebot
|
||||
say_list_type = /datum/say_list/hivebot
|
||||
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/death()
|
||||
..()
|
||||
visible_message(span("warning","\The [src] blows apart!"))
|
||||
new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc)
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
qdel(src)
|
||||
|
||||
// The hivebot's default projectile.
|
||||
/obj/item/projectile/bullet/hivebot
|
||||
damage = 10
|
||||
damage_type = BRUTE
|
||||
sharp = FALSE
|
||||
edge = FALSE
|
||||
@@ -0,0 +1,106 @@
|
||||
// These hivebots are intended for general damage causing, at range.
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage
|
||||
maxHealth = 2 LASERS_TO_KILL // 60 health
|
||||
health = 2 LASERS_TO_KILL
|
||||
|
||||
|
||||
// The regular ranged hivebot, that fires somewhat weak projectiles.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic
|
||||
name = "ranged hivebot"
|
||||
desc = "A robot with a makeshift integrated ballistic weapon."
|
||||
|
||||
|
||||
// This one shoots quickly, and is considerably more dangerous.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid
|
||||
name = "rapid hivebot"
|
||||
desc = "A robot with a crude but deadly integrated rifle."
|
||||
base_attack_cooldown = 5 // Two attacks a second or so.
|
||||
player_msg = "You have a <b>rapid fire attack</b>."
|
||||
|
||||
|
||||
// Shoots deadly lasers.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser
|
||||
name = "laser hivebot"
|
||||
desc = "A robot with a photonic weapon integrated into itself."
|
||||
projectiletype = /obj/item/projectile/beam/blue
|
||||
projectilesound = 'sound/weapons/Laser.ogg'
|
||||
player_msg = "You have a <b>laser attack</b>."
|
||||
|
||||
|
||||
// Shoots EMPs, to screw over other robots.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion
|
||||
name = "ionic hivebot"
|
||||
desc = "A robot with an electromagnetic pulse projector."
|
||||
projectiletype = /obj/item/projectile/ion
|
||||
projectilesound = 'sound/weapons/Laser.ogg'
|
||||
player_msg = "You have a <b>ranged ion attack</b>, which is very strong against other synthetics.<br>\
|
||||
Be careful to not hit yourself or your team, as it will affect you as well."
|
||||
|
||||
// Beefy and ranged.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong
|
||||
name = "strong hivebot"
|
||||
desc = "A robot with a crude ballistic weapon and strong armor."
|
||||
maxHealth = 4 LASERS_TO_KILL // 120 health.
|
||||
health = 4 LASERS_TO_KILL
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 15
|
||||
|
||||
// Also beefy, but tries to stay at their 'home', ideal for base defense.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard
|
||||
name = "guard hivebot"
|
||||
desc = "A robot that seems to be guarding something."
|
||||
// ai_holder_type = todo
|
||||
|
||||
|
||||
// Inflicts a damage-over-time modifier on things it hits.
|
||||
// It is able to stack with repeated attacks.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot
|
||||
name = "ember hivebot"
|
||||
desc = "A robot that appears to utilize fire to cook their enemies."
|
||||
projectiletype = /obj/item/projectile/fire
|
||||
heat_resist = 1
|
||||
player_msg = "Your attacks inflict a <b>damage over time</b> effect, that will \
|
||||
harm your target slowly. The effect stacks with further attacks.<br>\
|
||||
You are also immune to fire."
|
||||
|
||||
/obj/item/projectile/fire
|
||||
name = "ember"
|
||||
icon_state = "fireball"
|
||||
modifier_type_to_apply = /datum/modifier/fire
|
||||
modifier_duration = 6 SECONDS // About 15 damage per stack, as Life() ticks every two seconds.
|
||||
damage = 0
|
||||
nodamage = TRUE
|
||||
|
||||
|
||||
// Very long ranged hivebot that rains down hell.
|
||||
// Their projectiles arc, meaning they go over everything until it hits the ground.
|
||||
// This means they're somewhat easier to avoid, but go over most defenses (like allies, or barriers),
|
||||
// and tend to do more harm than a regular projectile, due to being AoE.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege
|
||||
name = "siege engine hivebot"
|
||||
desc = "A large robot capable of delivering long range bombardment."
|
||||
projectiletype = /obj/item/projectile/arc/test
|
||||
icon_scale = 2
|
||||
player_msg = "You are capable of firing <b>very long range bombardment attacks</b>.<br>\
|
||||
To use, click on a tile or enemy at a long range. Note that the projectile arcs in the air, \
|
||||
so it will fly over everything inbetween you and the target.<br>\
|
||||
The bombardment is most effective when attacking a static structure, as it cannot avoid your fire."
|
||||
|
||||
// Fires EMP blasts.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp
|
||||
name = "ionic artillery hivebot"
|
||||
desc = "A large robot capable of annihilating electronics from a long distance."
|
||||
projectiletype = /obj/item/projectile/arc/emp_blast
|
||||
|
||||
// Fires shots that irradiate the tile hit.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation
|
||||
name = "desolator hivebot"
|
||||
desc = "A large robot capable of irradiating a large area from afar."
|
||||
projectiletype = /obj/item/projectile/arc/radioactive
|
||||
|
||||
// Essentially a long ranged frag grenade.
|
||||
/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation
|
||||
name = "anti-personnel artillery hivebot"
|
||||
desc = "A large robot capable of delivering fragmentation shells to rip apart their fleshy enemies."
|
||||
projectiletype = /obj/item/projectile/arc/fragmentation
|
||||
@@ -0,0 +1,87 @@
|
||||
// These hivebots help their team in various ways, and can be very powerful with allies, but are otherwise very weak when alone.
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/support
|
||||
attacktext = list("prodded")
|
||||
movement_cooldown = 5
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 2
|
||||
|
||||
|
||||
// This hivebot supplies a general buff to nearby hivebots that improve their performance.
|
||||
// Note that the commander itself does not receive the buff.
|
||||
/mob/living/simple_mob/mechanical/hivebot/support/commander
|
||||
name = "commander hivebot"
|
||||
desc = "A robot that appears to be directing the others."
|
||||
maxHealth = 5 LASERS_TO_KILL // 150 health
|
||||
health = 5 LASERS_TO_KILL
|
||||
player_msg = "You <b>increase the performance of other hivebots near you</b> passively.<br>\
|
||||
You are otherwise very weak offensively."
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/support/commander/handle_special()
|
||||
world << "Wew"
|
||||
for(var/mob/living/L in range(4))
|
||||
if(L == src)
|
||||
continue // Don't buff ourselves.
|
||||
if(L.faction == src.faction && L.isSynthetic()) // Don't buff enemies.
|
||||
world << "Adding modifier."
|
||||
L.add_modifier(/datum/modifier/aura/hivebot_commander_buff, null, src)
|
||||
|
||||
// Modifier added to friendly hivebots nearby.
|
||||
// Boosts most stats by 30%.
|
||||
// The boost is lost if the commander is too far away or dies.
|
||||
/datum/modifier/aura/hivebot_commander_buff
|
||||
name = "Strategicals"
|
||||
on_created_text = "<span class='notice'>Signal established with commander. Optimizating combat performance...</span>"
|
||||
on_expired_text = "<span class='warning'>Lost signal to commander. Optimization halting.</span>"
|
||||
stacks = MODIFIER_STACK_FORBID
|
||||
aura_max_distance = 4
|
||||
mob_overlay_state = "signal_blue"
|
||||
|
||||
disable_duration_percent = 0.7
|
||||
outgoing_melee_damage_percent = 1.3
|
||||
attack_speed_percent = 1.3
|
||||
accuracy = 30
|
||||
slowdown = -1
|
||||
evasion = 30
|
||||
|
||||
// Variant that automatically commands nearby allies to follow it when created.
|
||||
// Useful to avoid having to manually set follow to a lot of hivebots that are gonna die in the next minute anyways.
|
||||
/mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow/initialize()
|
||||
for(var/mob/living/L in hearers(7, src))
|
||||
if(!L.ai_holder)
|
||||
continue
|
||||
if(L.faction != src.faction)
|
||||
continue
|
||||
var/datum/ai_holder/AI = L.ai_holder
|
||||
AI.set_follow(src)
|
||||
return ..()
|
||||
|
||||
|
||||
// This hivebot adds charges to nearby allied hivebots that use the charge system for their special attacks.
|
||||
// A charge is given to a nearby ally every so often.
|
||||
// Charges cannot exceed the initial starting amount.
|
||||
/mob/living/simple_mob/mechanical/hivebot/support/logistics
|
||||
name = "logistics hivebot"
|
||||
desc = "A robot that resupplies their allies."
|
||||
maxHealth = 3 LASERS_TO_KILL // 90 health
|
||||
health = 3 LASERS_TO_KILL
|
||||
player_msg = "You <b>passively restore 'charges' to allies with special abilities</b> who are \
|
||||
limited to using them a specific number of times."
|
||||
var/resupply_range = 5
|
||||
var/resupply_cooldown = 4 SECONDS
|
||||
var/last_resupply = null
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/support/logistics/handle_special()
|
||||
if(last_resupply + resupply_cooldown > world.time)
|
||||
return // On cooldown.
|
||||
|
||||
for(var/mob/living/simple_mob/SM in hearers(resupply_range, src))
|
||||
if(SM == src)
|
||||
continue // We don't use charges buuuuut in case that changes in the future...
|
||||
if(SM.faction == src.faction) // Don't resupply enemies.
|
||||
if(!isnull(SM.special_attack_charges) && SM.special_attack_charges < initial(SM.special_attack_charges))
|
||||
SM.special_attack_charges += 1
|
||||
to_chat(SM, span("notice", "\The [src] has resupplied you, and you can use your special ability one additional time."))
|
||||
to_chat(src, span("notice", "You have resupplied \the [SM]."))
|
||||
last_resupply = world.time
|
||||
break // Only one resupply per pulse.
|
||||
@@ -0,0 +1,166 @@
|
||||
// These hivebots are harder to kill than normal, and are meant to protect their squad by
|
||||
// distracting their enemies. This is done by being seen as very threatening.
|
||||
// Their melee attacks weaken whatever they hit.
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank
|
||||
attacktext = list("prodded")
|
||||
projectiletype = null // To force the AI to melee.
|
||||
movement_cooldown = 10
|
||||
melee_damage_lower = 3
|
||||
melee_damage_upper = 3
|
||||
attack_sound = 'sound/weapons/Egloves.ogg'
|
||||
|
||||
// All tank hivebots apply a modifier to their target, and force them to attack them if they're AI controlled.
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/apply_melee_effects(atom/A)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
L.taunt(src, TRUE)
|
||||
L.add_modifier(/datum/modifier/hivebot_weaken, 3 SECONDS)
|
||||
|
||||
// Modifier applied to whatever a tank hivebot hits, intended to make the target do even less damage.
|
||||
/datum/modifier/hivebot_weaken
|
||||
name = "Shocked"
|
||||
desc = "You feel less able to exert yourself after being prodded."
|
||||
on_created_text = "<span class='warning'>You feel weak...</span>"
|
||||
on_expired_text = "<span class='notice'>You feel better.</span>"
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
mob_overlay_state = "electricity"
|
||||
|
||||
attack_speed_percent = 0.6
|
||||
outgoing_melee_damage_percent = 0.7
|
||||
accuracy = -40
|
||||
accuracy_dispersion = 1
|
||||
slowdown = 1
|
||||
evasion = -20
|
||||
|
||||
// This one is tanky by having a massive amount of health.
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/meatshield
|
||||
name = "bulky hivebot"
|
||||
desc = "A large robot."
|
||||
maxHealth = 10 LASERS_TO_KILL // 300 health
|
||||
health = 10 LASERS_TO_KILL
|
||||
icon_scale = 2
|
||||
player_msg = "You have a very large amount of health."
|
||||
|
||||
|
||||
// This one is tanky by having armor.
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/armored
|
||||
name = "armored hivebot"
|
||||
desc = "A robot clad in heavy armor."
|
||||
maxHealth = 5 LASERS_TO_KILL // 150 health.
|
||||
health = 5 LASERS_TO_KILL
|
||||
icon_scale = 1.5
|
||||
player_msg = "You are heavily armored."
|
||||
// Note that armor effectively makes lasers do about 9 damage instead of 30,
|
||||
// so it has an effective health of ~16.6 LASERS_TO_KILL if regular lasers are used.
|
||||
// Xrays will do much better against this.
|
||||
armor = list(
|
||||
"melee" = 40,
|
||||
"bullet" = 40,
|
||||
"laser" = 40,
|
||||
"energy" = 30,
|
||||
"bomb" = 30,
|
||||
"bio" = 100,
|
||||
"rad" = 100
|
||||
)
|
||||
armor_soak = list(
|
||||
"melee" = 15,
|
||||
"bullet" = 10,
|
||||
"laser" = 15,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 0,
|
||||
"rad" = 0
|
||||
)
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee
|
||||
name = "riot hivebot"
|
||||
desc = "A robot specialized in close quarters combat."
|
||||
player_msg = "You are heavily armored against close quarters combat."
|
||||
armor = list(
|
||||
"melee" = 70,
|
||||
"bullet" = 0,
|
||||
"laser" = 0,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 100,
|
||||
"rad" = 100
|
||||
)
|
||||
armor_soak = list(
|
||||
"melee" = 20,
|
||||
"bullet" = 0,
|
||||
"laser" = 0,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 0,
|
||||
"rad" = 0
|
||||
)
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet
|
||||
name = "bulletproof hivebot"
|
||||
desc = "A robot specialized in ballistic defense."
|
||||
player_msg = "You are heavily armored against ballistic weapons."
|
||||
armor = list(
|
||||
"melee" = 0,
|
||||
"bullet" = 70,
|
||||
"laser" = 0,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 100,
|
||||
"rad" = 100
|
||||
)
|
||||
armor_soak = list(
|
||||
"melee" = 0,
|
||||
"bullet" = 20,
|
||||
"laser" = 0,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 0,
|
||||
"rad" = 0
|
||||
)
|
||||
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser
|
||||
name = "ablative hivebot"
|
||||
desc = "A robot specialized in photonic defense."
|
||||
player_msg = "You are heavily armored against laser weapons."
|
||||
armor = list(
|
||||
"melee" = 0,
|
||||
"bullet" = 0,
|
||||
"laser" = 70,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 100,
|
||||
"rad" = 100
|
||||
)
|
||||
armor_soak = list(
|
||||
"melee" = 0,
|
||||
"bullet" = 0,
|
||||
"laser" = 20,
|
||||
"energy" = 0,
|
||||
"bomb" = 0,
|
||||
"bio" = 0,
|
||||
"rad" = 0
|
||||
)
|
||||
var/reflect_chance = 40 // Same as regular ablative.
|
||||
|
||||
// Ablative Hivebots can reflect lasers just like humans.
|
||||
/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser/bullet_act(obj/item/projectile/P)
|
||||
if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
|
||||
var/reflect_prob = reflect_chance - round(P.damage/3)
|
||||
if(prob(reflect_prob))
|
||||
visible_message(span("danger", "The [P.name] gets reflected by [src]'s armor!"), \
|
||||
span("userdanger", "The [P.name] gets reflected by [src]'s armor!"))
|
||||
|
||||
// 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))
|
||||
@@ -1,3 +1,4 @@
|
||||
// Mechanical mobs don't care about the atmosphere and cannot be hurt by tasers.
|
||||
/mob/living/simple_mob/mechanical
|
||||
mob_class = MOB_CLASS_SYNTHETIC
|
||||
min_oxy = 0
|
||||
|
||||
@@ -59,6 +59,8 @@
|
||||
var/drowsy = 0
|
||||
var/agony = 0
|
||||
var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections.
|
||||
var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile.
|
||||
var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent.
|
||||
|
||||
embed_chance = 0 //Base chance for a projectile to embed
|
||||
|
||||
@@ -86,6 +88,8 @@
|
||||
// if(isanimal(target)) return 0
|
||||
var/mob/living/L = target
|
||||
L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY!
|
||||
if(modifier_type_to_apply)
|
||||
L.add_modifier(modifier_type_to_apply, modifier_duration)
|
||||
return 1
|
||||
|
||||
//called when the projectile stops flying because it collided with something
|
||||
@@ -323,6 +327,7 @@
|
||||
|
||||
before_move()
|
||||
Move(location.return_turf())
|
||||
after_move()
|
||||
|
||||
if(!bumped && !isturf(original))
|
||||
if(loc == get_turf(original))
|
||||
@@ -348,6 +353,9 @@
|
||||
/obj/item/projectile/proc/before_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/after_move()
|
||||
return
|
||||
|
||||
/obj/item/projectile/proc/setup_trajectory(turf/startloc, turf/targloc, var/x_offset = 0, var/y_offset = 0)
|
||||
// setup projectile state
|
||||
starting = startloc
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// These projectiles are somewhat different from the other projectiles in the code.
|
||||
// First, these have an 'arcing' visual, that is accomplished by having the projectile icon rotate as its flying, and
|
||||
// moving up, then down as it approaches the target. There is also a small shadow effect that follows the projectile
|
||||
// as its flying.
|
||||
|
||||
// Besides the visuals, arcing projectiles do not collide with anything until they reach the target, as they fly over them.
|
||||
// For best effect, use this only when it makes sense to do so, IE on the Surface. The projectiles don't care about ceilings or gravity.
|
||||
|
||||
/obj/item/projectile/arc
|
||||
name = "arcing shot"
|
||||
icon_state = "fireball" // WIP
|
||||
step_delay = 2 // Travel a bit slower, to really sell the arc visuals.
|
||||
plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane.
|
||||
var/target_distance = null // How many tiles the impact site is.
|
||||
var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right.
|
||||
var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality.
|
||||
|
||||
/obj/item/projectile/arc/initialize()
|
||||
shadow = new(get_turf(src))
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/Destroy()
|
||||
qdel_null(shadow)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/Bump(atom/A, forced=0)
|
||||
return 0
|
||||
// if(get_turf(src) != original)
|
||||
// return 0
|
||||
// else
|
||||
// return ..()
|
||||
|
||||
// This is a test projectile in the sense that its testing the code to make sure it works,
|
||||
// as opposed to a 'can I hit this thing' projectile.
|
||||
/obj/item/projectile/arc/test/on_impact(turf/T)
|
||||
new /obj/effect/explosion(T)
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
kill_count = expected_distance // So the projectile "hits the ground."
|
||||
target_distance = expected_distance
|
||||
fired_dir = get_dir(loc, target)
|
||||
..() // Does the regular launching stuff.
|
||||
if(fired_dir & EAST)
|
||||
transform = turn(transform, -45)
|
||||
else if(fired_dir & WEST)
|
||||
transform = turn(transform, 45)
|
||||
|
||||
|
||||
// Visuals.
|
||||
/obj/item/projectile/arc/after_move()
|
||||
// Handle projectile turning in flight.
|
||||
// This won't turn if fired north/south, as it looks weird.
|
||||
var/turn_per_step = 90 / target_distance
|
||||
if(fired_dir & EAST)
|
||||
transform = turn(transform, turn_per_step)
|
||||
else if(fired_dir & WEST)
|
||||
transform = turn(transform, -turn_per_step)
|
||||
|
||||
// Now for the fake height.
|
||||
// We need to know how far along our "arc" we are.
|
||||
var/arc_progress = get_dist(src, original)
|
||||
var/arc_max_height = (target_distance * world.icon_size) / 2 // TODO: Real math.
|
||||
// var/arc_center = target_distance / 2
|
||||
// var/projectile_position = abs(arc_progress - arc_center)
|
||||
// var/height_multiplier = projectile_position / arc_center
|
||||
// height_multiplier = abs(height_multiplier - 1)
|
||||
// height_multiplier = height_multiplier ** 2
|
||||
|
||||
|
||||
// animate(src, pixel_z = arc_max_height * height_multiplier, time = step_delay)
|
||||
var/projectile_position = arc_progress / target_distance
|
||||
var/sine_position = projectile_position * 180
|
||||
var/pixel_z_position = arc_max_height * sin(sine_position)
|
||||
animate(src, pixel_z = pixel_z_position, time = step_delay)
|
||||
|
||||
// Update our shadow.
|
||||
shadow.forceMove(loc)
|
||||
|
||||
|
||||
|
||||
/obj/effect/projectile_shadow
|
||||
name = "shadow"
|
||||
desc = "You better avoid the thing coming down!"
|
||||
icon = 'icons/obj/projectiles.dmi'
|
||||
icon_state = "arc_shadow"
|
||||
anchored = TRUE
|
||||
|
||||
|
||||
// Subtypes
|
||||
|
||||
/obj/item/projectile/arc/emp_blast
|
||||
name = "emp blast"
|
||||
icon_state = "bluespace"
|
||||
|
||||
/obj/item/projectile/arc/emp_blast/on_impact(turf/T)
|
||||
empulse(T, 2, 4, 7, 10) // Normal EMP grenade.
|
||||
return ..()
|
||||
|
||||
/obj/item/projectile/arc/emp_blast/weak/on_impact(turf/T)
|
||||
empulse(T, 1, 2, 3, 4) // Sec EMP grenade.
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/projectile/arc/radioactive
|
||||
name = "radiation blast"
|
||||
icon_state = "green_pellet"
|
||||
icon_scale = 2
|
||||
var/rad_power = 50
|
||||
|
||||
/obj/item/projectile/arc/radioactive/on_impact(turf/T)
|
||||
radiation_repository.radiate(T, rad_power)
|
||||
|
||||
|
||||
/obj/item/projectile/arc/fragmentation
|
||||
name = "fragmentation shot"
|
||||
icon_state = "shell"
|
||||
var/list/fragment_types = list(
|
||||
/obj/item/projectile/bullet/pellet/fragment, /obj/item/projectile/bullet/pellet/fragment, \
|
||||
/obj/item/projectile/bullet/pellet/fragment, /obj/item/projectile/bullet/pellet/fragment/strong
|
||||
)
|
||||
var/fragment_amount = 63 // Same as a grenade.
|
||||
var/spread_range = 7
|
||||
|
||||
/obj/item/projectile/arc/fragmentation/on_impact(turf/T)
|
||||
fragmentate(T, fragment_amount, spread_range, fragment_types)
|
||||
Reference in New Issue
Block a user