diff --git a/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm b/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm index c55b4c31be4..01513367895 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/telecomns_returns.dmm @@ -1464,7 +1464,7 @@ icon_state = "4-8" }, /obj/machinery/recharge_station/upgraded, -/mob/living/basic/hivebot/strong/malfborg, +/mob/living/basic/malfborg/sec, /turf/simulated/floor/plasteel/dark{ name = "Mainframe floor"; nitrogen = 400; @@ -2578,7 +2578,7 @@ icon_state = "2-4" }, /obj/machinery/recharge_station/upgraded, -/mob/living/basic/hivebot/strong/malfborg, +/mob/living/basic/malfborg/sec, /turf/simulated/floor/plasteel/dark{ name = "Mainframe floor"; nitrogen = 400; diff --git a/code/__DEFINES/ai/hostile_mech_blackboard_defines.dm b/code/__DEFINES/ai/hostile_mech_blackboard_defines.dm new file mode 100644 index 00000000000..7e28c7cb871 --- /dev/null +++ b/code/__DEFINES/ai/hostile_mech_blackboard_defines.dm @@ -0,0 +1,3 @@ +#define BB_HOSTILE_MECH_SECONDARY_WEAPON "BB_HOSTILE_MECH_SECONDARY_WEAPON" +#define BB_HOSTILE_MECH_TERTIARY_WEAPON "BB_HOSTILE_MECH_TERTIARY_WEAPON" +#define BB_HOSTILE_MECH_WEAPON_COOLDOWN "BB_HOSTILE_MECH_WEAPON_COOLDOWN" diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm index d45d0383505..4a67eefb231 100644 --- a/code/__DEFINES/dcs/mob_signals.dm +++ b/code/__DEFINES/dcs/mob_signals.dm @@ -317,6 +317,9 @@ ///from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers) #define COMSIG_BASICMOB_POST_ATTACK_RANGED "basicmob_post_attack_ranged" +/// from the ranged_attacks component for basic mobs: (mob/living/basic/firer, atom/target, modifiers) +#define COMSIG_BASICMOB_POST_ATTACK_RANGED_BURST "basicmob_post_attack_ranged_burst" + /// From base of /datum/action/cooldown/proc/PreActivate(), sent to the action owner: (datum/action/cooldown/activated) #define COMSIG_MOB_ABILITY_STARTED "mob_ability_base_started" /// Return to block the ability from starting / activating diff --git a/code/datums/components/basic_mob_aggro_emote.dm b/code/datums/components/basic_mob_aggro_emote.dm index 30996ef0381..d499bf29598 100644 --- a/code/datums/components/basic_mob_aggro_emote.dm +++ b/code/datums/components/basic_mob_aggro_emote.dm @@ -18,6 +18,8 @@ var/aggro_volume /// List of possible things to say var/list/say_list + /// Does the sound vary + var/sound_vary /datum/component/aggro_emote/Initialize( target_key = BB_BASIC_MOB_CURRENT_TARGET, @@ -29,6 +31,7 @@ minimum_chance = 2, subtract_chance = 7, list/say_list, + sound_vary = TRUE ) . = ..() if(!isatom(parent)) @@ -44,6 +47,7 @@ src.minimum_chance = minimum_chance src.subtract_chance = subtract_chance src.say_list = say_list + src.sound_vary = sound_vary /datum/component/aggro_emote/RegisterWithParent() . = ..() @@ -68,6 +72,6 @@ if(emote_list) mob_parent.emote("me", EMOTE_VISIBLE, "[pick(emote_list)] at [new_target].") if(aggro_sound) - playsound(mob_parent.loc, aggro_sound, aggro_volume, TRUE) + playsound(mob_parent.loc, aggro_sound, aggro_volume, sound_vary) if(say_list) mob_parent.say(pick(say_list)) diff --git a/code/modules/awaymissions/mission_code/ruins/telecomns.dm b/code/modules/awaymissions/mission_code/ruins/telecomns.dm index df60f8b312a..628c4ea412b 100644 --- a/code/modules/awaymissions/mission_code/ruins/telecomns.dm +++ b/code/modules/awaymissions/mission_code/ruins/telecomns.dm @@ -340,51 +340,6 @@ GLOBAL_LIST_EMPTY(telecomms_trap_tank) aggressive = TRUE aggressive_tilt_chance = 100 //It will tip on you, and it will be funny. -/mob/living/basic/hivebot/strong/malfborg - name = "Security cyborg" - desc = "Oh god they still have access to these!" - icon = 'icons/mob/robots.dmi' - icon_state = "Noble-Security" - health = 200 - maxHealth = 200 - faction = list("malf_drone") - speed = 0.5 - projectile_type = /obj/projectile/beam/disabler/weak - projectile_sound = 'sound/weapons/taser2.ogg' - ranged_burst_count = 2 - gold_core_spawnable = NO_SPAWN // Could you imagine xenobio with this? lmao. - a_intent = INTENT_HARM - var/obj/item/melee/baton/infinite_cell/baton = null // stunbaton bot uses to melee attack - ai_controller = /datum/ai_controller/basic_controller/simple/simple_skirmisher - -/mob/living/basic/hivebot/strong/malfborg/Initialize(mapload) - . = ..() - baton = new(src) - -/mob/living/basic/hivebot/strong/malfborg/Destroy() - QDEL_NULL(baton) - return ..() - -/mob/living/basic/hivebot/strong/malfborg/melee_attack(atom/target, list/modifiers, ignore_cooldown) - if(!early_melee_attack(target, modifiers, ignore_cooldown)) - return FALSE - if(QDELETED(target)) - return FALSE - face_atom(target) - baton.melee_attack_chain(src, target) - SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, TRUE) - return TRUE - -/mob/living/basic/hivebot/strong/malfborg/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) - if(!used_item && !isturf(A)) - used_item = baton - ..() - -/mob/living/basic/hivebot/strong/malfborg/emp_act(severity) - . = ..() - ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) - adjustBruteLoss(50) - /obj/structure/displaycase/dvoraks_treat alert = TRUE // Ooopsies you opened this after doomsday and the doors bolted, oh nooooo force_alarm = TRUE diff --git a/code/modules/mob/living/basic/hostile/silicon/evil_cyborgs.dm b/code/modules/mob/living/basic/hostile/silicon/evil_cyborgs.dm new file mode 100644 index 00000000000..b702dd5a21d --- /dev/null +++ b/code/modules/mob/living/basic/hostile/silicon/evil_cyborgs.dm @@ -0,0 +1,160 @@ +/mob/living/basic/malfborg + name = "malfunctioning cyborg" + desc = "It doesn't appear to obey the laws of robotics." + icon = 'icons/mob/robots.dmi' + icon_state = "Noble" + health = 200 + maxHealth = 200 + faction = list("malf_drone") + mob_biotypes = MOB_ROBOTIC + speed = 0.5 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minimum_survivable_temperature = 0 + speak_emote = list("states") + a_intent = INTENT_HARM + harm_intent_damage = 0 + melee_damage_lower = 25 + melee_damage_upper = 35 + attack_verb_simple = "slash" + attack_verb_continuous = "slashes" + melee_attack_cooldown_min = 1.5 SECONDS + melee_attack_cooldown_min = 2.5 SECONDS + initial_traits = list(TRAIT_NOFIRE) + attack_sound = 'sound/weapons/blade1.ogg' + is_ranged = TRUE + projectile_type = /obj/projectile/beam/laser + projectile_sound = 'sound/weapons/laser.ogg' + death_sound = 'sound/voice/borg_deathsound.ogg' + ranged_burst_count = 2 + death_message = "blows apart!" + bubble_icon = "machine" + basic_mob_flags = DEL_ON_DEATH + ai_controller = /datum/ai_controller/basic_controller/malfborg + +/mob/living/basic/malfborg/Initialize(mapload) + . = ..() + AddComponent(/datum/component/aggro_emote, emote_list = list("beeps", "buzzes"), emote_chance = 50) + add_language("Galactic Common") + set_default_language(GLOB.all_languages["Galactic Common"]) + AddElement(/datum/element/ai_retaliate) + +/mob/living/basic/malfborg/emp_act(severity) + . = ..() + ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + adjustBruteLoss(50) + +/mob/living/basic/malfborg/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + do_sparks(3, 1, src) + new /obj/effect/gibspawner/robot(get_turf(src)) + +/mob/living/basic/malfborg/sec + name = "security cyborg" + desc = "Oh god they still have access to these!" + icon_state = "Noble-Security" + attack_verb_simple = "baton" + attack_verb_continuous = "batons" + projectile_type = /obj/projectile/beam/disabler/weak + projectile_sound = 'sound/weapons/taser2.ogg' + var/obj/item/melee/baton/infinite_cell/baton = null // stunbaton bot uses to melee attack + +/mob/living/basic/malfborg/sec/Initialize(mapload) + . = ..() + baton = new(src) + +/mob/living/basic/malfborg/sec/melee_attack(atom/target, list/modifiers, ignore_cooldown) + if(!early_melee_attack(target, modifiers, ignore_cooldown)) + return FALSE + if(QDELETED(target)) + return FALSE + face_atom(target) + baton.melee_attack_chain(src, target) + SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, TRUE) + return TRUE + +/mob/living/basic/malfborg/sec/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) + if(!used_item && !isturf(A)) + used_item = baton + ..() + +/mob/living/basic/malfborg/sec/Destroy() + QDEL_NULL(baton) + return ..() + +/mob/living/basic/malfborg/sec/engi // Engiborg has a batonga. Might as well inherit it. + name = "engineering cyborg" + desc = "Don't let this one near the engine." + icon_state = "Noble-Engineering" + projectile_type = /obj/projectile/beam/emitter + projectile_sound = 'sound/weapons/emitter.ogg' + ranged_burst_count = 1 + ranged_cooldown = 1.5 SECONDS + +/mob/living/basic/malfborg/sec/combat + name = "combat cyborg" + desc = "One of Central Command's own, subverted to work for a hostile AI." + icon_state = "Bloodhound_Combat" + attack_sound = 'sound/weapons/sonic_jackhammer.ogg' + casing_type = /obj/item/ammo_casing/caseless/multi_immolator + projectile_type = null + projectile_sound = 'sound/weapons/laser3.ogg' + ranged_burst_count = 1 + ranged_cooldown = 1.5 SECONDS + +/mob/living/basic/malfborg/service + name = "service cyborg" + desc = "It's ready to serve an ass kicking." + icon_state = "Noble-Service" + melee_damage_lower = 15 + melee_damage_upper = 25 + attack_verb_simple = "slam" + attack_verb_continuous = "slams" + attack_sound = 'sound/weapons/guitarslam.ogg' + projectile_type = null + casing_type = /obj/item/ammo_casing/caseless/eshotgun + ranged_burst_count = 1 + ranged_cooldown = 1.5 SECONDS + +/obj/item/ammo_casing/caseless/eshotgun + projectile_type = /obj/projectile/beam/scatter/eshotgun + pellets = 6 + variance = 25 + fire_sound = 'sound/weapons/laser.ogg' + +/obj/item/ammo_casing/caseless/multi_immolator + projectile_type = /obj/projectile/beam/immolator/weak + pellets = 6 + variance = 25 + fire_sound = 'sound/weapons/laser3.ogg' + +/mob/living/basic/malfborg/mining + name = "mining cyborg" + desc = "It digs holes in organic flesh." + icon_state = "Noble-DIG" + melee_damage_lower = 20 + melee_damage_upper = 30 + attack_verb_simple = "mine" + attack_verb_continuous = "mines" + attack_sound = 'sound/weapons/sonic_jackhammer.ogg' + projectile_type = /obj/projectile/kinetic/malf + projectile_sound = 'sound/weapons/kenetic_accel.ogg' + ranged_burst_count = 1 + +/datum/ai_controller/basic_controller/malfborg + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, + ) + ai_movement = /datum/ai_movement/jps + idle_behavior = /datum/idle_behavior/idle_random_walk + interesting_dist = AI_SIMPLE_INTERESTING_DIST + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/ranged_skirmish, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) diff --git a/code/modules/mob/living/basic/hostile/silicon/hostile_mech_actions.dm b/code/modules/mob/living/basic/hostile/silicon/hostile_mech_actions.dm new file mode 100644 index 00000000000..31f1f4aed7a --- /dev/null +++ b/code/modules/mob/living/basic/hostile/silicon/hostile_mech_actions.dm @@ -0,0 +1,39 @@ +/datum/action/cooldown/mob_cooldown/hostile_mech + button_icon = 'icons/mecha/mecha_equipment.dmi' + button_icon_state = "mecha_uac2" + +/datum/action/cooldown/mob_cooldown/hostile_mech/launcher + name = "launch" + button_icon_state = "mecha_missilerack_six" + desc = ABSTRACT_TYPE_DESC + melee_cooldown_time = CLICK_CD_CLICK_ABILITY + cooldown_time = 6 SECONDS + shared_cooldown = NONE + var/obj/projectile/projectile + +/datum/action/cooldown/mob_cooldown/hostile_mech/launcher/Activate(atom/target) + . = ..() + var/mob/living/basic/hostile_mech/mech = owner + if(!istype(mech)) + return + + mech.shoot_projectile(target, projectile) + playsound(get_turf(mech), 'sound/effects/bang.ogg', 100, TRUE) + StartCooldown() + +/datum/action/cooldown/mob_cooldown/hostile_mech/bola_launcher + name = "Bola Launcher" + desc = "Fire a bola at the target." + button_icon = "mecha_bola" + cooldown_time = 20 SECONDS + +/datum/action/cooldown/mob_cooldown/hostile_mech/bola_launcher/Activate(atom/target) + . = ..() + var/mob/living/basic/hostile_mech/mech = owner + if(!istype(mech)) + return + + var/obj/item/restraints/legcuffs/bola/bola = new /obj/item/restraints/legcuffs/bola(mech.loc) + bola.throw_at(target, 7, 1) + playsound(get_turf(mech), 'sound/weapons/whip.ogg', 100, TRUE) + StartCooldown() diff --git a/code/modules/mob/living/basic/hostile/silicon/hostile_mechs.dm b/code/modules/mob/living/basic/hostile/silicon/hostile_mechs.dm new file mode 100644 index 00000000000..e15f58c45f3 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/silicon/hostile_mechs.dm @@ -0,0 +1,354 @@ +/mob/living/basic/hostile_mech + name = "hostile mech" + desc = "I'm a base type and someone was bad and spawned me." + icon = 'icons/mecha/mecha.dmi' + icon_state = "ripley" + health = 300 + maxHealth = 300 + faction = list("malf_drone") + mob_biotypes = MOB_ROBOTIC + sentience_type = SENTIENCE_ARTIFICIAL + move_resist = MOVE_FORCE_OVERPOWERING + pull_force = MOVE_FORCE_OVERPOWERING + flags_2 = IMMUNE_TO_SHUTTLECRUSH_2 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minimum_survivable_temperature = 0 + maximum_survivable_temperature = 20000 + environment_smash = ENVIRONMENT_SMASH_RWALLS + speak_emote = list("states") + a_intent = INTENT_HARM + harm_intent_damage = 0 + melee_damage_lower = 30 + melee_damage_upper = 40 + attack_verb_simple = "smash" + attack_verb_continuous = "smashes" + melee_attack_cooldown_min = 1.5 SECONDS + melee_attack_cooldown_min = 2.5 SECONDS + damage_coeff = list(BRUTE = 0.7, BURN = 0.6, TOX = 0, STAMINA = 0, OXY = 0) + initial_traits = list(TRAIT_NOFIRE) + is_ranged = TRUE + projectile_type = /obj/projectile/beam/laser + projectile_sound = 'sound/weapons/laser.ogg' + ranged_burst_interval = 0.3 SECONDS + death_message = "breaks apart!" + bubble_icon = "machine" + basic_mob_flags = DEL_ON_DEATH + ai_controller = /datum/ai_controller/basic_controller/evil_mech + /// Weapon types the mech has + var/list/weapon_types = list() + /// Time until next move + var/can_move = 0 + /// Amount of time to add on move + var/step_in = 1 SECONDS + /// Chance the mech bounces a hit + var/deflect_chance = 15 + /// Does the mech use a punch? + var/mech_punch = FALSE + /// Actions to grant on Initialize + var/list/innate_actions = list() + +/mob/living/basic/hostile_mech/Initialize(mapload) + . = ..() + AddComponent(/datum/component/aggro_emote, aggro_sound = 'sound/misc/interference.ogg', aggro_volume = 100, emote_chance = 50, sound_vary = FALSE) + add_language("Galactic Common") + set_default_language(GLOB.all_languages["Galactic Common"]) + AddElement(/datum/element/ai_retaliate) + grant_actions_by_list(innate_actions) + RegisterSignal(src, COMSIG_BASICMOB_PRE_ATTACK_RANGED, PROC_REF(cycle_weapon)) + update_icon(UPDATE_OVERLAYS) + +/mob/living/basic/hostile_mech/emp_act(severity) + . = ..() + adjustBruteLoss(30 / severity) + if(prob(25)) + apply_effect(3 SECONDS, STUN, 0) + playsound(get_turf(src), 'sound/mecha/internaldmgalarm.ogg', 100, FALSE) + +/mob/living/basic/hostile_mech/death(gibbed) + // Only execute the below if we successfully died + do_sparks(3, TRUE, src) + playsound(get_turf(src), 'sound/mecha/critdestrsyndi.ogg', 100, FALSE) + . = ..(gibbed) + if(!.) + return FALSE + +/mob/living/basic/hostile_mech/update_overlays() + . = ..() + underlays.Cut() + underlays += emissive_appearance('icons/mecha/mecha_emissive.dmi', "[icon_state]_lightmask") + +/mob/living/basic/hostile_mech/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + if(has_gravity(src)) + return TRUE + if(isspaceturf(get_turf(src))) + new /obj/effect/particle_effect/ion_trails(get_turf(src), dir) + return TRUE + +/mob/living/basic/hostile_mech/attack_by(obj/item/attacking, mob/living/user, params) + if(prob(deflect_chance)) + return + . = ..() + if(prob(25)) + do_sparks(3, TRUE, src) + +/mob/living/basic/hostile_mech/melee_attack(atom/target, list/modifiers, ignore_cooldown) + . = ..() + if(mech_punch && isliving(target)) + var/mob/living/L = target + step_away(L, src, 15) + L.Paralyse(2 SECONDS) + +/mob/living/basic/hostile_mech/Move(atom/destination) + if(can_move >= world.time) + return FALSE + . = ..() + playsound(get_turf(src), 'sound/mecha/mechstep.ogg', 100, TRUE) + can_move = world.time + step_in + +/// Proc used for switching weapons +/mob/living/basic/hostile_mech/proc/cycle_weapon() + if(!prob(25)) // 75% chance we keep using the existing weapon + return + select_new_weapon() + update_ranged_weapon() + +/mob/living/basic/hostile_mech/proc/select_new_weapon() + return + +/mob/living/basic/hostile_mech/proc/update_ranged_weapon() + var/datum/component/ranged_attacks/comp = GetComponent(/datum/component/ranged_attacks) + if(projectile_type) + comp.projectile_type = projectile_type + comp.casing_type = null + else if(casing_type) + comp.casing_type = casing_type + comp.projectile_type = null + comp.projectile_sound = projectile_sound + comp.burst_shots = ranged_burst_count + +/mob/living/basic/hostile_mech/proc/shoot_projectile(atom/target_atom, obj/projectile/thing_to_shoot, set_angle) + var/turf/startloc = get_turf(src) + dir = get_dir(src, target_atom) + + if(!startloc || !target_atom) + return + + var/obj/projectile/P = new thing_to_shoot(startloc) + P.preparePixelProjectile(target_atom, startloc) + P.firer = src + P.firer_source_atom = src + + if(isnum(set_angle)) + P.fire(set_angle) + else + P.fire() + +/datum/ai_controller/basic_controller/evil_mech + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + ) + ai_movement = /datum/ai_movement/jps // Need this for smashing through walls + idle_behavior = /datum/idle_behavior/idle_random_walk + interesting_dist = AI_SIMPLE_INTERESTING_DIST + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/targeted_mob_ability/evil_mech_weaponry, + /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, + /datum/ai_planning_subtree/ranged_skirmish/hostile_mech, + /datum/ai_planning_subtree/attack_obstacle_in_path/walls, + ) +/datum/ai_planning_subtree/ranged_skirmish/hostile_mech + min_range = 0 + +/datum/ai_planning_subtree/targeted_mob_ability/evil_mech_weaponry + ability_key = BB_HOSTILE_MECH_SECONDARY_WEAPON + /// Minimum time between any firing + var/weapon_swap_delay = 2 SECONDS + +/datum/ai_planning_subtree/targeted_mob_ability/evil_mech_weaponry/select_behaviors(datum/ai_controller/controller, seconds_per_tick) + if(controller.blackboard[BB_HOSTILE_MECH_WEAPON_COOLDOWN] >= world.time) + return + var/list/ability_keys = list(BB_HOSTILE_MECH_SECONDARY_WEAPON, BB_HOSTILE_MECH_TERTIARY_WEAPON) + ability_key = pick_n_take(ability_keys) + var/datum/action/cooldown/mob_cooldown/selected_action = controller.blackboard[ability_key] + while(selected_action && !selected_action.IsAvailable()) + if(!ability_keys.len) // All extras are on cooldown + return + ability_key = pick_n_take(ability_keys) + selected_action = controller.blackboard[ability_key] + controller.set_blackboard_key(BB_HOSTILE_MECH_WEAPON_COOLDOWN, world.time + weapon_swap_delay) + return ..() + +/mob/living/basic/hostile_mech/ripley + name = "hostile ripley" + desc = "A hacked Ripley mech, armed with a malfunctioning grenade launcher, heavy plasma cutter, and a drill." + melee_damage_lower = 10 + melee_damage_upper = 15 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + attack_verb_simple = "drill" + attack_verb_continuous = "drills" + melee_attack_cooldown_min = 0.5 SECONDS + melee_attack_cooldown_min = 1.5 SECONDS + attack_sound = 'sound/weapons/drill.ogg' + projectile_type = /obj/projectile/plasma/adv/mech/evil + loot = list(/obj/structure/mecha_wreckage/ripley) + weapon_types = list("217-D Heavy Plasma Cutter", "\"Fat Boy\" Mining Grenade Launcher") + +/mob/living/basic/hostile_mech/ripley/select_new_weapon() + var/new_weapon = pick(weapon_types) + switch(new_weapon) + if("217-D Heavy Plasma Cutter") + projectile_type = /obj/projectile/plasma/adv/mech/evil + projectile_sound = 'sound/weapons/laser.ogg' + if("\"Fat Boy\" Mining Grenade Launcher") + projectile_type = /obj/projectile/bullet/a40mm + projectile_sound = 'sound/effects/bang.ogg' + visible_message(SPAN_WARNING("[src] raises its [new_weapon]!")) + +/obj/projectile/plasma/adv/mech/evil + damage = 15 + +/mob/living/basic/hostile_mech/gygax + name = "hostile gygax" + desc = "A hacked gygax mech, armed with a small missile pod, heavy LMG, and a bola launcher." + icon_state = "gygax" + health = 250 + maxHealth = 250 + melee_damage_lower = 25 + melee_damage_upper = 35 + damage_coeff = list(BRUTE = 0.8, BURN = 0.75, TOX = 0, STAMINA = 0, OXY = 0) + projectile_type = /obj/projectile/bullet/weakbullet3 + ranged_burst_count = 3 + ranged_cooldown = 1.5 SECONDS + projectile_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' + innate_actions = list( + /datum/action/cooldown/mob_cooldown/hostile_mech/bola_launcher = BB_HOSTILE_MECH_SECONDARY_WEAPON + ) + step_in = 0.3 SECONDS + mech_punch = TRUE + deflect_chance = 5 + loot = list(/obj/structure/mecha_wreckage/gygax) + weapon_types = list("SRM-8 Light Missile Rack", "Ultra AC 2") + +/mob/living/basic/hostile_mech/gygax/select_new_weapon() + var/new_weapon = pick(weapon_types) + switch(new_weapon) + if("SRM-8 Light Missile Rack") + projectile_type = /obj/projectile/missile/light + ranged_burst_count = 1 + projectile_sound = 'sound/effects/bang.ogg' + if("Ultra AC 2") + projectile_type = /obj/projectile/bullet/weakbullet3 + ranged_burst_count = 3 + projectile_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' + visible_message(SPAN_WARNING("[src] raises its [new_weapon]!")) + +/mob/living/basic/hostile_mech/durand + name = "hostile durand" + desc = "A hacked Durand mech, armed with a small missile pod, Solaris laser cannon, and a scattershot shotgun." + icon_state = "durand" + health = 400 + maxHealth = 400 + melee_damage_lower = 35 + melee_damage_upper = 45 + damage_coeff = list(BRUTE = 0.6, BURN = 0.85, TOX = 0, STAMINA = 0, OXY = 0) + projectile_type = /obj/projectile/beam/laser/heavylaser + ranged_burst_interval = 1 SECONDS + ranged_cooldown = 1.5 SECONDS + projectile_sound = 'sound/weapons/lasercannonfire.ogg' + step_in = 0.4 SECONDS + mech_punch = TRUE + deflect_chance = 20 + loot = list(/obj/structure/mecha_wreckage/durand) + weapon_types = list("SRM-8 Light Missile Rack", "CH-LC \"Solaris\" Laser Cannon", "LBX AC 10 \"Scattershot\"") + +/mob/living/basic/hostile_mech/durand/select_new_weapon() + var/new_weapon = pick(weapon_types) + switch(new_weapon) + if("SRM-8 Light Missile Rack") + casing_type = null + projectile_type = /obj/projectile/missile/light + projectile_sound = 'sound/effects/bang.ogg' + if("CH-LC \"Solaris\" Laser Cannon") + casing_type = null + projectile_type = /obj/projectile/beam/laser/heavylaser + projectile_sound = 'sound/weapons/lasercannonfire.ogg' + if("LBX AC 10 \"Scattershot\"") + casing_type = /obj/item/ammo_casing/caseless/mecha_scatter + projectile_type = null + projectile_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + visible_message(SPAN_WARNING("[src] raises its [new_weapon]!")) + +/mob/living/basic/hostile_mech/ares + name = "hostile ares" + desc = "A hacked Ares mech, armed with a Solaris laser cannon, xray laser rifle, and an AC6 light machine gun." + icon_state = "ares" + health = 450 + maxHealth = 450 + melee_damage_lower = 35 + melee_damage_upper = 45 + damage_coeff = list(BRUTE = 0.5, BURN = 0.6, TOX = 0, STAMINA = 0, OXY = 0) + projectile_type = /obj/projectile/beam/xray + ranged_cooldown = 1.5 SECONDS + projectile_sound = 'sound/weapons/laser3.ogg' + step_in = 0.4 SECONDS + mech_punch = TRUE + deflect_chance = 25 + loot = list(/obj/structure/mecha_wreckage/ares) + weapon_types = list("S-1 X-Ray Projector", "CH-LC \"Solaris\" Laser Cannon", "Ultra AC 2") + +/mob/living/basic/hostile_mech/ares/select_new_weapon() + var/new_weapon = pick(weapon_types) + switch(new_weapon) + if("S-1 X-Ray Projector") + projectile_type = /obj/projectile/beam/xray + ranged_burst_count = 1 + projectile_sound = 'sound/weapons/laser3.ogg' + if("CH-LC \"Solaris\" Laser Cannon") + projectile_type = /obj/projectile/beam/laser/heavylaser + ranged_burst_count = 1 + projectile_sound = 'sound/weapons/lasercannonfire.ogg' + if("Ultra AC 2") + projectile_type = /obj/projectile/bullet/weakbullet3 + ranged_burst_count = 3 + projectile_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' + visible_message(SPAN_WARNING("[src] raises its [new_weapon]!")) + +/mob/living/basic/hostile_mech/marauder + name = "hostile marauder" + desc = "A hacked Marauder mech, armed with a pulse laser cannon, xray laser rifle, and a light missile launcher." + icon_state = "marauder" + health = 500 + maxHealth = 500 + melee_damage_lower = 35 + melee_damage_upper = 45 + damage_coeff = list(BRUTE = 0.5, BURN = 0.55, TOX = 0, STAMINA = 0, OXY = 0) + projectile_type = /obj/projectile/beam/xray + ranged_cooldown = 1.5 SECONDS + projectile_sound = 'sound/weapons/laser3.ogg' + step_in = 0.4 SECONDS + mech_punch = TRUE + deflect_chance = 25 + loot = list(/obj/structure/mecha_wreckage/ares) + weapon_types = list("S-1 X-Ray Projector", "eZ-13 mk2 Heavy Pulse Rifle", "SRM-8 Light Missile Rack") + +/mob/living/basic/hostile_mech/marauder/select_new_weapon() + var/new_weapon = pick(weapon_types) + switch(new_weapon) + if("S-1 X-Ray Projector") + projectile_type = /obj/projectile/beam/xray + projectile_sound = 'sound/weapons/laser3.ogg' + if("eZ-13 mk2 Heavy Pulse Rifle") + projectile_type = /obj/projectile/beam/pulse/hitscan/heavy + projectile_sound = 'sound/weapons/marauder.ogg' + if("SRM-8 Light Missile Rack") + projectile_type = /obj/projectile/missile/light + projectile_sound = 'sound/effects/bang.ogg' + visible_message(SPAN_WARNING("[src] raises its [new_weapon]!")) + +/obj/item/ammo_casing/caseless/mecha_scatter + projectile_type = /obj/projectile/bullet/midbullet2 + pellets = 4 + variance = 25 + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' diff --git a/code/modules/projectiles/projectile/special_projectiles.dm b/code/modules/projectiles/projectile/special_projectiles.dm index 9e06d0ac382..738150ff44c 100644 --- a/code/modules/projectiles/projectile/special_projectiles.dm +++ b/code/modules/projectiles/projectile/special_projectiles.dm @@ -148,20 +148,28 @@ icon = 'icons/obj/grenade.dmi' icon_state = "missile" damage = 50 - ///If the missile will have a heavy, or light explosion. - var/heavy = TRUE + /// Devastation from the missile + var/devastation = 1 + /// Heavy damage from the missile + var/heavy_damage = 2 + /// Light damage from the missile + var/light_damage = 3 /obj/projectile/missile/on_hit(atom/target, blocked, hit_zone) ..() - if(heavy) - explosion(target, 1, 2, 3, cause = "[type] fired by [key_name(firer)]") - else - explosion(target, -1, 0, 2, cause = "[type] fired by [key_name(firer)]") + explosion(target, devastation, heavy_damage, light_damage, cause = "[type] fired by [key_name(firer)]") return TRUE /obj/projectile/missile/light damage = 15 - heavy = FALSE + devastation = -1 + heavy_damage = 0 + light_damage = 2 + +/obj/projectile/missile/medium + damage = 30 + devastation = -1 + heavy_damage = 1 /obj/projectile/energy/floramut name = "alpha somatoray" diff --git a/paradise.dme b/paradise.dme index 1c11169936e..646e29947fa 100644 --- a/paradise.dme +++ b/paradise.dme @@ -165,6 +165,7 @@ #include "code\__DEFINES\ai\ai_defines.dm" #include "code\__DEFINES\ai\blackboard_defines.dm" #include "code\__DEFINES\ai\changeling_giant_spider_ai_defines.dm" +#include "code\__DEFINES\ai\hostile_mech_blackboard_defines.dm" #include "code\__DEFINES\ai\incursion_blackboard_defines.dm" #include "code\__DEFINES\ai\monkey_ai_defines.dm" #include "code\__DEFINES\ai\swarmer_defines.dm" @@ -2690,6 +2691,9 @@ #include "code\modules\mob\living\basic\hostile\nether_mobs\skeleton_mob.dm" #include "code\modules\mob\living\basic\hostile\revenant\revenant.dm" #include "code\modules\mob\living\basic\hostile\revenant\revenant_abilities.dm" +#include "code\modules\mob\living\basic\hostile\silicon\evil_cyborgs.dm" +#include "code\modules\mob\living\basic\hostile\silicon\hostile_mech_actions.dm" +#include "code\modules\mob\living\basic\hostile\silicon\hostile_mechs.dm" #include "code\modules\mob\living\basic\hostile\swarmers\swarmer.dm" #include "code\modules\mob\living\basic\hostile\swarmers\swarmer_actions.dm" #include "code\modules\mob\living\basic\hostile\swarmers\swarmer_ai.dm"