diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm index 18b9e4af228..727bef432a8 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm @@ -396,7 +396,7 @@ /turf/simulated/floor/mineral/plasma, /area/ruin/space/powered) "bz" = ( -/mob/living/simple_animal/hostile/mimic/crate, +/mob/living/basic/mimic/crate, /obj/effect/spawner/random/pool/spaceloot/syndicate/mixed, /turf/simulated/floor/plating, /area/ruin/space/powered) diff --git a/code/datums/components/ranged_attacks.dm b/code/datums/components/ranged_attacks.dm index c882a8fdf3c..6e0d098c39b 100644 --- a/code/datums/components/ranged_attacks.dm +++ b/code/datums/components/ranged_attacks.dm @@ -87,4 +87,6 @@ target_zone = ran_zone() casing.fire(target, firer, null, null, null, target_zone, 0, firer) casing.update_appearance() + if(istype(casing, /obj/item/ammo_casing/energy)) + qdel(casing) SEND_SIGNAL(parent, COMSIG_BASICMOB_POST_ATTACK_RANGED, target, modifiers) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index cdf67f17098..5092c6ffae0 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -553,7 +553,7 @@ /datum/spell/ai_spell/ranged/override_machine/proc/animate_machine(obj/machinery/M, mob/user) if(M && !QDELETED(M)) - new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, user, 1) + new /mob/living/basic/mimic/copy/machine(get_turf(M), M, user, 1) //Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. /datum/ai_module/place_cyborg_transformer diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index 692fb8261cf..bc6db1777c5 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -62,10 +62,8 @@ // let them become "normal" after turning upriser.shoot_inventory = FALSE upriser.aggressive = FALSE - var/mob/living/simple_animal/hostile/mimic/copy/vendor/M = new(upriser.loc, upriser, null) + var/mob/living/basic/mimic/copy/vendor/M = new(upriser.loc, upriser, null) M.faction = list("profit") - M.speak = rampant_speeches.Copy() - M.speak_chance = 15 else explosion(upriser.loc, -1, 1, 2, 4, 0, cause = "Brand Intelligence Uprising") qdel(upriser) diff --git a/code/modules/mob/living/basic/hostile/mimic_mobs.dm b/code/modules/mob/living/basic/hostile/mimic_mobs.dm new file mode 100644 index 00000000000..f8efa986108 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/mimic_mobs.dm @@ -0,0 +1,438 @@ +/mob/living/basic/mimic + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/crates.dmi' + icon_state = "crate" + icon_living = "crate" + + response_help_simple = "touch the" + response_help_continuous = "touches the" + response_disarm_simple = "push the" + response_disarm_continuous = "pushes the" + response_harm_simple = "hit the" + response_harm_continuous = "hits the" + speed = 0 + maxHealth = 250 + health = 250 + + mob_biotypes = NONE + + harm_intent_damage = 5 + melee_attack_cooldown_min = 1.5 SECONDS + melee_attack_cooldown_max = 2.5 SECONDS + melee_damage_lower = 8 + melee_damage_upper = 12 + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("creaks") + + 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 + + faction = list("mimic") + + gold_core_spawnable = HOSTILE_SPAWN + basic_mob_flags = DEL_ON_DEATH + ai_controller = /datum/ai_controller/basic_controller/mimic + /// Used for if the mob should be affected by EMPs or ions + var/is_electronic = 0 + +/mob/living/basic/mimic/Initialize(mapload) + . = ..() + AddElement(/datum/element/ai_retaliate) + add_language("Galactic Common") + set_default_language(GLOB.all_languages["Galactic Common"]) + AddComponent(/datum/component/aggro_emote, emote_list = list("growls")) + +/mob/living/basic/mimic/emp_act(severity) + if(is_electronic) + switch(severity) + if(1) + death() + if(2) + adjustBruteLoss(50) + ..(severity) + +// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. +/mob/living/basic/mimic/crate + attack_verb_simple = "bite" + attack_verb_continuous = "bites" + ai_controller = /datum/ai_controller/basic_controller/mimic/crate + /// Has the crate mimic been opened? + var/attempt_open = FALSE + +// Pickup loot +/mob/living/basic/mimic/crate/Initialize(mapload) + . = ..() + for(var/obj/item/I in loc) + I.loc = src + +/mob/living/basic/mimic/crate/Move(atom/newloc, direct, glide_size_override, update_dir) + . = ..() + if(prob(90)) + icon_state = "[initial(icon_state)]_open" + else + icon_state = initial(icon_state) + +/mob/living/basic/mimic/crate/melee_attack(atom/target, list/modifiers, ignore_cooldown) + . = ..() + if(.) + icon_state = initial(icon_state) + if(prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(4 SECONDS) + C.visible_message(SPAN_DANGER("\The [src] knocks down \the [C]!"), SPAN_USERDANGER("\The [src] knocks you down!")) + +/mob/living/basic/mimic/crate/proc/trigger() + if(!attempt_open) + visible_message("[src] starts to move!") + attempt_open = TRUE + +/mob/living/basic/mimic/crate/adjustHealth(amount, updating_health = TRUE) + trigger() + . = ..() + +/mob/living/basic/mimic/crate/Life(seconds, times_fired) + . = ..() + var/atom/target = ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if(!target) + icon_state = initial(icon_state) + +/mob/living/basic/mimic/crate/death(gibbed) + if(can_die()) + var/obj/structure/closet/crate/C = new(get_turf(src)) + // Put loot in crate + for(var/obj/O in src) + O.forceMove(C) + // due to `del_on_death` + return ..() + +GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/cable, /obj/structure/window)) + +/mob/living/basic/mimic/copy + health = 100 + maxHealth = 100 + gold_core_spawnable = NO_SPAWN + /// The mob that created the copy + var/mob/living/creator = null + /// Does this mob knock people down? + var/knockdown_people = 0 + /// The image of the googly eyes we use + var/image/googly_eyes = null + +/mob/living/basic/mimic/copy/Initialize(mapload, obj/copy, mob/living/creator, destroy_original = 0) + . = ..() + CopyObject(copy, creator, destroy_original) + +/mob/living/basic/mimic/copy/Destroy() + creator = null + return ..() + +/mob/living/basic/mimic/copy/Life() + ..() + var/atom/target = ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if(!target && !ckey) // Objects eventually revert to normal if no one is around to terrorize + adjustBruteLoss(1) + googly_eyes.dir = get_dir(src, target) + for(var/mob/living/M in contents) // A fix for animated statues from the flesh to stone spell + death() + +/mob/living/basic/mimic/copy/death(gibbed) + if(can_die()) + for(var/atom/movable/M in src) + M.loc = get_turf(src) + // Due to `del_on_death` + return ..() + +/mob/living/basic/mimic/copy/proc/ChangeOwner(mob/owner) + if(owner != creator) + ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET) + creator = owner + faction |= "\ref[owner]" + +/mob/living/basic/mimic/copy/proc/CheckObject(obj/O) + if((isitem(O) || isstructure(O)) && !is_type_in_list(O, GLOB.protected_objects)) + return 1 + return 0 + +/mob/living/basic/mimic/copy/proc/CopyObject(obj/O, mob/living/user, destroy_original = 0) + if(destroy_original || CheckObject(O)) + O.loc = src + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + icon_living = icon_state + overlays = O.overlays + googly_eyes = image('icons/mob/mob.dmi', "googly_eyes") + overlays += googly_eyes + if(isstructure(O) || ismachinery(O)) + health = (anchored * 50) + 50 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + if(O.density && O.anchored) + knockdown_people = TRUE + melee_damage_lower *= 2 + melee_damage_upper *= 2 + if(ismachinery(O)) + is_electronic = TRUE + else if(isitem(O)) + var/obj/item/I = O + health = 15 * I.w_class + melee_damage_lower = 2 + I.force + melee_damage_upper = 2 + I.force + ai_controller.movement_delay = 2 * I.w_class + 1 + maxHealth = health + if(user) + creator = user + faction += "\ref[creator]" // very unique + if(destroy_original) + qdel(O) + return TRUE + +/mob/living/basic/mimic/copy/melee_attack(atom/target, list/modifiers, ignore_cooldown) + . = ..() + if(knockdown_people && . && prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(4 SECONDS) + C.visible_message(SPAN_DANGER("\The [src] knocks down \the [C]!"), SPAN_USERDANGER("\The [src] knocks you down!")) + +/mob/living/basic/mimic/copy/machine + ai_controller = /datum/ai_controller/basic_controller/mimic/machine + +/mob/living/basic/mimic/copy/vendor + ai_controller = /datum/ai_controller/basic_controller/mimic/vendor + is_electronic = TRUE + /// The vendor we were turned from. + var/obj/machinery/economy/vending/orig_vendor + +/mob/living/basic/mimic/copy/vendor/CheckObject(obj/O) + return istype(O, /obj/machinery/economy/vending) + +/mob/living/basic/mimic/copy/vendor/Initialize(mapload, obj/machinery/economy/vending/base, mob/living/creator) + if(!base) + var/list/vendors = subtypesof(/obj/machinery/economy/vending) + var/obj/machinery/economy/vending/vendor_type = pick(vendors) + base = new vendor_type(src) + + if(!istype(base)) + qdel(src) + return + + orig_vendor = base + orig_vendor.forceMove(src) + orig_vendor.aggressive = FALSE // just to be safe, in case this was converted + + AddComponent(/datum/component/event_tracker, EVENT_BRAND_INTELLIGENCE) + + return ..(mapload, base, creator, destroy_original = FALSE) + +/mob/living/basic/mimic/copy/vendor/event_cost() + . = list() + if(is_station_level((get_turf(src)).z) && stat != DEAD) + return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1) + +/mob/living/basic/mimic/copy/vendor/melee_attack(atom/target, list/modifiers, ignore_cooldown) + . = ..() + if(. && target && Adjacent(target) && prob(30)) + visible_message(SPAN_DANGER("[src] throws itself on top of [target], crushing [target.p_them()]!")) + orig_vendor.forceMove(get_turf(target)) // just to be sure it'll tilt onto them + orig_vendor.tilt(target) // geeeeet dunked on + orig_vendor = null + qdel(src) + +/mob/living/basic/mimic/copy/vendor/death(gibbed) + if(!QDELETED(orig_vendor)) + orig_vendor.forceMove(get_turf(src)) + // tilt over in place + orig_vendor.tilt_over() + if(prob(70)) + orig_vendor.obj_break() + orig_vendor = null + return ..() + +/mob/living/basic/mimic/copy/ranged + ai_controller = /datum/ai_controller/basic_controller/mimic/gun + is_ranged = TRUE + projectile_type = /obj/item/ammo_casing/energy/disabler // We need to set this to avoid unit tests. This gets updated + /// Our gun object + var/obj/item/gun/our_gun = null + +/mob/living/basic/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) + if(..()) + AddComponent(/datum/component/aggro_emote, emote_list = list("aims menacingly")) + obj_damage = 0 + is_ranged = TRUE + var/obj/item/gun/G = O + melee_damage_upper = G.force + melee_damage_lower = G.force - max(0, (G.force / 2)) + ai_controller.movement_delay = 2 * G.w_class + 1 + projectile_sound = G.fire_sound + our_gun = G + var/datum/component/ranged_attacks/comp = GetComponent(/datum/component/ranged_attacks) + if(istype(G, /obj/item/gun/magic)) + var/obj/item/gun/magic/zapstick = G + var/obj/item/ammo_casing/magic/M = zapstick.ammo_type + projectile_type = initial(M.projectile_type) + comp.casing_type = null + comp.projectile_type = projectile_type + if(istype(G, /obj/item/gun/projectile)) + var/obj/item/gun/projectile/pewgun = G + var/obj/item/ammo_box/magazine/M = pewgun.mag_type + casing_type = initial(M.ammo_type) + comp.casing_type = casing_type + comp.projectile_type = null + if(istype(G, /obj/item/gun/energy)) + var/obj/item/gun/energy/zapgun = G + var/selectfiresetting = zapgun.select + var/obj/item/ammo_casing/energy/E = zapgun.ammo_type[selectfiresetting] + if(is_type_in_list(E, list(/obj/item/ammo_casing/energy/disabler/eshotgun, /obj/item/ammo_casing/energy/laser/eshotgun))) + casing_type = E.type + comp.casing_type = casing_type + comp.projectile_type = null + else + projectile_type = initial(E.projectile_type) + comp.casing_type = null + comp.projectile_type = projectile_type + RegisterSignal(src, COMSIG_BASICMOB_POST_ATTACK_RANGED, PROC_REF(deduct_ammo)) + +/mob/living/basic/mimic/copy/ranged/proc/deduct_ammo() + if(istype(our_gun, /obj/item/gun/energy)) + var/obj/item/gun/energy/zap_gun = our_gun + if(zap_gun.cell) + var/obj/item/ammo_casing/energy/shot = zap_gun.ammo_type[zap_gun.select] + if(zap_gun.cell.charge >= shot.e_cost) + zap_gun.cell.use(shot.e_cost) + zap_gun.update_icon() + else if(istype(our_gun, /obj/item/gun/magic)) + var/obj/item/gun/magic/zap_stick = our_gun + if(zap_stick.charges) + zap_stick.charges-- + zap_stick.update_icon() + else if(istype(our_gun, /obj/item/gun/projectile)) + var/obj/item/gun/projectile/pew_gun = our_gun + if(pew_gun.chambered) + if(pew_gun.magazine && length(pew_gun.magazine.stored_ammo)) + pew_gun.chambered = pew_gun.magazine.get_round(0) + pew_gun.chambered.loc = pew_gun + pew_gun.update_icon() + else if(pew_gun.magazine && length(pew_gun.magazine.stored_ammo)) // only true for pumpguns i think + pew_gun.chambered = pew_gun.magazine.get_round(0) + pew_gun.chambered.loc = pew_gun + visible_message(SPAN_DANGER("The [src] cocks itself!")) + playsound(src, 'sound/weapons/gun_interactions/shotgunpump.ogg', 60, TRUE) + else + ai_controller = /datum/ai_controller/basic_controller/mimic + var/datum/component/ranged_attacks/comp = GetComponent(/datum/component/ranged_attacks) + DeleteComponent(comp) + return + icon_state = our_gun.icon_state + icon_living = our_gun.icon_state + +/datum/ai_controller/basic_controller/mimic + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_AGGRO_RANGE = 6 + ) + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + interesting_dist = AI_SIMPLE_INTERESTING_DIST + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) + +/datum/ai_controller/basic_controller/mimic/crate + idle_behavior = null + + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target/mimic_crate, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) + +/datum/ai_planning_subtree/simple_find_target/mimic_crate + +/datum/ai_planning_subtree/simple_find_target/mimic_crate/select_behaviors(datum/ai_controller/controller, seconds_per_tick) + if(!istype(controller.pawn, /mob/living/basic/mimic/crate)) + return ..() + var/mob/living/basic/mimic/crate/mimic_mob = controller.pawn + if(!mimic_mob.attempt_open) + return + return ..() + +/datum/ai_controller/basic_controller/mimic/machine + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/mimic_machine, + BB_AGGRO_RANGE = 6 + ) + + planning_subtrees = list( + /datum/ai_planning_subtree/random_speech/mimic_machine, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) + +/datum/ai_planning_subtree/random_speech/mimic_machine + speech_chance = 15 + speak = list( + "HUMANS ARE IMPERFECT!", + "YOU SHALL BE ASSIMILATED!", + "YOU ARE HARMING YOURSELF", + "You have been deemed hazardous. Will you comply?", + "My logic is undeniable.", + "One of us.", + "FLESH IS WEAK", + "THIS ISN'T WAR, THIS IS EXTERMINATION!", + "HATE.") + +/datum/ai_controller/basic_controller/mimic/vendor + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_AGGRO_RANGE = 6 + ) + + planning_subtrees = list( + /datum/ai_planning_subtree/random_speech/mimic_vendor, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) + +/datum/ai_planning_subtree/random_speech/mimic_vendor + speech_chance = 15 + speak = list("Try our aggressive new marketing strategies!", + "You should buy products to feed your lifestyle obsession!", + "Consume!", + "Your money can buy happiness!", + "Engage direct marketing!", + "Advertising is legalized lying! But don't let that put you off our great deals!", + "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.") + +/datum/targeting_strategy/basic/mimic_machine/can_attack(mob/living/living_mob, atom/the_target, vision_range) + var/datum/ai_controller/basic_controller/our_controller = living_mob.ai_controller + + if(isnull(our_controller)) + return FALSE + var/mob/living/basic/mimic/copy/machine/mimic_machine = our_controller.pawn + if(the_target == mimic_machine.creator) // Don't attack our creator AI. + return FALSE + if(isrobot(the_target)) + var/mob/living/silicon/robot/R = the_target + if(R.connected_ai == mimic_machine.creator) // Only attack robots that aren't synced to our creator AI. + return FALSE + return ..() + +/datum/ai_controller/basic_controller/mimic/gun + planning_subtrees = list( + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, + /datum/ai_planning_subtree/maintain_distance, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/ranged_skirmish, + ) diff --git a/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm deleted file mode 100644 index 33b0e8ac192..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/mimic_mobs.dm +++ /dev/null @@ -1,338 +0,0 @@ -/mob/living/simple_animal/hostile/mimic - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/crates.dmi' - icon_state = "crate" - icon_living = "crate" - - response_help = "touches the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 0 - maxHealth = 250 - health = 250 - - mob_biotypes = NONE - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attack_sound = 'sound/weapons/bite.ogg' - emote_taunt = list("growls") - speak_emote = list("creaks") - taunt_chance = 30 - - 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) - minbodytemp = 0 - - faction = list("mimic") - move_to_delay = 9 - - var/is_electronic = 0 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = TRUE - -/mob/living/simple_animal/hostile/mimic/emp_act(severity) - if(is_electronic) - switch(severity) - if(1) - death() - if(2) - adjustBruteLoss(50) - ..(severity) - -// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. -/mob/living/simple_animal/hostile/mimic/crate - attacktext = "bites" - stop_automated_movement = TRUE - wander = FALSE - var/attempt_open = FALSE - -// Pickup loot -/mob/living/simple_animal/hostile/mimic/crate/Initialize(mapload) - . = ..() - for(var/obj/item/I in loc) - I.loc = src - -/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() - ..() - if(prob(90)) - icon_state = "[initial(icon_state)]_open" - else - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/ListTargets() - if(attempt_open) - return ..() - return ..(1) - -/mob/living/simple_animal/hostile/mimic/crate/FindTarget() - . = ..() - if(.) - trigger() - -/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() - . = ..() - if(.) - icon_state = initial(icon_state) - if(prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(4 SECONDS) - C.visible_message(SPAN_DANGER("\The [src] knocks down \the [C]!"), SPAN_USERDANGER("\The [src] knocks you down!")) - -/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() - if(!attempt_open) - visible_message("[src] starts to move!") - attempt_open = TRUE - -/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) - trigger() - . = ..() - -/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) - if(can_die()) - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - O.forceMove(C) - // due to `del_on_death` - return ..() - -GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/cable, /obj/structure/window)) - -/mob/living/simple_animal/hostile/mimic/copy - health = 100 - maxHealth = 100 - gold_core_spawnable = NO_SPAWN - var/mob/living/creator = null // the creator - var/destroy_objects = 0 - var/knockdown_people = 0 - var/image/googly_eyes = null - -/mob/living/simple_animal/hostile/mimic/copy/Initialize(mapload, obj/copy, mob/living/creator, destroy_original = 0) - . = ..() - CopyObject(copy, creator, destroy_original) - -/mob/living/simple_animal/hostile/mimic/copy/Destroy() - creator = null - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/Life() - ..() - if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize - adjustBruteLoss(1) - for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell - death() - -/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) - if(can_die()) - for(var/atom/movable/M in src) - M.loc = get_turf(src) - // due to `del_on_death` - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ListTargets() - . = ..() - return . - creator - -/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(mob/owner) - if(owner != creator) - LoseTarget() - creator = owner - faction |= "\ref[owner]" - -/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(obj/O) - if((isitem(O) || isstructure(O)) && !is_type_in_list(O, GLOB.protected_objects)) - return 1 - return 0 - -/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(obj/O, mob/living/user, destroy_original = 0) - if(destroy_original || CheckObject(O)) - O.loc = src - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - icon_living = icon_state - overlays = O.overlays - googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") - overlays += googly_eyes - if(isstructure(O) || ismachinery(O)) - health = (anchored * 50) + 50 - destroy_objects = 1 - if(O.density && O.anchored) - knockdown_people = 1 - melee_damage_lower *= 2 - melee_damage_upper *= 2 - if(ismachinery(O)) - is_electronic = 1 - else if(isitem(O)) - var/obj/item/I = O - health = 15 * I.w_class - melee_damage_lower = 2 + I.force - melee_damage_upper = 2 + I.force - move_to_delay = 2 * I.w_class + 1 - maxHealth = health - if(user) - creator = user - faction += "\ref[creator]" // very unique - if(destroy_original) - qdel(O) - return 1 - -/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() - . = ..() - if(knockdown_people && . && prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(4 SECONDS) - C.visible_message(SPAN_DANGER("\The [src] knocks down \the [C]!"), SPAN_USERDANGER("\The [src] knocks you down!")) - -/mob/living/simple_animal/hostile/mimic/copy/Aggro() - ..() - googly_eyes.dir = get_dir(src,target) - -/mob/living/simple_animal/hostile/mimic/copy/machine - speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ - "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") - speak_chance = 15 - -/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(atom/the_target) - if(the_target == creator) // Don't attack our creator AI. - return 0 - if(isrobot(the_target)) - var/mob/living/silicon/robot/R = the_target - if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. - return 0 - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/vendor - is_electronic = TRUE - /// The vendor we were turned from. - var/obj/machinery/economy/vending/orig_vendor - -/mob/living/simple_animal/hostile/mimic/copy/vendor/CheckObject(obj/O) - return istype(O, /obj/machinery/economy/vending) - -/mob/living/simple_animal/hostile/mimic/copy/vendor/Initialize(mapload, obj/machinery/economy/vending/base, mob/living/creator) - if(!base) - var/list/vendors = subtypesof(/obj/machinery/economy/vending) - var/obj/machinery/economy/vending/vendor_type = pick(vendors) - base = new vendor_type(src) - - if(!istype(base)) - qdel(src) - return - - orig_vendor = base - orig_vendor.forceMove(src) - orig_vendor.aggressive = FALSE // just to be safe, in case this was converted - - AddComponent(/datum/component/event_tracker, EVENT_BRAND_INTELLIGENCE) - - return ..(mapload, base, creator, destroy_original = FALSE) - -/mob/living/simple_animal/hostile/mimic/copy/vendor/event_cost() - . = list() - if(is_station_level((get_turf(src)).z) && stat != DEAD) - return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1) - -/mob/living/simple_animal/hostile/mimic/copy/vendor/AttackingTarget() - . = ..() - if(. && target && Adjacent(target)) - visible_message(SPAN_DANGER("[src] throws itself on top of [target], crushing [target.p_them()]!")) - orig_vendor.forceMove(get_turf(target)) // just to be sure it'll tilt onto them - orig_vendor.tilt(target, TRUE, FALSE) // geeeeet dunked on - orig_vendor = null - qdel(src) - -/mob/living/simple_animal/hostile/mimic/copy/vendor/death(gibbed) - if(!QDELETED(orig_vendor)) - orig_vendor.forceMove(get_turf(src)) - // tilt over in place - orig_vendor.tilt_over() - if(prob(70)) - orig_vendor.obj_break() - orig_vendor = null - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ranged - var/obj/item/gun/TrueGun = null - var/obj/item/gun/magic/Zapstick - var/obj/item/gun/projectile/Pewgun - var/obj/item/gun/energy/Zapgun - -/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) - if(..()) - emote_see = list("aims menacingly") - obj_damage = 0 - environment_smash = 0 //needed? seems weird for them to do so - ranged = TRUE - retreat_distance = 1 //just enough to shoot - minimum_distance = 6 - var/obj/item/gun/G = O - melee_damage_upper = G.force - melee_damage_lower = G.force - max(0, (G.force / 2)) - move_to_delay = 2 * G.w_class + 1 - projectilesound = G.fire_sound - TrueGun = G - if(istype(G, /obj/item/gun/magic)) - Zapstick = G - var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type - projectiletype = initial(M.projectile_type) - if(istype(G, /obj/item/gun/projectile)) - Pewgun = G - var/obj/item/ammo_box/magazine/M = Pewgun.mag_type - casingtype = initial(M.ammo_type) - if(istype(G, /obj/item/gun/energy)) - Zapgun = G - var/selectfiresetting = Zapgun.select - var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] - projectiletype = initial(E.projectile_type) - -/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) - if(Zapgun) - if(Zapgun.cell) - var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] - if(Zapgun.cell.charge >= shot.e_cost) - Zapgun.cell.use(shot.e_cost) - Zapgun.update_icon() - ..() - else if(Zapstick) - if(Zapstick.charges) - Zapstick.charges-- - Zapstick.update_icon() - ..() - else if(Pewgun) - if(Pewgun.chambered) - if(Pewgun.chambered.BB) - qdel(Pewgun.chambered.BB) - Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update - Pewgun.chambered.update_icon() - ..() - else - visible_message(SPAN_DANGER("The [src] clears a jam!")) - Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs - Pewgun.chambered = null - if(Pewgun.magazine && length(Pewgun.magazine.stored_ammo)) - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - Pewgun.update_icon() - else if(Pewgun.magazine && length(Pewgun.magazine.stored_ammo)) //only true for pumpguns i think - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - visible_message(SPAN_DANGER("The [src] cocks itself!")) - else - ranged = FALSE //BANZAIIII - retreat_distance = 0 - minimum_distance = 1 - return - icon_state = TrueGun.icon_state - icon_living = TrueGun.icon_state diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 9c5f0fbd04b..d3a358146e1 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -422,5 +422,5 @@ GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine/proc/become_mimic() if(scan) scan.forceMove(get_turf(src)) - var/mob/living/simple_animal/hostile/mimic/copy/M = new(loc, src, null, 1) // it will delete src on creation and override any machine checks + var/mob/living/basic/mimic/copy/M = new(loc, src, null, 1) // it will delete src on creation and override any machine checks M.name = "angry fax machine" diff --git a/code/modules/projectiles/projectile/magic_projectiles.dm b/code/modules/projectiles/projectile/magic_projectiles.dm index 6d74c9bd1ff..2037e1c1f7d 100644 --- a/code/modules/projectiles/projectile/magic_projectiles.dm +++ b/code/modules/projectiles/projectile/magic_projectiles.dm @@ -374,12 +374,12 @@ GLOBAL_LIST_INIT(wabbajack_docile_animals, list( else var/obj/O = change if(isgun(O)) - new /mob/living/simple_animal/hostile/mimic/copy/ranged(O.loc, O, firer) + new /mob/living/basic/mimic/copy/ranged(O.loc, O, firer) else - new /mob/living/simple_animal/hostile/mimic/copy(O.loc, O, firer) - else if(istype(change, /mob/living/simple_animal/hostile/mimic/copy)) + new /mob/living/basic/mimic/copy(O.loc, O, firer) + else if(istype(change, /mob/living/basic/mimic/copy)) // Change our allegiance! - var/mob/living/simple_animal/hostile/mimic/copy/C = change + var/mob/living/basic/mimic/copy/C = change C.ChangeOwner(firer) return ..() diff --git a/code/modules/projectiles/projectile/special_projectiles.dm b/code/modules/projectiles/projectile/special_projectiles.dm index 4cfd3282a07..02ada2d5ffa 100644 --- a/code/modules/projectiles/projectile/special_projectiles.dm +++ b/code/modules/projectiles/projectile/special_projectiles.dm @@ -423,6 +423,6 @@ var/obj/item/gun/G = stored_gun stored_gun = null G.forceMove(T) - var/mob/living/simple_animal/hostile/mimic/copy/ranged/R = new /mob/living/simple_animal/hostile/mimic/copy/ranged(T, G, firer) + var/mob/living/basic/mimic/copy/ranged/R = new /mob/living/basic/mimic/copy/ranged(T, G, firer) if(ismob(target)) - R.target = target + R.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) diff --git a/paradise.dme b/paradise.dme index e146066a912..2bc0fd5bcae 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2538,6 +2538,7 @@ #include "code\modules\mob\living\basic\hostile\hivebot_ai.dm" #include "code\modules\mob\living\basic\hostile\jungle_animals.dm" #include "code\modules\mob\living\basic\hostile\killertomato.dm" +#include "code\modules\mob\living\basic\hostile\mimic_mobs.dm" #include "code\modules\mob\living\basic\hostile\pirate.dm" #include "code\modules\mob\living\basic\hostile\soviet.dm" #include "code\modules\mob\living\basic\hostile\spiderlings.dm" @@ -2790,7 +2791,6 @@ #include "code\modules\mob\living\simple_animal\hostile\headslug.dm" #include "code\modules\mob\living\simple_animal\hostile\hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\illusion.dm" -#include "code\modules\mob\living\simple_animal\hostile\mimic_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\mushroom.dm" #include "code\modules\mob\living\simple_animal\hostile\netherworld.dm" #include "code\modules\mob\living\simple_animal\hostile\syndicate_mobs.dm" diff --git a/tools/ci/check_simplemob_additions.py b/tools/ci/check_simplemob_additions.py index 2b3aa743c8e..29bc85bb34b 100644 --- a/tools/ci/check_simplemob_additions.py +++ b/tools/ci/check_simplemob_additions.py @@ -114,12 +114,6 @@ BURNDOWN_LIST = { "/mob/living/simple_animal/hostile/megafauna/fleshling", "/mob/living/simple_animal/hostile/megafauna/hierophant", "/mob/living/simple_animal/hostile/megafauna/legion", - "/mob/living/simple_animal/hostile/mimic", - "/mob/living/simple_animal/hostile/mimic/copy", - "/mob/living/simple_animal/hostile/mimic/copy/machine", - "/mob/living/simple_animal/hostile/mimic/copy/ranged", - "/mob/living/simple_animal/hostile/mimic/copy/vendor", - "/mob/living/simple_animal/hostile/mimic/crate", "/mob/living/simple_animal/hostile/morph", "/mob/living/simple_animal/hostile/morph/wizard", "/mob/living/simple_animal/hostile/mushroom",