diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm index 4e044735793c..534ac101986b 100644 --- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm +++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm @@ -95,3 +95,7 @@ /// From /mob/living/carbon/xenomorph/proc/do_evolve() #define COMSIG_XENO_EVOLVE_TO_NEW_CASTE "xeno_evolve_to_new_caste" + +/// From /obj/structure/tunnel/attack_alien() : (mob/living/carbon/xenomorph/xeno) +#define COMSIG_XENO_ENTER_TUNNEL "xeno_enter_tunnel" + #define COMPONENT_CANCEL_TUNNEL (1<<0) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 4a3f16858fda..7b5ed3162e5d 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -509,3 +509,6 @@ GLOBAL_LIST(trait_name_map) #define HACKED_TRAIT "hacked" /// traits from chloroform usage #define CHLOROFORM_TRAIT "chloroform" + +// from watchtower.dm +#define TRAIT_ON_WATCHTOWER "on_watchtower" diff --git a/code/datums/elements/bullet_trait/direct_only.dm b/code/datums/elements/bullet_trait/direct_only.dm new file mode 100644 index 000000000000..0e3a609d5ecb --- /dev/null +++ b/code/datums/elements/bullet_trait/direct_only.dm @@ -0,0 +1,43 @@ +// This trait makes the projectile only hit targets directly clicked + +/datum/element/bullet_trait_direct_only + // General bullet trait vars + element_flags = ELEMENT_DETACH|ELEMENT_BESPOKE + id_arg_index = 2 + +/datum/element/bullet_trait_direct_only/Attach(datum/target) + . = ..() + if(!istype(target, /obj/projectile)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_BULLET_CHECK_MOB_SKIPPING, PROC_REF(check_distance)) + +/datum/element/bullet_trait_direct_only/Detach(datum/target) + UnregisterSignal(target, COMSIG_BULLET_CHECK_MOB_SKIPPING) + + return ..() + +/datum/element/bullet_trait_direct_only/proc/check_distance(obj/projectile/projectile, mob/living/carbon/human/projectile_target) + SIGNAL_HANDLER + + if(projectile.original != projectile_target) + return COMPONENT_SKIP_MOB + +/datum/element/bullet_trait_direct_only/watchtower/check_distance(obj/projectile/projectile, mob/living/carbon/human/projectile_target) + if(!HAS_TRAIT(projectile.firer, TRAIT_ON_WATCHTOWER)) + if(!istype(projectile.firer, /mob)) + return + var/mob/firer = projectile.firer + var/obj/item/weapon/gun/gun = firer.get_inactive_hand() + if(istype(gun)) + gun.remove_bullet_traits(list("watchtower_arc")) + + gun = firer.get_active_hand() + if(istype(gun)) + gun.remove_bullet_traits(list("watchtower_arc")) + return + + if(HAS_TRAIT(projectile_target, TRAIT_ON_WATCHTOWER)) + return + + return ..() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index e0fed33a7aa1..e1527813971f 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -882,8 +882,6 @@ SPAN_NOTICE("You look up from [zoom_device].")) zoom = !zoom COOLDOWN_START(user, zoom_cooldown, 20) - SEND_SIGNAL(user, COMSIG_LIVING_ZOOM_OUT, src) - SEND_SIGNAL(src, COMSIG_ITEM_UNZOOM, user) UnregisterSignal(src, list( COMSIG_ITEM_DROPPED, COMSIG_ITEM_UNWIELD, @@ -896,6 +894,9 @@ user.client.pixel_x = 0 user.client.pixel_y = 0 + SEND_SIGNAL(user, COMSIG_LIVING_ZOOM_OUT, src) + SEND_SIGNAL(src, COMSIG_ITEM_UNZOOM, user) + /obj/item/proc/zoom_handle_mob_move_or_look(mob/living/mover, actually_moving, direction, specific_direction) SIGNAL_HANDLER diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 325af12c814b..690fefee896b 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -176,6 +176,33 @@ if(STATE_REINFORCED_WALL) return do_reinforced_wall(W, user) if(STATE_DISPLACED) + if(HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + var/area/area = get_area(src) + if(CEILING_IS_PROTECTED(area.ceiling, CEILING_GLASS)) + to_chat(user, SPAN_WARNING("Watchtowers can only be built in the open.")) + return + + var/list/turf/turfs = CORNER_BLOCK(get_turf(src), 2, 2) + var/list/obj/structure/girder/girders = list() + + for(var/turf/current_turf in turfs) + var/found_girder = FALSE + for(var/obj/structure/girder/girder in current_turf) + if(girder.state == STATE_DISPLACED) + found_girder = TRUE + girders += girder + if(!found_girder) + return + + + if(!do_after(user,30, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + new /obj/structure/watchtower(loc) + + for(var/list/obj/structure/girder as anything in girders) + qdel(girder) + if(HAS_TRAIT(W, TRAIT_TOOL_CROWBAR)) var/turf/open/floor = loc if(!floor.allow_construction) @@ -393,6 +420,13 @@ anchored = FALSE state = STATE_DISPLACED +/obj/structure/girder/broken + health = 0 + icon_state = "girder_damaged" + anchored = FALSE + density = FALSE + state = STATE_STANDARD + /obj/structure/girder/reinforced icon_state = "reinforced" health = 500 diff --git a/code/modules/cm_aliens/structures/tunnel.dm b/code/modules/cm_aliens/structures/tunnel.dm index 4ceec0160bdf..bfe03f224a58 100644 --- a/code/modules/cm_aliens/structures/tunnel.dm +++ b/code/modules/cm_aliens/structures/tunnel.dm @@ -211,6 +211,9 @@ . = attack_alien(M) /obj/structure/tunnel/attack_alien(mob/living/carbon/xenomorph/M) + if(SEND_SIGNAL(M, COMSIG_XENO_ENTER_TUNNEL) & COMPONENT_CANCEL_TUNNEL) + return XENO_NO_DELAY_ACTION + if(!istype(M) || M.is_mob_incapacitated(TRUE)) return XENO_NO_DELAY_ACTION diff --git a/code/modules/movement/movement.dm b/code/modules/movement/movement.dm index 8151d2df6707..0a0496c67e1c 100644 --- a/code/modules/movement/movement.dm +++ b/code/modules/movement/movement.dm @@ -56,18 +56,19 @@ return NO_BLOCKED_MOVEMENT /atom/movable/Move(NewLoc, direct) - // If Move is not valid, exit - if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, NewLoc) & COMPONENT_CANCEL_MOVE) - return FALSE - var/atom/oldloc = loc var/old_dir = dir - . = ..() if (flags_atom & DIRLOCK) setDir(old_dir) else if(old_dir != direct) setDir(direct) + + // If Move is not valid, exit + if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, NewLoc) & COMPONENT_CANCEL_MOVE) + return FALSE + + . = ..() l_move_time = world.time if ((oldloc != loc && oldloc && oldloc.z == z)) last_move_dir = get_dir(oldloc, loc) diff --git a/code/modules/watchtower/blockers.dm b/code/modules/watchtower/blockers.dm new file mode 100644 index 000000000000..3802d3abf183 --- /dev/null +++ b/code/modules/watchtower/blockers.dm @@ -0,0 +1,45 @@ +/// Invisible Blocker Walls, they link up with the watchtower and collapse with it +/obj/structure/blocker/watchtower + name = "Watchtower Blocker" + icon = 'icons/obj/structures/barricades.dmi' + icon_state = "folding_0" // for map editing only + flags_atom = ON_BORDER + invisibility = INVISIBILITY_MAXIMUM + density = TRUE + opacity = FALSE // Unfortunately this doesn't behave as we'd want with ON_BORDER so we can't make tent opaque + throwpass = TRUE // Needs this so xenos can attack through the blocker and hit the tents or people inside + /// The watchtower this blocker relates to, will be destroyed along with it + var/obj/structure/watchtower/linked_watchtower + +/obj/structure/blocker/watchtower/Initialize(mapload, ...) + . = ..() + icon_state = null + linked_watchtower = locate(/obj/structure/watchtower) in loc + if(!linked_watchtower) + return INITIALIZE_HINT_QDEL + RegisterSignal(linked_watchtower, COMSIG_PARENT_QDELETING, PROC_REF(collapse)) + +/obj/structure/blocker/watchtower/Destroy(force) + . = ..() + linked_watchtower = null + +/obj/structure/blocker/watchtower/proc/collapse() + SIGNAL_HANDLER + qdel(src) + +/obj/structure/blocker/watchtower/initialize_pass_flags(datum/pass_flags_container/PF) + ..() + if (PF) + PF.flags_can_pass_all = NONE + PF.flags_can_pass_front = NONE + PF.flags_can_pass_behind = NONE + +/obj/structure/blocker/watchtower/get_projectile_hit_boolean(obj/projectile/P) + . = ..() + return FALSE // Always fly through the watchtower + +//Blocks all direction, basically an invisible wall +/obj/structure/blocker/watchtower/full_tile + flags_atom = NO_FLAGS + icon = 'icons/landmarks.dmi' + icon_state = "invisible_wall" diff --git a/code/modules/watchtower/watchtower.dm b/code/modules/watchtower/watchtower.dm new file mode 100644 index 000000000000..ba387ed12d09 --- /dev/null +++ b/code/modules/watchtower/watchtower.dm @@ -0,0 +1,378 @@ +#define WATCHTOWER_STAGE_WELDED 1 +#define WATCHTOWER_STAGE_COLUMNS 2 +#define WATCHTOWER_STAGE_HEIGHTNED_WELDER 2.5 +#define WATCHTOWER_STAGE_HEIGHTNED_WRENCH 3 +#define WATCHTOWER_STAGE_FLOOR 4 +#define WATCHTOWER_STAGE_BARRICADED 5 +#define WATCHTOWER_STAGE_ROOF_SUPPORT 6 +#define WATCHTOWER_STAGE_COMPLETE 7 + +/obj/structure/watchtower + name = "watchtower" + icon = 'icons/obj/structures/watchtower.dmi' + icon_state = "stage1" + + density = FALSE + bound_width = 64 + bound_height = 96 + health = 1000 + layer = ABOVE_TURF_LAYER + var/max_health = 1000 + + var/stage = 1 + var/image/roof_image + +/obj/structure/watchtower/Initialize() + var/list/turf/blocked_turfs = CORNER_BLOCK(get_turf(src), 2, 1) + CORNER_BLOCK_OFFSET(get_turf(src), 2, 1, 0, 2) + + var/atom/west_blocker = new /obj/structure/blocker/watchtower(locate(x, y+1, z)) + var/atom/east_blocker = new /obj/structure/blocker/watchtower(locate(x+1, y+1, z)) + west_blocker.dir = WEST + east_blocker.dir = EAST + + for(var/turf/current_turf in blocked_turfs) + new /obj/structure/blocker/watchtower/full_tile(current_turf) + + update_icon() + + return ..() + +/obj/structure/watchtower/Destroy() + playsound(src, 'sound/effects/metal_crash.ogg', 50, 1) + var/list/turf/top_turfs = CORNER_BLOCK_OFFSET(get_turf(src), 2, 1, 0, 1) + + for(var/turf/current_turf in top_turfs) + for(var/mob/falling_mob in current_turf.contents) + falling_mob.ex_act(100, 0) + on_leave(falling_mob) + + new /obj/structure/girder(get_turf(src)) + new /obj/structure/girder/broken(locate(x+1, y, z)) + new /obj/structure/girder/broken(locate(x, y+1, z)) + new /obj/item/stack/sheet/metal(locate(x+1, y+1, z), 10) + new /obj/item/stack/rods(locate(x+1, y+1, z), 20) + + return ..() + +/obj/structure/watchtower/update_icon() + . = ..() + icon_state = "stage[stage]" + + overlays.Cut() + + if(stage >= WATCHTOWER_STAGE_BARRICADED) + overlays += image(icon=icon, icon_state="railings", layer=ABOVE_MOB_LAYER, pixel_y=25) + + if (stage == WATCHTOWER_STAGE_COMPLETE) + roof_image = image(icon=icon, icon_state="roof", layer=ABOVE_MOB_LAYER, pixel_y=51) + roof_image.plane = ROOF_PLANE + roof_image.appearance_flags = KEEP_APART + overlays += roof_image + +/obj/structure/watchtower/attackby(obj/item/item, mob/user) + if(istool(item) && !skillcheck(user, SKILL_CONSTRUCTION, SKILL_CONSTRUCTION_ENGI)) + to_chat(user, SPAN_WARNING("You are not trained to configure [src]...")) + return TRUE + + switch(stage) + if(WATCHTOWER_STAGE_WELDED) + if(!istype(item, /obj/item/stack/rods)) + return + + var/obj/item/stack/rods/rods = item + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_NO_NEEDHAND|BEHAVIOR_IMMOBILE, BUSY_ICON_FRIENDLY, src)) + return + + if(rods.use(60)) + to_chat(user, SPAN_NOTICE("You add connection rods to the watchtower.")) + stage = WATCHTOWER_STAGE_COLUMNS + update_icon() + else + to_chat(user, SPAN_NOTICE("You failed to construct the connection rods. You need more rods.")) + + return + if(WATCHTOWER_STAGE_COLUMNS) + if(!iswelder(item)) + return + + if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + to_chat(user, SPAN_NOTICE("You weld the connection rods to the frame.")) + stage = WATCHTOWER_STAGE_HEIGHTNED_WELDER + + return + if(WATCHTOWER_STAGE_HEIGHTNED_WELDER) + if(!HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) + return + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + to_chat(user, SPAN_NOTICE("You elevate the the frame and screw it up top.")) + stage = WATCHTOWER_STAGE_HEIGHTNED_WRENCH + update_icon() + + return + if(WATCHTOWER_STAGE_HEIGHTNED_WRENCH) + if(!HAS_TRAIT(item, TRAIT_TOOL_SCREWDRIVER)) + return + + var/obj/item/stack/sheet/metal/metal = user.get_inactive_hand() + if(!istype(metal)) + to_chat(user, SPAN_BOLDWARNING("You need metal sheets in your offhand to continue construction of the watchtower.")) + return FALSE + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + if(metal.use(50)) + to_chat(user, SPAN_NOTICE("You construct the watchtower platform.")) + stage = WATCHTOWER_STAGE_FLOOR + update_icon() + else + to_chat(user, SPAN_NOTICE("You failed to construct the watchtower platform, you need more metal sheets in your offhand.")) + + return + if(WATCHTOWER_STAGE_FLOOR) + if(!HAS_TRAIT(item, TRAIT_TOOL_CROWBAR)) + return + + var/obj/item/stack/sheet/plasteel/plasteel = user.get_inactive_hand() + if(!istype(plasteel)) + to_chat(user, SPAN_BOLDWARNING("You need plasteel sheets in your offhand to continue construction of the watchtower.")) + return FALSE + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + if(plasteel.use(25)) + to_chat(user, SPAN_NOTICE("You construct the watchtower railing.")) + stage = WATCHTOWER_STAGE_BARRICADED + update_icon() + else + to_chat(user, SPAN_NOTICE("You failed to construct the watchtower railing, you need more plasteel sheets in your offhand.")) + + return + if(WATCHTOWER_STAGE_BARRICADED) + if (!HAS_TRAIT(item, TRAIT_TOOL_WRENCH)) + return + + var/obj/item/stack/rods/rods = user.get_inactive_hand() + if(!istype(rods)) + to_chat(user, SPAN_BOLDWARNING("You need metal rods in your offhand to continue construction of the watchtower.")) + return FALSE + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + if(rods.use(60)) + to_chat(user, SPAN_NOTICE("You construct the watchtower support rods.")) + stage = WATCHTOWER_STAGE_ROOF_SUPPORT + update_icon() + else + to_chat(user, SPAN_NOTICE("You failed to construct the watchtower support rods, you need more metal rods in your offhand.")) + + return + if(WATCHTOWER_STAGE_ROOF_SUPPORT) + if (!iswelder(item)) + return + + if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + var/obj/item/stack/sheet/plasteel/plasteel = user.get_inactive_hand() + if(!istype(plasteel)) + to_chat(user, SPAN_BOLDWARNING("You need plasteel sheets in your offhand to continue construction of the watchtower.")) + return FALSE + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + if(plasteel.use(25)) + to_chat(user, SPAN_NOTICE("You complete the watchtower.")) + stage = WATCHTOWER_STAGE_COMPLETE + update_icon() + else + to_chat(user, SPAN_NOTICE("You failed to complete the watchtower, you need more plasteel sheets in your offhand.")) + + return + if(WATCHTOWER_STAGE_COMPLETE) + if (!iswelder(item)) + return + + if(!HAS_TRAIT(item, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + var/obj/item/stack/sheet/metal/metal = user.get_inactive_hand() + if(!istype(metal)) + to_chat(user, SPAN_BOLDWARNING("You need metal sheets in your offhand to patch the watchtower.")) + return + + if(health >= max_health) + to_chat(user, SPAN_NOTICE("The watchtower is in good condition.")) + return + + if(!do_after(user, 4 SECONDS * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + if(metal.use(5)) + to_chat(user, SPAN_NOTICE("You patch the watchtower with the metal sheets.")) + update_health(-50) + else + to_chat(user, SPAN_NOTICE("You failed to patch the watchtower, you need more metal sheets in your offhand.")) + +/obj/structure/watchtower/get_examine_text(mob/user) + . = ..() + + var/dam = health / max_health + + if(dam <= 0.3) + . += SPAN_WARNING("It looks heavily damaged.") + else if(dam <= 0.6) + . += SPAN_WARNING("It looks moderately damaged.") + else if (dam < 1) + . += SPAN_DANGER("It looks slightly damaged.") + + +/obj/structure/watchtower/attack_hand(mob/user) + if (stage < WATCHTOWER_STAGE_COMPLETE) + return + + if(get_turf(user) == locate(x, y-1, z)) + var/people_on_watchtower = 0 + + for(var/turf/current_turf in CORNER_BLOCK_OFFSET(src, 2, 1, 0, 1)) + for(var/mob/mob in current_turf.contents) + if(mob.stat != DEAD) + people_on_watchtower++ + + if(people_on_watchtower >= 2) + to_chat(user, SPAN_NOTICE("The watchtower is too crowded!")) + return + + if(!do_after(user, 3 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + + var/turf/actual_turf = locate(x, y+1, z) + if(people_on_watchtower < 1 && user.pulling) + user.pulling.forceMove(actual_turf) + on_enter(user.pulling) + + user.forceMove(actual_turf) + on_enter(user) + + + else if(get_turf(user) == locate(x, y+1, z)) + if(!do_after(user, 3 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + var/turf/actual_turf = locate(x, y-1, z) + if(user.pulling) + user.pulling.forceMove(actual_turf) + on_leave(user.pulling) + + user.forceMove(actual_turf) + on_leave(user) + +/obj/structure/watchtower/proc/on_enter(mob/user) + ADD_TRAIT(user, TRAIT_ON_WATCHTOWER, "watchtower") + if(user.client) + user.client.change_view(user.client.view + 2) + var/atom/movable/screen/plane_master/roof/roof_plane = user.hud_used.plane_masters["[ROOF_PLANE]"] + roof_plane?.invisibility = INVISIBILITY_MAXIMUM + add_trait_to_all_guns(user) + RegisterSignal(user, COMSIG_ITEM_PICKUP, PROC_REF(item_picked_up)) + RegisterSignal(user, COMSIG_LIVING_ZOOM_OUT, PROC_REF(on_unzoom)) + + if(isxeno(user)) + RegisterSignal(user, COMSIG_XENO_ENTER_TUNNEL, PROC_REF(on_tunnel)) + +/obj/structure/watchtower/proc/on_unzoom(mob/user) + SIGNAL_HANDLER + if(user.client) + user.client.change_view(user.client.view + 2) + +/obj/structure/watchtower/proc/on_tunnel() + SIGNAL_HANDLER + return COMPONENT_CANCEL_TUNNEL + +/obj/structure/watchtower/proc/on_leave(mob/user) + REMOVE_TRAIT(user, TRAIT_ON_WATCHTOWER, "watchtower") + if(user.client) + user.client.change_view(max(user.client.view - 2, 7)) + var/atom/movable/screen/plane_master/roof/roof_plane = user.hud_used.plane_masters["[ROOF_PLANE]"] + roof_plane?.invisibility = 0 + UnregisterSignal(user, COMSIG_ITEM_PICKUP) + UnregisterSignal(user, COMSIG_LIVING_ZOOM_OUT) + + if(isxeno(user)) + UnregisterSignal(COMSIG_XENO_ENTER_TUNNEL) + +/obj/structure/watchtower/proc/add_trait_to_all_guns(mob/user) + for(var/obj/item/weapon/gun/gun in user) + gun.add_bullet_traits(list(BULLET_TRAIT_ENTRY_ID("watchtower_arc", /datum/element/bullet_trait_direct_only/watchtower))) + + for(var/obj/item/storage/storage in user) + for(var/obj/item/weapon/gun/gun in storage.contents) + gun.add_bullet_traits(list(BULLET_TRAIT_ENTRY_ID("watchtower_arc", /datum/element/bullet_trait_direct_only/watchtower))) + +/obj/structure/watchtower/proc/item_picked_up(obj/item/picked_up_item, mob/living/carbon/human/user) + SIGNAL_HANDLER + if(!istype(picked_up_item, /obj/item/weapon/gun)) + return + + var/obj/item/weapon/gun/gun = picked_up_item + gun.add_bullet_traits(list(BULLET_TRAIT_ENTRY_ID("watchtower_arc", /datum/element/bullet_trait_direct_only/watchtower))) + +/obj/structure/watchtower/attack_alien(mob/living/carbon/xenomorph/xeno) + if(get_turf(xeno) == locate(x, y-1, z) && xeno.a_intent != INTENT_HARM && xeno.mob_size < MOB_SIZE_BIG && stage >= WATCHTOWER_STAGE_COMPLETE) + if(!do_after(xeno, 3 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE)) + return + + var/turf/actual_turf = locate(x, y+1, z) + xeno.forceMove(actual_turf) + on_enter(xeno) + else if(get_turf(xeno) == locate(x, y+1, z) && xeno.a_intent != INTENT_HARM && xeno.mob_size < MOB_SIZE_BIG && stage >= WATCHTOWER_STAGE_COMPLETE) + if(!do_after(xeno, 3 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_HOSTILE)) + return + + var/turf/actual_turf = locate(x, y-1, z) + xeno.forceMove(actual_turf) + on_leave(xeno) + else + xeno.animation_attack_on(src) + playsound(src, "alien_claw_metal", 25, TRUE) + update_health(rand(xeno.melee_damage_lower, xeno.melee_damage_upper)) + return XENO_ATTACK_ACTION + +// For Mappers +/obj/structure/watchtower/stage1 + stage = WATCHTOWER_STAGE_WELDED + icon_state = "stage1" +/obj/structure/watchtower/stage2 + stage = WATCHTOWER_STAGE_COLUMNS + icon_state = "stage2" +/obj/structure/watchtower/stage3 + stage = WATCHTOWER_STAGE_HEIGHTNED_WRENCH + icon_state = "stage3" +/obj/structure/watchtower/stage4 + stage = WATCHTOWER_STAGE_FLOOR + icon_state = "stage4" +/obj/structure/watchtower/stage5 + stage = WATCHTOWER_STAGE_BARRICADED + icon_state = "stage5" +/obj/structure/watchtower/stage6 + stage = WATCHTOWER_STAGE_ROOF_SUPPORT + icon_state = "stage6" +/obj/structure/watchtower/complete + stage = WATCHTOWER_STAGE_COMPLETE + icon_state = "stage7" diff --git a/colonialmarines.dme b/colonialmarines.dme index 85310035ef8b..660a912abefc 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -503,6 +503,7 @@ #include "code\datums\elements\suturing.dm" #include "code\datums\elements\yautja_tracked_item.dm" #include "code\datums\elements\bullet_trait\damage_boost.dm" +#include "code\datums\elements\bullet_trait\direct_only.dm" #include "code\datums\elements\bullet_trait\iff.dm" #include "code\datums\elements\bullet_trait\ignored_range.dm" #include "code\datums\elements\bullet_trait\incendiary.dm" @@ -2517,6 +2518,8 @@ #include "code\modules\vox\vox_tgui.dm" #include "code\modules\vox\vox_sounds\vox.dm" #include "code\modules\vox\vox_sounds\vox_military.dm" +#include "code\modules\watchtower\blockers.dm" +#include "code\modules\watchtower\watchtower.dm" #include "interface\fonts.dm" #include "interface\interface.dm" #include "interface\skin.dmf" diff --git a/icons/obj/structures/watchtower.dmi b/icons/obj/structures/watchtower.dmi new file mode 100644 index 000000000000..e3d62fd08d00 Binary files /dev/null and b/icons/obj/structures/watchtower.dmi differ