From 0cf536b7de82f3b8a6fc44a42022c3228d0ddf92 Mon Sep 17 00:00:00 2001 From: Neerti Date: Wed, 28 Nov 2018 03:44:13 -0500 Subject: [PATCH] Addresses Ater's things. --- code/game/objects/structures/girders.dm | 2 +- code/game/turfs/simulated/wall_attacks.dm | 2 +- .../ai/aI_holder_subtypes/simple_mob_ai.dm | 247 +----------------- code/modules/ai/ai_holder_combat.dm | 3 +- code/modules/ai/ai_holder_communication.dm | 1 - code/modules/ai/ai_holder_debug.dm | 25 +- .../mob/living/carbon/human/human_helpers.dm | 7 +- code/modules/mob/living/simple_mob/defense.dm | 2 +- .../subtypes/animal/giant_spider/electric.dm | 12 +- .../subtypes/animal/giant_spider/hunter.dm | 65 +++++ .../subtypes/animal/giant_spider/nurse.dm | 38 ++- .../simple_mob/subtypes/animal/pets/parrot.dm | 15 +- .../subtypes/animal/sif/hooligan_crab.dm | 19 +- .../subtypes/mechanical/hivebot/hivebot.dm | 8 +- .../subtypes/mechanical/hivebot/support.dm | 4 +- .../mechanical/mecha/adv_dark_gygax.dm | 81 ++++++ .../subtypes/mechanical/mecha/durand.dm | 2 +- .../subtypes/mechanical/ward/monitor_ward.dm | 40 +-- .../subtypes/mechanical/ward/ward.dm | 5 + .../simple_mob/subtypes/slime/feral/feral.dm | 2 +- html/changelog.html | 1 - html/changelogs/Mechoid - Mecha.yml | 7 - html/changelogs/Mechoid - MechaWeapons.yml | 7 - html/changelogs/Mechoid - Vehicles.yml | 36 --- html/changelogs/Woodrat - Spacesuit.yml | 38 --- html/changelogs/changelog-instruments.yml | 5 - 26 files changed, 282 insertions(+), 392 deletions(-) delete mode 100644 html/changelogs/Mechoid - Mecha.yml delete mode 100644 html/changelogs/Mechoid - MechaWeapons.yml delete mode 100644 html/changelogs/Mechoid - Vehicles.yml delete mode 100644 html/changelogs/Woodrat - Spacesuit.yml delete mode 100644 html/changelogs/changelog-instruments.yml diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index a98585c1de..4a858d5cce 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -84,7 +84,7 @@ cover = 25 /obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") - if(!damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return 0 user.do_attack_animation(src) visible_message("[user] [attack_message] the [src]!") diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 3a68afb5f7..bd6adfe32f 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -120,7 +120,7 @@ radiate() user.setClickCooldown(user.get_attack_speed()) var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if(!damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) try_touch(user, rotting) return diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm index ad362edb71..f2d1f05e13 100644 --- a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm @@ -1,4 +1,5 @@ -// AIs for simple mobs. +// Base AIs for simple mobs. +// Mob-specific AIs are in their mob's file. /datum/ai_holder/simple_mob hostile = TRUE // The majority of simplemobs are hostile. @@ -9,24 +10,12 @@ wander = TRUE base_wander_delay = 4 -// For animals. +// For non-hostile animals, and pets like Ian and Runtime. /datum/ai_holder/simple_mob/passive hostile = FALSE can_flee = TRUE violent_breakthrough = FALSE -// For parrots like Poly. -// They modify their say_list datum based on what their mob hears. -/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 - // Won't wander away, ideal for event-spawned mobs like carp or drones. /datum/ai_holder/simple_mob/event wander = FALSE @@ -45,21 +34,6 @@ /datum/ai_holder/simple_mob/inert/astar use_astar = TRUE -// Can't attack but calls for help. Used by the monitor and spotter wards. -// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. -/datum/ai_holder/simple_mob/monitor - hostile = TRUE // Required to call for help. - cooperative = TRUE - stand_ground = TRUE // So it doesn't run up to the thing it sees. - wander = FALSE - can_flee = FALSE - -/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) - return FALSE - -/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) - return FALSE - // Ranged mobs. /datum/ai_holder/simple_mob/ranged @@ -107,15 +81,10 @@ holder.IMove(get_step_towards(holder, A)) holder.face_atom(A) -// 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. +// Yakkity saxes while firing at you. +/datum/ai_holder/hostile/ranged/robust/on_engagement(atom/movable/AM) + step_rand(holder) + holder.face_atom(AM) // Switches intents based on specific criteria. // Used for special mobs who do different things based on intents (and aren't slimes). @@ -123,84 +92,6 @@ /datum/ai_holder/simple_mob/intentional -// The Advanced Dark Gygax's AI. -// The mob has three special attacks, based on the current intent. -// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax - conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. - var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. - - var/electric_defense_radius = 3 // How big to assume electric defense's area is. - var/microsingulo_radius = 3 // Same but for microsingulo pull. - var/rocket_explosive_radius = 2 // Explosion radius for the rockets. - - var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. - var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. - -// Used to control the mob's positioning based on which special attack it has done. -// Note that the intent will not change again until the next special attack is about to happen. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) - // Make the AI backpeddle if using an AoE special attack. - var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. - if(holder.a_intent in risky_intents) - var/closest_distance = 1 - switch(holder.a_intent) // Plus one just in case. - if(I_HURT) - closest_distance = rocket_explosive_radius + 1 - if(I_GRAB) - closest_distance = microsingulo_radius + 1 - - if(get_dist(holder, A) <= closest_distance) - holder.IMove(get_step_away(holder, A, closest_distance)) - - // Otherwise get up close and personal. - else if(get_dist(holder, A) > closest_desired_distance) - holder.IMove(get_step_towards(holder, A)) - -// Changes the mob's intent, which controls which special attack is used. -// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. -/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) - if(isliving(A)) - var/mob/living/target = A - - // If we're surrounded, Electric Defense will quickly fix that. - var/tally = 0 - var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. - for(var/atom/movable/AM in potential_targets) - if(get_dist(holder, AM) > electric_defense_radius) - continue - if(!can_attack(AM)) - continue - tally++ - - // Should we shock them? - if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) - holder.a_intent = I_DISARM - return - - // Otherwise they're a fair distance away and we're not getting mobbed up close. - // See if we should use missiles or microsingulo. - tally = 0 // Let's recycle the var. - for(var/atom/movable/AM in potential_targets) - if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. - continue - if(!can_attack(AM)) - continue - if(AM.anchored) // Microsingulo doesn't do anything to anchored things. - tally-- - else - tally++ - - // Lots of people means minisingulo would be more useful. - if(tally >= microsingulo_threshold) - holder.a_intent = I_GRAB - else // Otherwise use rockets. - holder.a_intent = I_HURT - - else - holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. - - // These try to avoid collateral damage. /datum/ai_holder/simple_mob/restrained violent_breakthrough = FALSE @@ -218,58 +109,7 @@ holder.IMove(get_step(holder, pick(alldirs))) holder.face_atom(A) -// 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("\The [holder] starts to follow \the [L].") - set_follow(L, rand(20 SECONDS, 40 SECONDS)) - - -// 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 // This AI hits something, then runs away for awhile. // It will (almost) always flee if they are uncloaked, AND their target is not stunned. @@ -279,84 +119,13 @@ // Used for the 'running' part of hit and run. /datum/ai_holder/simple_mob/melee/hit_and_run/special_flee_check() if(!holder.is_cloaked()) - if(target && isliving(target)) + if(isliving(target)) var/mob/living/L = target return !L.incapacitated(INCAPACITATION_DISABLED) // Don't flee if our target is stunned in some form, even if uncloaked. This is so the mob keeps attacking a stunned opponent. return TRUE // We're out in the open, uncloaked, and our target isn't stunned, so lets flee. return FALSE -// This AI isolates 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. -*/ - -/datum/ai_holder/simple_mob/hivebot - pointblank = TRUE - conserve_ammo = TRUE - firing_lanes = TRUE - can_flee = FALSE // Fearless dumb machines. - // Simple mobs that aren't hostile, but will fight back. /datum/ai_holder/simple_mob/retaliate hostile = FALSE diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index ae462d5b7f..4a8fbdff69 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -3,12 +3,11 @@ /datum/ai_holder var/firing_lanes = FALSE // If ture, tries to refrain from shooting allies or the wall. var/conserve_ammo = FALSE // If true, the mob will avoid shooting anything that does not have a chance to hit a mob. Requires firing_lanes to be true. - - var/shoot_range = 5 // How close the mob needs to be to attempt to shoot at the enemy, if the mob is capable of ranged attacks. var/pointblank = FALSE // If ranged is true, and this is true, people adjacent to the mob will suffer the ranged instead of using a melee attack. var/can_breakthrough = TRUE // If false, the AI will not try to open a path to its goal, like opening doors. var/violent_breakthrough = TRUE // If false, the AI is not allowed to destroy things like windows or other structures in the way. Requires above var to be true. + var/stand_ground = FALSE // If true, the AI won't try to get closer to an enemy if out of range. diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm index fa576b6a8f..93267d19f4 100644 --- a/code/modules/ai/ai_holder_communication.dm +++ b/code/modules/ai/ai_holder_communication.dm @@ -9,7 +9,6 @@ var/last_threaten_time = null // Ditto but only for threats. var/speak_chance = 0 // Probability that the mob talks (this is 'X in 200' chance since even 1/100 is pretty noisy) - var/reacts = 0 // Reacts to some things being said. /datum/ai_holder/proc/should_threaten() diff --git a/code/modules/ai/ai_holder_debug.dm b/code/modules/ai/ai_holder_debug.dm index 2c8f0bc1fa..beee998af8 100644 --- a/code/modules/ai/ai_holder_debug.dm +++ b/code/modules/ai/ai_holder_debug.dm @@ -14,14 +14,6 @@ var/debug_ai = AI_LOG_OFF // The level of debugging information to display to people who can see log_debug(). -/* -#define AI_LOG_OFF 0 // Don't show anything. -#define AI_LOG_ERROR 1 // Show logs of things likely causing the mob to not be functioning correctly. -#define AI_LOG_WARNING 2 // Show less serious but still helpful to know issues that might be causing things to work incorrectly. -#define AI_LOG_INFO 3 // Important regular events, like selecting a target or switching stances. -#define AI_LOG_DEBUG 4 // More detailed information about the flow of execution. -#define AI_LOG_TRACE 5 // Even more detailed than the last. -*/ /datum/ai_holder/New() ..() path_overlay = new(path_icon,path_icon_state) @@ -85,19 +77,7 @@ last_turf_display = TRUE debug_ai = AI_LOG_INFO -/datum/ai_holder/test - hostile = TRUE - use_astar = TRUE - -/datum/ai_holder/hostile/ranged -// ranged = TRUE - cooperative = TRUE - firing_lanes = TRUE - conserve_ammo = TRUE - threaten = TRUE - wander = TRUE - -/datum/ai_holder/hostile/ranged/debug +/datum/ai_holder/hostile/debug wander = FALSE conserve_ammo = FALSE intelligence_level = AI_SMART @@ -107,6 +87,3 @@ last_turf_display = TRUE debug_ai = AI_LOG_INFO -/datum/ai_holder/hostile/ranged/robust/on_engagement(atom/movable/AM) - step_rand(holder) - holder.face_atom(AM) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index cd80358b31..3c9d68f68a 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -43,15 +43,14 @@ cloaker.deactivate() /mob/living/carbon/human/is_cloaked() - if(mind && mind.changeling) // Ling camo. - return mind.changeling.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 - else - return ..() + return ..() /mob/living/carbon/human/get_ear_protection() var/sum = 0 diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm index d9b0d3be42..af1b2774b3 100644 --- a/code/modules/mob/living/simple_mob/defense.dm +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -69,7 +69,7 @@ var/datum/gender/T = gender_datums[src.get_visible_gender()] to_chat(user, "\The [src] is dead, medical items won't bring [T.him] back to life.") // 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) || istype(O, /obj/item/weapon/material/knife/butch)) + if(istype(O, /obj/item/weapon/material/knife)) harvest(user) return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm index 63e91a1e62..c3d1f60731 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm @@ -32,4 +32,14 @@ /obj/item/projectile/beam/stun/electric_spider name = "stun beam" - agony = 20 \ No newline at end of file + 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. \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm index f039ba6bad..f3eb1cf81e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm @@ -98,3 +98,68 @@ // visible_message("\The [src] seizes \the [victim] aggressively!") // 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. +*/ diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm index 5dd460e165..62934195dd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -191,4 +191,40 @@ 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" \ No newline at end of file + 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 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm index ecac2d5add..bedd95e3ce 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -11,6 +11,7 @@ // 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!", @@ -236,4 +237,16 @@ icon_state = "mtoo" icon_rest = "mtoo-held" icon_dead = "mtoo-dead" - tt_desc = "E Lophochroa leadbeateri" \ No newline at end of file + 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 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm index 9ef4f0de94..3b7f279653 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -82,4 +82,21 @@ 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]!")) \ No newline at end of file + 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("\The [holder] starts to follow \the [L].") + set_follow(L, rand(20 SECONDS, 40 SECONDS)) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm index cb25725b07..fe43c856f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm @@ -45,4 +45,10 @@ maxHealth = 1 LASERS_TO_KILL health = 1 LASERS_TO_KILL melee_damage_lower = 8 - melee_damage_upper = 8 \ No newline at end of file + melee_damage_upper = 8 + +/datum/ai_holder/simple_mob/hivebot + pointblank = TRUE + conserve_ammo = TRUE + firing_lanes = TRUE + can_flee = FALSE // Fearless dumb machines. \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm index ca5712b9e2..360686d591 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm @@ -23,7 +23,7 @@ for(var/mob/living/L in range(4, src)) if(L == src) continue // Don't buff ourselves. - if(L.faction == src.faction && L.isSynthetic()) // Don't buff enemies. + if(IIsAlly(L) && L.isSynthetic()) // Don't buff enemies. L.add_modifier(/datum/modifier/aura/hivebot_commander_buff, null, src) // Modifier added to friendly hivebots nearby. @@ -78,7 +78,7 @@ 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(IIsAlly(SM)) // 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.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index 6a5c5095ec..246b0fa1c6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -178,3 +178,84 @@ /obj/effect/temporary_effect/pulse/microsingulo/on_pulse() for(var/atom/A in range(pull_radius, src)) A.singularity_pull(src, pull_strength) + + +// The Advanced Dark Gygax's AI. +// The mob has three special attacks, based on the current intent. +// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax + conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. + var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. + + var/electric_defense_radius = 3 // How big to assume electric defense's area is. + var/microsingulo_radius = 3 // Same but for microsingulo pull. + var/rocket_explosive_radius = 2 // Explosion radius for the rockets. + + var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. + var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. + +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) + // Make the AI backpeddle if using an AoE special attack. + var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. + if(holder.a_intent in risky_intents) + var/closest_distance = 1 + switch(holder.a_intent) // Plus one just in case. + if(I_HURT) + closest_distance = rocket_explosive_radius + 1 + if(I_GRAB) + closest_distance = microsingulo_radius + 1 + + if(get_dist(holder, A) <= closest_distance) + holder.IMove(get_step_away(holder, A, closest_distance)) + + // Otherwise get up close and personal. + else if(get_dist(holder, A) > closest_desired_distance) + holder.IMove(get_step_towards(holder, A)) + +// Changes the mob's intent, which controls which special attack is used. +// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = I_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = I_GRAB + else // Otherwise use rockets. + holder.a_intent = I_HURT + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. + else + holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm index a29616a9bc..c20d623c53 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm @@ -42,7 +42,7 @@ // So players can toggle it too. /mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() set name = "Toggle Defense Mode" - set desc = "Toggles a special mode which makes you unable to move but become more resilient." + set desc = "Toggles a special mode which makes you immobile and much more resilient." set category = "Abilities" set_defense_mode(!defense_mode) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index d8349dffd3..153bc9c478 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -50,25 +50,17 @@ /mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() var/last_seen_mobs_len = seen_mobs.len - var/list/things_in_sight = view(view_range, src) + var/list/mobs_nearby = hearers(view_range, src) var/list/newly_seen_mobs = list() - for(var/mob/living/L in things_in_sight) + for(var/mob/living/L in mobs_nearby) if(L == src) // Don't detect ourselves. continue if(L.stat) // Dead mobs aren't concerning. continue - if(owner) - if(L == owner) // Don't yell at our owner for seeing them. - continue - - if(L.IIsAlly(owner)) // Don't yell if they're friendly to our owner. - continue - - else - if(src.IIsAlly(L)) - continue + if(src.IIsAlly(L)) + continue // Decloak them . if(L.is_cloaked()) @@ -84,15 +76,31 @@ if(!(L in seen_mobs)) seen_mobs += L newly_seen_mobs += L - if(owner) - to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) + + if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) // Now get rid of old mobs that left vision. for(var/thing in seen_mobs) - if(!(thing in things_in_sight)) + if(!(thing in mobs_nearby)) seen_mobs -= thing // Check if we need to update icon. if(seen_mobs.len != last_seen_mobs_len) update_icon() - last_seen_mobs_len = seen_mobs.len + + +// Can't attack but calls for help. Used by the monitor and spotter wards. +// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. +/datum/ai_holder/simple_mob/monitor + hostile = TRUE // Required to call for help. + cooperative = TRUE + stand_ground = TRUE // So it doesn't run up to the thing it sees. + wander = FALSE + can_flee = FALSE + +/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) + return FALSE + +/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) + return FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm index 2fdffae0b1..0e897599ac 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -34,4 +34,9 @@ /mob/living/simple_mob/mechanical/ward/Destroy() owner = null + return ..() + +/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) + if(owner == L) + return TRUE return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index 4b6fd4cfc3..8986cbcbe3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -53,7 +53,7 @@ player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
\ You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
\ - Unprotected enemies are also Chilled, making them slower, less evasive, and to suffer disabling effects for longer." + Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." /obj/item/projectile/icicle name = "icicle" diff --git a/html/changelog.html b/html/changelog.html index f553d99325..e926e5bfd2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -100,7 +100,6 @@

Mechoid updated:

Neerti updated: