diff --git a/code/__DEFINES/__game.dm b/code/__DEFINES/__game.dm index 96ba47d7fd2a..aa07f952fea6 100644 --- a/code/__DEFINES/__game.dm +++ b/code/__DEFINES/__game.dm @@ -385,6 +385,8 @@ #define WALL_THICKRESIN "thickresin" #define WALL_WEEDBOUND_RESIN "weedboundresin" #define WALL_THICK_WEEDBOUND_RESIN "thickweedboundresin" +#define WALL_NODEBOUND_RESIN "nodeboundresin" +#define WALL_THICK_NODEBOUND_RESIN "thicknodeboundresin" #define WALL_MEMBRANE "membrane" #define WALL_THICKMEMBRANE "thickmembrane" #define WALL_BONE_RESIN "bone_resin" diff --git a/code/game/turfs/walls/wall_types.dm b/code/game/turfs/walls/wall_types.dm index f81475f0497c..da49d8eab73f 100644 --- a/code/game/turfs/walls/wall_types.dm +++ b/code/game/turfs/walls/wall_types.dm @@ -860,6 +860,7 @@ blend_turfs = list(/turf/closed/wall/resin) blend_objects = list(/obj/structure/mineral_door/resin) repair_materials = list() + var/destroy_override = FALSE var/hivenumber = XENO_HIVE_NORMAL var/should_track_build = FALSE var/upgrading_now = FALSE //flag to track upgrading/thickening process @@ -871,8 +872,9 @@ /turf/closed/wall/resin/Initialize(mapload) . = ..() - for(var/obj/effect/alien/weeds/node/weed_node in contents) - qdel(weed_node) + if(!destroy_override) + for(var/obj/effect/alien/weeds/node/weed_node in contents) + qdel(weed_node) if(hivenumber == XENO_HIVE_NORMAL) RegisterSignal(SSdcs, COMSIG_GLOB_GROUNDSIDE_FORSAKEN_HANDLING, PROC_REF(forsaken_handling)) diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index a427c8508f6c..af3b9e170550 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -587,7 +587,7 @@ CLIENT_VERB(toggle_custom_cursors) do_toggle_custom_cursors() /client/proc/do_toggle_custom_cursors(mob/user) - var/result = tgui_alert(user, "Do you want custom cursors enabled?", "Custom Cursors", list("Yes", "No", "Enable Main Cursor", "Disable Main Cursor")) + var/result = tgui_alert(user, "Do you want custom cursors enabled?", "Custom Cursors", list("Yes", "No", "Enable Main Cursor", "Disable Main Cursor", "Enable Ability Cursor", "Disable Ability Cursor")) if(!result) return switch(result) @@ -616,6 +616,15 @@ CLIENT_VERB(toggle_custom_cursors) to_chat(src, SPAN_NOTICE("You're no longer using custom cursors.")) mouse_pointer_icon = initial(mouse_pointer_icon) prefs.save_preferences() + if("Enable Ability Cursor") + prefs.custom_cursors = TRUE + to_chat(src, SPAN_NOTICE("Your ability cursor will now be visible.")) + prefs.save_preferences() + if("Disable Ability Cursor") + prefs.custom_cursors = FALSE + to_chat(src, SPAN_NOTICE("Your ability cursor will no longer be visible.")) + mouse_pointer_icon = initial(mouse_pointer_icon) + prefs.save_preferences() if("Enable Main Cursor") prefs.main_cursor = TRUE to_chat(src, SPAN_NOTICE("Your main cursor will now be customized.")) diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm index fa79e70115dd..924bc33d90aa 100644 --- a/code/modules/cm_aliens/weeds.dm +++ b/code/modules/cm_aliens/weeds.dm @@ -676,4 +676,32 @@ /obj/effect/resin_construct/transparent/weak icon_state = "WeakTransparentConstruct" +/obj/effect/resin_construct/fastweak + icon_state = "WeakReflectiveFast" + +/obj/effect/resin_construct/speed_node + icon_state = "speednode" + +/obj/effect/resin_construct/cost_node + icon_state = "costnode" + +/obj/effect/resin_construct/construct_node + icon_state = "constructnode" + +/obj/effect/resin_construct/construct_doorslow + icon_state = "BoundDoorSlow" + +/obj/effect/resin_construct/construct_wallslow + icon_state = "BoundWallSlow" + +/obj/effect/resin_construct/thickfast + icon_state = "ThickConstructFast" + +/obj/effect/resin_construct/thickdoorfast + icon_state = "ThickDoorConstructFast" + layer = FIREDOOR_CLOSED_LAYER + +/obj/effect/resin_construct/transparent/thickfast + icon_state = "WeakTransparentConstructFast" + #undef WEED_BASE_GROW_SPEED diff --git a/code/modules/mob/living/carbon/xenomorph/Powers.dm b/code/modules/mob/living/carbon/xenomorph/Powers.dm index aed2e719fd43..b9bc9ee208f5 100644 --- a/code/modules/mob/living/carbon/xenomorph/Powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/Powers.dm @@ -15,10 +15,14 @@ if(total_resin_cost > plasma_max && (XENO_RESIN_BASE_COST + resin_construct.cost) < plasma_max) total_resin_cost = plasma_max + var/turf/current_turf = get_turf(target) + if(action_busy && !can_stack_builds) return SECRETE_RESIN_FAIL if(!check_state()) return SECRETE_RESIN_FAIL + for(var/obj/effect/alien/resin/design/cost_node/cn in current_turf.contents) + total_resin_cost -= (total_resin_cost / 2) if(use_plasma && !check_plasma(total_resin_cost)) return SECRETE_RESIN_FAIL if(SSinterior.in_interior(src)) @@ -31,8 +35,6 @@ to_chat(src, SPAN_XENOWARNING("We've already built the maximum possible structures we can!")) return SECRETE_RESIN_FAIL - var/turf/current_turf = get_turf(target) - if(extra_build_dist != IGNORE_BUILD_DISTANCE && get_dist(src, target) > src.caste.max_build_dist + extra_build_dist) // Hivelords and eggsac carriers have max_build_dist of 1, drones and queens 0 to_chat(src, SPAN_XENOWARNING("We can't build from that far!")) return SECRETE_RESIN_FAIL @@ -103,9 +105,6 @@ for(var/obj/effect/alien/resin/design/speed_node/sn in current_turf.contents) wait_time -= ((resin_construct.build_time * caste.build_time_mult) / 2) - for(var/obj/effect/alien/resin/design/cost_node/cn in current_turf.contents) - total_resin_cost -= (total_resin_cost / 2) - if(!resin_construct.can_build_here(current_turf, src)) return SECRETE_RESIN_FAIL diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index eeac2255cbc8..a16ed8fa20f2 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -204,6 +204,8 @@ var/plasmapool_modifier = 1 var/plasmagain_modifier = 0 var/tackle_chance_modifier = 0 + var/tacklestrength_min_modifier = 0 + var/tacklestrength_max_modifier = 0 var/regeneration_multiplier = 1 var/speed_modifier = 0 var/phero_modifier = 0 @@ -312,11 +314,15 @@ var/obj/effect/alien/resin/fruit/selected_fruit = null var/list/built_structures = list() - // Designer stuff + /// the typepath of the designer placeable we wanna put down. var/obj/effect/alien/resin/design/selected_design = null + /// List of available design marks for this designer. var/list/available_design = list() + /// Stores the current design nodes placed by the designer. var/list/current_design = list() + /// Maximum design nodes the designer can place. var/max_design_nodes = 0 + /// Currently selected design mark to place var/selected_design_mark var/icon_xeno @@ -328,6 +334,9 @@ bubble_icon = "alien" + /// Custom action mouse cursor + var/active_action_cursor + ///////////////////////////////////////////////////////////////////// // // Phero related vars @@ -592,6 +601,30 @@ if(fire_immunity & FIRE_IMMUNITY_XENO_FRENZY) . |= COMPONENT_XENO_FRENZY +/mob/living/carbon/xenomorph/proc/set_action_cursor(mouse_pointer) + if(!client) + return + if(active_action_cursor) + UnregisterSignal(client, COMSIG_CLIENT_RESET_VIEW) + if(!client.prefs.custom_cursors) + return + active_action_cursor = mouse_pointer + client.mouse_pointer_icon = mouse_pointer + RegisterSignal(client, COMSIG_CLIENT_RESET_VIEW, PROC_REF(handle_view)) + +/mob/living/carbon/xenomorph/proc/clear_action_cursor() + if(!client) + return + active_action_cursor = null + client.mouse_pointer_icon = null + UnregisterSignal(client, COMSIG_CLIENT_RESET_VIEW) + +/mob/living/carbon/xenomorph/proc/handle_view(client/user) + SIGNAL_HANDLER + if(active_action_cursor) + if(user?.prefs?.custom_cursors) + user.mouse_pointer_icon = active_action_cursor + //Off-load this proc so it can be called freely //Since Xenos change names like they change shoes, we need somewhere to hammer in all those legos //We set their name first, then update their real_name AND their mind name @@ -924,8 +957,8 @@ tackle_min = caste.tackle_min tackle_max = caste.tackle_max tackle_chance = caste.tackle_chance + tackle_chance_modifier - tacklestrength_min = caste.tacklestrength_min - tacklestrength_max = caste.tacklestrength_max + tacklestrength_min = caste.tacklestrength_min + tacklestrength_min_modifier + tacklestrength_max = caste.tacklestrength_max + tacklestrength_max_modifier /mob/living/carbon/xenomorph/proc/recalculate_health() var/new_max_health = nocrit ? health_modifier + maxHealth : health_modifier + caste.max_health diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm index 134f1865d67d..f02e8abefb3e 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm @@ -8,6 +8,8 @@ */ /mob/living/carbon/xenomorph/proc/set_selected_ability(datum/action/xeno_action/activable/ability) + if(active_action_cursor && active_action_cursor != ability) + clear_action_cursor() if(selected_ability) selected_ability.on_deselect(src) if(!ability) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm index d00b9d83173c..e22dc8200d45 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm @@ -263,7 +263,14 @@ if(!SSmapping.same_z_map(target.z, xeno_owner.z)) to_chat(owner, SPAN_XENOWARNING("This area is too far away to affect!")) return + + var/build_on_design = FALSE + var/turf/target_turf = get_turf(target) + if(target_turf) + for(var/obj/effect/alien/resin/design/target_design in target_turf) + build_on_design = TRUE apply_cooldown() + switch(xeno_owner.build_resin(target, thick, make_message, plasma_cost != 0, build_speed_mod)) if(SECRETE_RESIN_INTERRUPT) if(xeno_cooldown) @@ -273,6 +280,10 @@ if(xeno_cooldown) apply_cooldown_override(xeno_cooldown_fail) return FALSE + if(SECRETE_RESIN_SUCCESS) + if(build_on_design) + apply_cooldown_override() + return TRUE return TRUE // leader Marker diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/hivelord/hivelord_macros.dm b/code/modules/mob/living/carbon/xenomorph/abilities/hivelord/hivelord_macros.dm new file mode 100644 index 000000000000..2bfeb63d02c7 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/abilities/hivelord/hivelord_macros.dm @@ -0,0 +1,28 @@ +/datum/action/xeno_action/verb/verb_change_design() + set category = "Alien" + set name = "Change Design Mark" + set hidden = TRUE + var/action_name = "Change Design Mark" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/place_design() + set category = "Alien" + set name = "Place Design" + set hidden = TRUE + var/action_name = "Place Design" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_toggle_design_icons() + set category = "Alien" + set name = "Change Design Mark" + set hidden = TRUE + var/action_name = "Change Design Mark" + handle_xeno_macro(src, action_name) + +/datum/action/xeno_action/verb/verb_greater_surge() + set category = "Alien" + set name = "Greater Resin Surge" + set hidden = TRUE + var/action_name = "Greater Resin Surge" + handle_xeno_macro(src, action_name) + diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/designer.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/designer.dm index 6230452f25ed..36c6d5abbc78 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/designer.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/designer.dm @@ -1,7 +1,7 @@ /datum/xeno_strain/designer name = HIVELORD_DESIGNER - description = "You give up direct resin building, lose some plasma and health, but gain stronger pheromones and longer vision. You can place up to 36 design nodes: optimized nodes boost building by 50%, flexible nodes reduce plasma cost by 50%, and construct nodes allow anyone to donate plasma to build weedbound resin walls or doors, even on surfaces where we can't normally build. Some castes like hivelord, carrier, burrower and queen can stimulate construct nodes to make thick weedbound variant including plasma fruit. You can mark nodes as walls or doors, remotely thicken structures, control doors, and remove nodes. Using Greater Resin Surge turns all design nodes into weaker reflective walls for temporary hive defense. Your tackle is slightly stronger, causing longer knockdowns." - flavor_description = "You are hive's designer, while you no longer build with your own claws, your influence shapes the very foundation of the swarm, allowing it to expand and adapt beyond limits." + description = "You give up direct resin building, lose some plasma and health, but gain stronger pheromones and longer vision. You can place up to 36 design nodes: optimized nodes boost building by 50%, flexible nodes reduce plasma cost by 50%, and construct nodes allow anyone to donate plasma to build weedbound resin walls or doors, even on surfaces where we can't normally build. Some castes like hivelord, carrier, burrower and queen can stimulate construct nodes to make thick weedbound variant including gardener drone. You can mark nodes as walls or doors, remotely thicken structures, control doors, and remove nodes. Using Greater Resin Surge turns all design nodes into weaker reflective walls for temporary hive defense. Your tackle is slightly stronger, causing longer knockdowns." + flavor_description = "You are hive's designer, while you no longer build with your own claws, your influence shapes the very foundation of the swarm, allowing it to expand beyond limits." icon_state_prefix = "Designer" actions_to_remove = list( @@ -31,17 +31,15 @@ /obj/effect/alien/resin/design/upgrade, /obj/effect/alien/resin/design/remove, ) - hivelord.selected_design = /obj/effect/alien/resin/design/speed_node hivelord.selected_design_mark = /datum/design_mark/resin_wall hivelord.plasma_types = list(PLASMA_NUTRIENT, PLASMA_PHEROMONE) hivelord.max_design_nodes = 36 hivelord.viewsize = WHISPERER_VIEWRANGE hivelord.health_modifier -= XENO_HEALTH_MOD_LARGE hivelord.phero_modifier += XENO_PHERO_MOD_LARGE - hivelord.speed_modifier += XENO_SPEED_TIER_3 //Lost 30% plasma in sac, you lost some weight hivelord.plasmapool_modifier = 0.7 //-30% plasma pool - hivelord.tacklestrength_min = 5 - hivelord.tacklestrength_max = 6 + hivelord.tacklestrength_min_modifier += 1 + hivelord.tacklestrength_max_modifier += 1 hivelord.recalculate_everything() // Also change the primacy value for our abilities (because we want the same place but have another primacy ability) @@ -63,52 +61,6 @@ . = list() . += "Nodes sustained: [length(bound_xeno.current_design)] / [bound_xeno.max_design_nodes]" -// ""animations"" (effects) -/obj/effect/resin_construct/fastweak - icon_state = "WeakReflectiveFast" - -/obj/effect/resin_construct/speed_node - icon_state = "speednode" - -/obj/effect/resin_construct/cost_node - icon_state = "costnode" - -/obj/effect/resin_construct/construct_node - icon_state = "constructnode" - -/obj/effect/resin_construct/construct_doorslow - icon_state = "DoorConstrucSlow" - -/obj/effect/resin_construct/construct_wallslow - icon_state = "WeakConstructSlow" - -/obj/effect/resin_construct/thickfast - icon_state = "ThickConstructFast" - -/obj/effect/resin_construct/thickdoorfast - icon_state = "ThickDoorConstructFast" - layer = FIREDOOR_CLOSED_LAYER - -/obj/effect/resin_construct/transparent/thickfast - icon_state = "WeakTransparentConstructFast" - -//Marks - -/datum/design_mark - var/name = "xeno_declare" - var/icon_state = "empty" - var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to each other." - -/datum/design_mark/resin_wall - name = "Resin Wall" - desc = "Place resin wall here!" - icon_state = "mark_wall" - -/datum/design_mark/resin_door - name = "Resin Door" - desc = "Place resin door here!" - icon_state = "mark_door" - // Far-sight /datum/action/xeno_action/onclick/toggle_long_range/designer handles_movement = FALSE @@ -116,1149 +68,1341 @@ ability_primacy = XENO_NOT_PRIMARY_ACTION delay = 0 -////////////////////////// -/// Designer... Paths. /// -////////////////////////// - -/obj/effect/alien/resin/design - name = "Design Node" - desc = "A weird node, it looks mutated." - icon = 'icons/mob/xenos/effects.dmi' - icon_state = "static_speednode" - density = FALSE - opacity = FALSE - layer = RESIN_UNDER_STRUCTURE_LAYER - plane = FLOOR_PLANE - health = HEALTH_RESIN_XENO_STICKY +/datum/action/xeno_action/proc/update_mouse_pointer() + var/mob/living/carbon/xenomorph/xeno = owner - var/datum/design_mark/mark_meaning = /datum/design_mark/resin_wall - var/mob/living/carbon/xenomorph/bound_xeno - var/obj/effect/alien/weeds/bound_weed - var/hivenumber = XENO_HIVE_NORMAL - var/plasma_cost = 0 + if(xeno.selected_design == /obj/effect/alien/resin/design/speed_node) + if(xeno.selected_design_mark == /datum/design_mark/resin_wall) + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/spd_wall_mouse.dmi') + else + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/spd_door_mouse.dmi') + return - var/image/chosenMark + if(xeno.selected_design == /obj/effect/alien/resin/design/cost_node) + if(xeno.selected_design_mark == /datum/design_mark/resin_wall) + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/cost_wall_mouse.dmi') + else + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/cost_door_mouse.dmi') + return -/obj/effect/alien/resin/design/Initialize(mapload, obj/effect/alien/weeds/weeds, mob/living/carbon/xenomorph/xeno) - if(!istype(xeno)) - return INITIALIZE_HINT_QDEL + if(xeno.selected_design == /obj/effect/alien/resin/design/construct_node) + if(xeno.selected_design_mark == /datum/design_mark/resin_wall) + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/const_wall_mouse.dmi') + else + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/const_door_mouse.dmi') + return - bound_xeno = xeno - bound_weed = weeds - hivenumber = xeno.hivenumber +////////////////////////// +/// Change Design /// +////////////////////////// - RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - RegisterSignal(bound_xeno, COMSIG_PARENT_QDELETING, PROC_REF(on_xeno_expire)) +/datum/action/xeno_action/onclick/change_design + name = "Choose Action" + action_icon_state = "static_speednode" + plasma_cost = 0 + xeno_cooldown = 0 + macro_path = /datum/action/xeno_action/verb/verb_change_design + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_2 - set_hive_data(src, hivenumber) - . = ..() +/datum/action/xeno_action/onclick/change_design/use_ability(atom/Atom) + var/mob/living/carbon/xenomorph/xeno = owner + if(!xeno.check_state()) + return - if(bound_weed.hivenumber != bound_xeno.hivenumber) - qdel(src) + var/static/list/options = list( + "Optimized Node (50)" = icon(/datum/action/xeno_action::icon_file, "static_speednode"), + "Construct Node (50)" = icon(/datum/action/xeno_action::icon_file, "static_constructnode"), + "Thicken Resin (60)" = icon(/datum/action/xeno_action::icon_file, "upgrade_resin"), + "Open Old UI" = icon(/datum/action/xeno_action::icon_file, "open_ui"), + "Remove Node" = icon(/datum/action/xeno_action::icon_file, "remove_node"), + "Flexible Node (50)" = icon(/datum/action/xeno_action::icon_file, "static_costnode") + ) - if(xeno.selected_design_mark) - mark_meaning = new xeno.selected_design_mark + var/choice + if(owner.client.prefs.no_radials_preference) + choice = tgui_input_list(owner, "Choose Desing Option", "Pick", options, theme="hive_status") + else + choice = show_radial_menu(owner, owner?.client.get_eye(), options, radius = 50) - var/datum/hive_status/hive = GLOB.hive_datum[hivenumber] - if(hive) - hive.designer_marks += src - if(mark_meaning) - var/icon_state_to_use = get_marker_icon_state() - if(icon_state_to_use) - chosenMark = image(icon, src, icon_state_to_use, ABOVE_HUD_LAYER, "pixel_y" = 5) - chosenMark.plane = ABOVE_HUD_PLANE - chosenMark.appearance_flags = RESET_COLOR + var/des = FALSE + var/rem = FALSE + plasma_cost = 0 + switch(choice) + if("Optimized Node (50)") + xeno.selected_design = /obj/effect/alien/resin/design/speed_node + des = TRUE + if("Flexible Node (50)") + xeno.selected_design = /obj/effect/alien/resin/design/cost_node + des = TRUE + if("Construct Node (50)") + xeno.selected_design = /obj/effect/alien/resin/design/construct_node + des = TRUE + if("Thicken Resin (60)") + xeno.selected_design = /obj/effect/alien/resin/design/upgrade + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/upgrade_mouse.dmi') + rem = TRUE + if("Remove Node") + xeno.selected_design = /obj/effect/alien/resin/design/remove + xeno.set_action_cursor('icons/effects/mouse_pointer/designer/remove_mouse.dmi') + rem = TRUE + if("Open Old UI") + tgui_interact(xeno) - for(xeno in hive.totalXenos) - if(xeno.client) - xeno.hud_set_design_marks() - refresh_marker() + if(des) + to_chat(xeno, SPAN_NOTICE("We will now build [xeno.selected_design.name].")) + if(rem) + to_chat(xeno, SPAN_NOTICE("We will now remotely [xeno.selected_design.name].")) -/obj/effect/alien/resin/design/Destroy() - var/datum/hive_status/hive = GLOB.hive_datum[hivenumber] - if(hive) - hive.designer_marks -= src - for(var/mob/living/carbon/xenomorph/xeno in hive.totalXenos) - if(xeno.client && chosenMark) - xeno.client.images -= chosenMark - xeno.hud_set_design_marks() + update_mouse_pointer() + xeno.update_icons() + button.overlays.Cut() + button.overlays += image(icon_file, button, xeno.selected_design.icon_state) - if(!QDELETED(bound_xeno)) - bound_xeno.current_design.Remove(src) - unregister_weed_expiration_signal_design() - bound_xeno = null - bound_weed = null - chosenMark = null return ..() -/obj/effect/alien/resin/design/proc/refresh_marker() - if(!chosenMark || !mark_meaning) - return +// Below is UI for old players. - if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) - chosenMark.icon_state = mark_meaning.icon_state +/datum/action/xeno_action/onclick/change_design/give_to(mob/living/carbon/xenomorph/xeno) + . = ..() -/obj/effect/alien/resin/design/proc/get_marker_icon_state() - if(!mark_meaning) - return null - return mark_meaning.icon_state + if(!xeno.selected_design) + xeno.selected_design = /obj/effect/alien/resin/design/speed_node -/obj/effect/alien/resin/design/proc/on_weed_expire() - SIGNAL_HANDLER - qdel(src) + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, initial(xeno.selected_design.icon_state)) + button.overlays += image(icon_file, button, action_icon_state) -/obj/effect/alien/resin/design/proc/on_xeno_expire() - SIGNAL_HANDLER - qdel(src) +/datum/action/xeno_action/onclick/change_design/ui_assets(mob/user) + return list(get_asset_datum(/datum/asset/spritesheet/choose_design)) -/obj/effect/alien/resin/design/proc/check_hivenumber_match() - if(!bound_weed || !bound_xeno) - visible_message(SPAN_XENOWARNING("The node shudders and decays back into the weeds.")) - qdel(src) - else if(bound_weed.hivenumber != bound_xeno.hivenumber) - visible_message(SPAN_XENOWARNING("The node withers away.")) - qdel(src) +/datum/action/xeno_action/onclick/change_design/ui_static_data(mob/user) + var/mob/living/carbon/xenomorph/xeno = user + if(!istype(xeno)) + return -/obj/effect/alien/resin/design/proc/unregister_weed_expiration_signal_design() - if(bound_weed) - UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) + . = list() -/obj/effect/alien/resin/design/proc/register_weed_expiration_signal_design(obj/effect/alien/weeds/new_weed) - RegisterSignal(new_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - bound_weed = new_weed - check_hivenumber_match() + var/list/design_list = list() + for(var/obj/effect/alien/resin/design/design as anything in xeno.available_design) + var/list/entry = list() -/obj/effect/alien/resin/design/proc/hud_set_queen_overwatch() - return + entry["name"] = initial(design.name) + entry["desc"] = initial(design.desc) + entry["image"] = replacetext(initial(design.icon_state), " ", "-") + entry["id"] = "[design]" + design_list += list(entry) -/obj/effect/alien/resin/design/speed_node - name = "Design Optimized Node (50)" - icon_state = "static_speednode" - plasma_cost = 50 + .["design"] = design_list -/obj/effect/alien/resin/design/speed_node/refresh_marker() - if(!chosenMark || !mark_meaning) +/datum/action/xeno_action/onclick/change_design/ui_data(mob/user) + var/mob/living/carbon/xenomorph/xeno = user + if(!istype(xeno)) return - if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) - chosenMark.icon_state = mark_meaning.icon_state + "_speed" - else - ..() + . = list() + .["selected_design"] = xeno.selected_design -/obj/effect/alien/resin/design/speed_node/get_marker_icon_state() - if(!mark_meaning) - return null - return mark_meaning.icon_state + "_speed" +/datum/action/xeno_action/onclick/change_design/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChooseDesign", "Choose Design") + ui.set_autoupdate(FALSE) + ui.open() -/obj/effect/alien/resin/design/speed_node/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this node looks like it has a big green oozing bulb at its center, making the weeds under it twitch...") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that building on top of this node will speed up your construction speed by [SPAN_BOLDNOTICE("50%")].") +/datum/action/xeno_action/onclick/change_design/Destroy() + SStgui.close_uis(src) + return ..() -/obj/effect/alien/resin/design/cost_node - name = "Design Flexible Node (60)" - icon_state = "static_costnode" - plasma_cost = 60 +/datum/action/xeno_action/onclick/change_design/ui_state(mob/user) + return GLOB.always_state -/obj/effect/alien/resin/design/cost_node/refresh_marker() - if(!chosenMark || !mark_meaning) +/datum/action/xeno_action/onclick/change_design/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) return - if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) - chosenMark.icon_state = mark_meaning.icon_state + "_cost" - else - ..() + var/mob/living/carbon/xenomorph/xeno = ui.user + if(!istype(xeno)) + return -/obj/effect/alien/resin/design/cost_node/get_marker_icon_state() - if(!mark_meaning) - return null - return mark_meaning.icon_state + "_cost" + switch(action) + if("choose_design") + var/selected_type = text2path(params["type"]) + if(!(selected_type in xeno.available_design)) + return -/obj/effect/alien/resin/design/cost_node/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this node looks like its made of smaller blue bulbs grown together, making the weeds under them look soft and squishy.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that building on top of this node will decrease plasma cost of basic resin structures by [SPAN_BOLDNOTICE("50%")].") + var/obj/effect/alien/resin/design/design = selected_type + to_chat(xeno, SPAN_NOTICE("We will now build [initial(design.name)] when designing.")) + //update the button's overlay with new choice + xeno.update_icons() + button.overlays.Cut() + button.overlays += image(icon_file, button, action_icon_state) + button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, initial(design.icon_state)) + xeno.selected_design = selected_type + . = TRUE -/obj/effect/alien/resin/design/construct_node - name = "Design Construct Node (70)" - icon_state = "static_constructnode" - plasma_cost = 70 - var/plasma_donation = 70 - var/building = FALSE - var/thick_build = FALSE - var/obj/effect/resin_construct/build_overlay + if("refresh_ui") + . = TRUE -/obj/effect/alien/resin/design/construct_node/Initialize(mapload) +///////////////////////////// +/// Place Design /// +///////////////////////////// + +/datum/action/xeno_action/activable/place_design + name = "Influence" + action_icon_state = "secrete_resin" + plasma_cost = 0 + macro_path = /datum/action/xeno_action/verb/place_design + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_3 + xeno_cooldown = 0 + /// How far we can reach with remote design placement. + var/max_reach = 10 + /// Toggle state for design icon. + var/design_toggle = TRUE + +/datum/action/xeno_action/activable/place_design/action_activate() . = ..() - var/area/area = get_area(src) - if(area) - if(area.linked_lz) - AddComponent(/datum/component/resin_cleanup) - area.current_resin_count++ + update_mouse_pointer() -/obj/effect/alien/resin/design/construct_node/proc/complete_construction(turf/Turf, design_mark, mob/living/carbon/xenomorph/xeno) - if(QDELETED(src) || QDELETED(Turf)) +/datum/action/xeno_action/activable/place_design/use_ability(atom/target_atom, mods, use_plasma = TRUE, message = TRUE) + var/mob/living/carbon/xenomorph/xeno = owner + if(!can_remote_build()) + to_chat(owner, SPAN_XENONOTICE("We must be standing on weeds to channel our nutrients and influence.")) return - if(build_overlay && !QDELETED(build_overlay)) - qdel(build_overlay) - build_overlay = null - - building = FALSE + if(!action_cooldown_check()) + return - if(istype(design_mark, /datum/design_mark/resin_wall)) - if(!istype(Turf, /turf/closed/wall)) - var/turf/placed - if(thick_build) - placed = Turf.place_on_top(/turf/closed/wall/resin/weedbound/thick) - else - placed = Turf.place_on_top(/turf/closed/wall/resin/weedbound/normal) + if(!xeno.check_state()) + return - var/turf/closed/wall/resin/Res = get_turf(Turf) - if(istype(Res)) - Res.hivenumber = src.hivenumber - set_hive_data(Res, Res.hivenumber) + if(mods["click_catcher"]) + return - to_chat(xeno, SPAN_NOTICE("We create a weedbound wall.")) - playsound(placed, "alien_resin_build", 25) - else - to_chat(xeno, SPAN_WARNING("A wall already exists here.")) + if(ismob(target_atom)) + if(!can_see(xeno, target_atom, max_reach)) + to_chat(xeno, SPAN_XENODANGER("We cannot see that location!")) + return + else + if(get_dist(xeno, target_atom) > max_reach) + to_chat(xeno, SPAN_WARNING("That's too far away!")) + return - else if(istype(design_mark, /datum/design_mark/resin_door)) - if(!istype(Turf, /obj/structure/mineral_door)) - var/obj/new_structure - if(thick_build) - new_structure = new /obj/structure/mineral_door/resin/weedbound/thick(Turf) - else - new_structure = new /obj/structure/mineral_door/resin/weedbound/normal(Turf) + var/turf/target_turf = get_turf(target_atom) + if(!istype(target_turf)) + to_chat(xeno, SPAN_WARNING("We cannot design without weeds.")) + return - var/obj/structure/mineral_door/resin/Res = locate(/obj/structure/mineral_door/resin) in get_turf(Turf) - if(istype(Res)) - Res.hivenumber = src.hivenumber - set_hive_data(Res, Res.hivenumber) + var/obj/effect/alien/weeds/target_weeds = locate(/obj/effect/alien/weeds) in target_turf + if(!target_weeds) + to_chat(xeno, SPAN_WARNING("There are no weeds to create a connection!")) + return - to_chat(xeno, SPAN_NOTICE("We create a weedbound door.")) - playsound(new_structure, "alien_resin_build", 25) - else - to_chat(xeno, SPAN_WARNING("A door already exists here.")) + if(target_weeds.hivenumber != xeno.hivenumber) + to_chat(xeno, SPAN_WARNING("These weeds do not belong to our hive; they reject our influence.")) + return - qdel(src) + var/plasma_cost + if(xeno.selected_design && xeno.selected_design.plasma_cost) + plasma_cost = xeno.selected_design.plasma_cost -/obj/effect/alien/resin/design/construct_node/Destroy() - if(build_overlay && !QDELETED(build_overlay)) - qdel(build_overlay) - if(bound_weed) - unregister_weed_expiration_signal_design() - var/area/area = get_area(src) - area?.current_resin_count-- - return ..() + if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/upgrade)) + if(!(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane) || istype(target_atom, /obj/structure/mineral_door/resin))) + to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin walls, membrane and doors!")) + return -/obj/effect/alien/resin/design/construct_node/attack_hand(mob/user) - if(!isxeno(user)) - to_chat(user, SPAN_WARNING("You don't understand how to interact with this strange node.")) - return + if(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane)) + var/turf/closed/wall/resin/wall = target_atom - var/mob/living/carbon/xenomorph/xeno = user + if(wall.hivenumber != xeno.hivenumber) + to_chat(xeno, SPAN_XENOWARNING("[wall] does not belong to our hive!")) + return - if(!can_begin_construction(xeno)) - return + if(wall.upgrading_now) //<--- Prevent spam and waste of plasma + to_chat(xeno, SPAN_WARNING("This wall is already being reinforced!")) + return - var/total_plasma_cost = get_total_plasma_cost(xeno) - if(xeno.plasma_stored < total_plasma_cost) - to_chat(xeno, SPAN_WARNING("You lack the plasma to feed this node. [xeno.plasma_stored]/[total_plasma_cost]")) - return + wall.upgrading_now = TRUE - xeno.plasma_stored -= total_plasma_cost - to_chat(xeno, SPAN_NOTICE("You activate the node, it latches onto us and it forcefully consumes [total_plasma_cost] of our plasma.")) + if(wall.type == /turf/closed/wall/resin) + var/obj/thick_wall = new /obj/effect/resin_construct/thickfast(target_turf, src, xeno) + if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) + qdel(thick_wall) + wall.upgrading_now = FALSE + return + qdel(thick_wall) + wall.ChangeTurf(/turf/closed/wall/resin/thick) - begin_construction(xeno) + else if(wall.type == /turf/closed/wall/resin/membrane) + var/obj/thick_membrane = new /obj/effect/resin_construct/transparent/thickfast(target_turf, src, xeno) + if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) + qdel(thick_membrane) + wall.upgrading_now = FALSE + return + qdel(thick_membrane) + wall.ChangeTurf(/turf/closed/wall/resin/membrane/thick) + else + to_chat(xeno, SPAN_XENOWARNING("[wall] can't be made thicker.")) + return -/obj/effect/alien/resin/design/construct_node/attackby(obj/item/item, mob/user) - if(isxeno(user) && user.a_intent != INTENT_HARM) - if(istype(item, /obj/item/reagent_container/food/snacks/resin_fruit/plasma)) - to_chat(user, SPAN_NOTICE("We squeeze plasma fruit juices on node, activating its growth.")) - qdel(item) - thick_build = TRUE + wall.upgrading_now = FALSE - var/mob/living/carbon/xenomorph/xeno = user - begin_construction(xeno) - return + else if(istype(target_atom, /obj/structure/mineral_door/resin)) + var/obj/structure/mineral_door/resin/door = target_atom - //Do NOT call attack_hand here — that bypasses destruction - to_chat(user, SPAN_NOTICE("You examine the node curiously, but nothing happens.")) - return + if(door.hivenumber != xeno.hivenumber) + to_chat(xeno, SPAN_XENOWARNING("[door] does not belong to your hive!")) + return - . = ..() + if(door.upgrading_now) + to_chat(xeno, SPAN_WARNING("This door is already being reinforced!")) + return -/obj/effect/alien/resin/design/construct_node/attack_alien(mob/living/carbon/xenomorph/xeno) - if(xeno.a_intent != INTENT_HARM) - return attack_hand(xeno) - else - return ..() + if(door.hardness == 1.5) + door.upgrading_now = TRUE + var/obj/thick_door = new /obj/effect/resin_construct/thickdoorfast(target_turf, src, xeno) + if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) + qdel(thick_door) + door.upgrading_now = FALSE + return + qdel(thick_door) + var/oldloc = door.loc + qdel(door) + new /obj/structure/mineral_door/resin/thick(oldloc, door.hivenumber) + else + if(xeno.try_toggle_resin_door(door)) + if(!check_and_use_plasma_owner()) + return TRUE + return + return -/obj/effect/alien/resin/design/construct_node/proc/get_total_plasma_cost(mob/living/carbon/xenomorph/xeno) - var/area/target_area = get_area(get_turf(src)) - var/total_plasma_cost = plasma_donation + else + to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin structures!")) + return - if(target_area && target_area.openable_turf_count) - var/density_ratio = target_area.current_resin_count / target_area.openable_turf_count - if(density_ratio > 0.4) - total_plasma_cost = ceil(total_plasma_cost * (density_ratio + 0.35) * 2) - if(total_plasma_cost > xeno.plasma_max && (XENO_RESIN_BASE_COST + plasma_donation) < xeno.plasma_max) - total_plasma_cost = xeno.plasma_max + if(!check_and_use_plasma_owner(plasma_cost)) + return - return total_plasma_cost + xeno.visible_message(SPAN_XENONOTICE("Weeds around [target_atom] start to twitch and pump substance towards it, thickening it in process!"), + SPAN_XENONOTICE("We start to channel nutrients towards [target_atom], using [plasma_cost] plasma."), null, 5) + playsound(target_atom, "alien_resin_build", 25) -/obj/effect/alien/resin/design/construct_node/proc/can_begin_construction(mob/living/carbon/xenomorph/xeno) - if(building) - to_chat(xeno, SPAN_WARNING("This node is already being infused with plasma.")) - return FALSE + target_atom.add_hiddenprint(xeno) //Tracks who reinforced it for admins + return TRUE - if(xeno.hivenumber != src.hivenumber) - to_chat(xeno, SPAN_WARNING("This construct node does not belong to your hive.")) - return FALSE + if(xeno.try_toggle_resin_door(target_atom)) + if(!check_and_use_plasma_owner()) + return TRUE + return - if(!mark_meaning) - to_chat(xeno, SPAN_WARNING("This node has no valid design selected.")) - return FALSE + if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/remove)) + var/obj/effect/alien/resin/design/target_node = locate(/obj/effect/alien/resin/design) in target_turf + if(!target_node) + to_chat(xeno, SPAN_XENOWARNING("There is no resin node here to remove!")) + return - var/turf/Turf = get_turf(src) - if(!istype(Turf)) - to_chat(xeno, SPAN_WARNING("This is not a valid location.")) - return FALSE + if(target_node.hivenumber != xeno.hivenumber) + to_chat(xeno, SPAN_XENOWARNING("This node does not belong to your hive!")) + return - return TRUE + if(target_node.bound_xeno != xeno) + to_chat(xeno, SPAN_XENOWARNING("We cannot remove a node placed by another sister!")) + return -/obj/effect/alien/resin/design/construct_node/proc/begin_construction(mob/living/carbon/xenomorph/xeno) - if(!can_begin_construction(xeno)) + qdel(target_node) + to_chat(xeno, SPAN_XENONOTICE("We sever the bond to the node, causing it to dissolve into the ground.")) + playsound(xeno.loc, "alien_resin_move2", 25) return - var/turf/Turf = get_turf(src) - building = TRUE - - var/obj/effect/resin_construct/overlay + if(length(xeno.current_design) >= xeno.max_design_nodes) //Check if there are more nodes than length that was defined + to_chat(xeno, SPAN_XENOWARNING("We cannot sustain another node, one will wither away to allow this one to live!")) + var/obj/effect/alien/resin/design/old_design = xeno.current_design[1] //Check with node is first for deletion on list + xeno.current_design.Remove(old_design) //Removes first node stored inside list + qdel(old_design) //Delete node. - if(istype(mark_meaning, /datum/design_mark/resin_wall)) - overlay = new /obj/effect/resin_construct/construct_wallslow(Turf) - else if(istype(mark_meaning, /datum/design_mark/resin_door)) - overlay = new /obj/effect/resin_construct/construct_doorslow(Turf) + var/selected_design = xeno.selected_design - build_overlay = overlay + var/obj/effect/alien/resin/design/existing_node = locate(/obj/effect/alien/resin/design) in target_turf + if(existing_node && xeno.selected_design_mark) + if(!istype(existing_node.mark_meaning, xeno.selected_design_mark)) + existing_node.mark_meaning = new xeno.selected_design_mark + existing_node.refresh_marker() + to_chat(xeno, SPAN_XENONOTICE("We reshape the meaning of our node.")) + return - if(bound_weed.weed_strength >= WEED_LEVEL_HIVE) - thick_build = TRUE + if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/speed_node)) //Check path you selected from list + if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) + to_chat(src, SPAN_WARNING("There's something built here already.")) + return + var/obj/speed_warn = new /obj/effect/resin_construct/speed_node(target_turf, src, xeno) //Create "Animation" overlay + if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) + qdel(speed_warn) //Delete "Animation" overlay after defined time + return + qdel(speed_warn) //Delete again just in case overlay don't get deleted + if(!is_turf_clean(target_turf)) //Recheck the turf again just in case + to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) + return + if(!check_and_use_plasma_owner(plasma_cost)) + return + xeno.visible_message(SPAN_XENONOTICE("\The [xeno] channels nutrients and shapes it into a node!")) + var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) //Create node you selected from list + if(!design) + to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find node to place! Contact a coder!")) + return + playsound(xeno.loc, "alien_resin_build", 25) + xeno.current_design.Add(design) //Add Node to list. - if((xeno.caste_type in XENO_CONSTRUCT_NODE_BOOST) && !istype(xeno.strain, /datum/xeno_strain/designer)) - thick_build = TRUE + if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/cost_node)) + if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) + to_chat(src, SPAN_WARNING("There's something built here already.")) + return + var/obj/cost_warn = new /obj/effect/resin_construct/cost_node(target_turf, src, xeno) + if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) + qdel(cost_warn) + return + qdel(cost_warn) + if(!is_turf_clean(target_turf)) + to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) + return + if(!check_and_use_plasma_owner(plasma_cost)) + return + xeno.visible_message(SPAN_XENONOTICE("The [xeno] channels nutrients and shapes it into a node!")) + var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) + if(!design) + to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find placeholder to place! Contact a coder!")) + return + playsound(xeno.loc, "alien_resin_build", 25) + xeno.current_design.Add(design) - addtimer(CALLBACK(src, PROC_REF(complete_construction), Turf, mark_meaning, xeno), 4 SECONDS) + if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/construct_node)) + if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) + to_chat(src, SPAN_WARNING("There's something built here already.")) + return + if(!xeno.check_alien_construction(target_turf, check_doors = FALSE)) + return FALSE + var/obj/const_warn = new /obj/effect/resin_construct/construct_node(target_turf, src, xeno) + if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) + qdel(const_warn) + return + qdel(const_warn) + if(!is_turf_clean(target_turf)) + to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) + return + if(!check_and_use_plasma_owner(plasma_cost)) + return + xeno.visible_message(SPAN_XENONOTICE("The [xeno] channels nutrients and shapes it into a node!")) + var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) + if(!design) + to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find placeholder to place! Contact a coder!")) + return + playsound(xeno.loc, "alien_resin_build", 25) + xeno.current_design.Add(design) + apply_cooldown() + return ..() -/obj/effect/alien/resin/design/construct_node/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this node looks like big blub composed of smaller purple glowing cups, pumping some strange liquid trough weeds.") - if(isxeno(user) || isobserver(user)) - var/mob/living/carbon/xenomorph/xeno = user - var/total_plasma_cost = get_total_plasma_cost(xeno) - . += SPAN_NOTICE("You sense that feeding [SPAN_BOLDNOTICE("[total_plasma_cost]")] plasma with our hand to this node will secrete a [SPAN_BOLDNOTICE("[mark_meaning]")], you also heard that using plasma fruit works too.") +/datum/action/xeno_action/activable/place_design/proc/can_remote_build() + if(!locate(/obj/effect/alien/weeds) in get_turf(owner)) + return FALSE + return TRUE -//Should not be upgradable because it's not "stable" but special actions should create thick variant -/turf/closed/wall/resin/weedbound //NEVER use this variant, use subtypes - name = "weedbound resin wall" - desc = "An oddly solidified resin wall with a layered pattern that reminds you of flower buds." - icon_state = "weedboundresin" - walltype = WALL_WEEDBOUND_RESIN +/mob/living/carbon/xenomorph/proc/try_toggle_resin_door(atom/target_atom) + if(!istype(target_atom, /obj/structure/mineral_door/resin)) + return FALSE - var/obj/effect/alien/weeds/bound_weed - var/old_hivenumber + var/obj/structure/mineral_door/resin/resin_door = target_atom -/turf/closed/wall/resin/weedbound/Initialize() - . = ..() - bound_weed = locate(/obj/effect/alien/weeds) in get_turf(src) - if(!bound_weed) - addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 3 SECONDS) + if(resin_door.hivenumber != hivenumber) + to_chat(src, SPAN_XENOWARNING("This door does not belong to our hive!")) + return TRUE + + if(resin_door.TryToSwitchState(src)) + if(resin_door.open) + to_chat(src, SPAN_XENONOTICE("We focus our connection to the resin and remotely close the resin door.")) + else + to_chat(src, SPAN_XENONOTICE("We focus our connection to the resin and remotely open the resin door.")) + + return TRUE + +/datum/action/xeno_action/activable/place_design/proc/is_turf_clean(turf/current_turf, check_resin_additions = FALSE, check_doors = FALSE, check_resin_doors = FALSE) + var/has_obstacle = FALSE + for(var/obj/target in current_turf) + if(check_doors) + if(istype(target, /obj/structure/machinery/door)) + to_chat(src, SPAN_WARNING("[target] is blocking the resin! There's not enough space to build that here.")) + return FALSE + if(check_resin_additions) + if(istype(target, /obj/effect/alien/resin/sticky) || istype(target, /obj/effect/alien/resin/spike) || istype(target, /obj/effect/alien/resin/sticky/fast)) + has_obstacle = TRUE + to_chat(src, SPAN_WARNING("[target] is blocking the resin!")) + return FALSE + if(check_resin_doors) + if(istype(target, /obj/structure/mineral_door/resin)) + to_chat(src, SPAN_WARNING("[target] is blocking the resin node! There's not enough space to build that here.")) + return FALSE + if(current_turf.density || has_obstacle || locate(/obj/effect/alien/resin/design) in current_turf) + return FALSE + return TRUE + +/////////////////////////////// +/// Change Node Marker /// +/////////////////////////////// + +/datum/action/xeno_action/onclick/toggle_design_icons + name = "Change Design Mark" + action_icon_state = "design_mark_1" + plasma_cost = 0 + macro_path = /datum/action/xeno_action/verb/verb_toggle_design_icons + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_4 + +/datum/action/xeno_action/onclick/toggle_design_icons/can_use_action() + var/mob/living/carbon/xenomorph/xeno = owner + if(xeno && !xeno.buckled && !xeno.is_mob_incapacitated()) + return TRUE + +/datum/action/xeno_action/onclick/toggle_design_icons/use_ability() + var/mob/living/carbon/xenomorph/xeno = owner + + if(!istype(xeno)) return - if(bound_weed) - old_hivenumber = bound_weed.hivenumber - RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) -/turf/closed/wall/resin/weedbound/Destroy() - if(bound_weed) - UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) - bound_weed = null + if(!xeno.check_state(TRUE)) + return - var/turf/Turf = get_turf(src) - if(Turf) - visible_message(SPAN_ALERT("The weedbound wall collapses into a puddle of sticky slime.")) - spawn_nutriplasm(Turf) + var/datum/action/xeno_action/activable/place_design/cAction = get_action(xeno, /datum/action/xeno_action/activable/place_design) + + if(!istype(cAction)) + return + + cAction.design_toggle = !cAction.design_toggle + + var/action_icon_result + if(cAction.design_toggle) + action_icon_result = "design_mark_1" + to_chat(xeno, SPAN_INFO("We will now place wall markers.")) + xeno.selected_design_mark = /datum/design_mark/resin_wall + else + action_icon_result = "design_mark_2" + to_chat(xeno, SPAN_INFO("We will now place door markers.")) + xeno.selected_design_mark = /datum/design_mark/resin_door + update_mouse_pointer() + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, action_icon_result) return ..() -/turf/closed/wall/resin/weedbound/proc/spawn_nutriplasm(turf/Turf) - return +////////////////////////// +// Greater Resin Surge. // +////////////////////////// -/turf/closed/wall/resin/weedbound/proc/on_weed_expire() - SIGNAL_HANDLER +/datum/action/xeno_action/activable/greater_resin_surge + name = "Greater Resin Surge (250)" + action_icon_state = "greater_resin_surge" + plasma_cost = 250 + xeno_cooldown = 30 SECONDS + macro_path = /datum/action/xeno_action/verb/verb_greater_surge + action_type = XENO_ACTION_CLICK + ability_primacy = XENO_PRIMARY_ACTION_5 - if(!old_hivenumber) - ScrapeAway() +/datum/action/xeno_action/activable/greater_resin_surge/use_ability(atom/target_atom) + var/mob/living/carbon/xenomorph/xeno = owner + if(!action_cooldown_check()) return - addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS) + if(!xeno.check_state()) + return -/turf/closed/wall/resin/weedbound/proc/check_weed_replacement() - var/turf/Turf = get_turf(src) - if(!Turf) - ScrapeAway() + if(!check_and_use_plasma_owner()) return - var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in Turf + for(var/obj/effect/alien/resin/design/node in xeno.current_design) + if(get_dist(xeno, node) > 7) + continue - if(new_weed && new_weed.hivenumber == old_hivenumber) - bound_weed = new_weed - RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - else - playsound(src, "alien_resin_break", 25) - ScrapeAway() + var/turf/node_loc = get_turf(node.loc) + if(node_loc) + create_animation_overlay(node_loc, /obj/effect/resin_construct/fastweak) -/turf/closed/wall/resin/weedbound/normal/spawn_nutriplasm(turf/Turf) - new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf) + addtimer(CALLBACK(src, PROC_REF(replace_nodes)), 1 SECONDS) + apply_cooldown() + xeno_cooldown = initial(xeno_cooldown) + return ..() -/turf/closed/wall/resin/weedbound/normal/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this strange wall appears to have merged with the resin below to hold itself together.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that this resin wall will collapse if the weeds it is merged with disappear.") +/datum/action/xeno_action/activable/greater_resin_surge/proc/replace_nodes() + var/mob/living/carbon/xenomorph/xeno = owner + for(var/obj/effect/alien/resin/design/node in xeno.current_design.Copy()) + if(get_dist(xeno, node) > 7) + continue -/turf/closed/wall/resin/weedbound/thick - name = "thick weedbound resin wall" - desc = "An oddly solidified thick resin wall with a layered pattern that reminds you of flower buds." - icon_state = "thickweedboundresin" - damage_cap = HEALTH_WALL_XENO_THICK - walltype = WALL_THICK_WEEDBOUND_RESIN + var/turf/node_loc = get_turf(node.loc) + if(!node_loc) + continue -/turf/closed/wall/resin/weedbound/thick/spawn_nutriplasm(turf/Turf) - new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf) + var/obj/effect/alien/weeds/target_weeds = node_loc.weeds + if(target_weeds && target_weeds.hivenumber == xeno.hivenumber) + xeno.visible_message(SPAN_XENODANGER("\The [xeno] surges the resin, creating an unstable wall!"), + SPAN_XENONOTICE("We surge the resin, creating an unstable wall!"), null, 5) -/turf/closed/wall/resin/weedbound/thick/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this strange darker wall appears to have merged with the resin below to hold itself together.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that this thick resin wall will collapse if the weeds it is merged with disappear.") + node_loc.place_on_top(/turf/closed/wall/resin/reflective/weak) + var/turf/closed/wall/resin/reflective/weak/good_wall = node_loc + if(good_wall) + good_wall.hivenumber = xeno.hivenumber + set_hive_data(good_wall, xeno.hivenumber) + playsound(node_loc, "alien_resin_build", 25) -/obj/structure/mineral_door/resin/weedbound //NEVER use this variant, use subtypes - name = "weedbound resin door" - desc = "A weird resin door that solidified strangely, forming a petal-like pattern." - icon_state = "weedbound resin" - mineralType = "weedbound resin" - hardness = 1.4 + qdel(node) + xeno.current_design -= node + +/datum/action/xeno_action/activable/greater_resin_surge/proc/create_animation_overlay(turf/target_turf, animation_type) + if(!istype(target_turf, /turf)) + return + + if(!ispath(animation_type, /obj/effect/resin_construct/fastweak)) + return + var/obj/effect/resin_construct/fastweak/animation = new animation_type(target_turf) + + addtimer(CALLBACK(animation, TYPE_PROC_REF(/obj/effect/resin_construct/fastweak, delete_animation)), 2 SECONDS) + +/obj/effect/resin_construct/fastweak/proc/delete_animation() + if(!QDELETED(src)) + qdel(src) +//Marks +/datum/design_mark + var/name = "xeno_declare" + var/icon_state = "empty" + var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to each other." + +/datum/design_mark/resin_wall + name = "Resin Wall" + desc = "Place resin wall here!" + icon_state = "mark_wall" + +/datum/design_mark/resin_door + name = "Resin Door" + desc = "Place resin door here!" + icon_state = "mark_door" + +//--------------------------------// +//-----// Design Node Base //-----// +//--------------------------------// + +/obj/effect/alien/resin/design + name = "Design Node" + desc = "A weird node, it looks mutated." + icon = 'icons/mob/xenos/effects.dmi' + icon_state = "static_speednode" + density = FALSE + opacity = FALSE + layer = RESIN_UNDER_STRUCTURE_LAYER + plane = FLOOR_PLANE + health = HEALTH_RESIN_XENO_STICKY + + /// With mark meaning assigned to this node, defaults to wall marker. + var/datum/design_mark/mark_meaning = /datum/design_mark/resin_wall + /// The xenomorph that placed this node. + var/mob/living/carbon/xenomorph/bound_xeno + /// The weed node this design node is bound to. var/obj/effect/alien/weeds/bound_weed - var/old_hivenumber + /// Which hive this node belongs to. + var/hivenumber = XENO_HIVE_NORMAL + /// Plasma cost to place this node. + var/plasma_cost = 0 + /// Image for the design mark. + var/image/chosenMark -/obj/structure/mineral_door/resin/weedbound/Initialize() +/obj/effect/alien/resin/design/Initialize(mapload, obj/effect/alien/weeds/weeds, mob/living/carbon/xenomorph/xeno) + if(!istype(xeno)) + return INITIALIZE_HINT_QDEL + + bound_xeno = xeno + bound_weed = weeds + hivenumber = xeno.hivenumber + + RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) + RegisterSignal(bound_xeno, COMSIG_PARENT_QDELETING, PROC_REF(on_xeno_expire)) + + set_hive_data(src, hivenumber) . = ..() - bound_weed = locate(/obj/effect/alien/weeds) in get_turf(src) - if(!bound_weed) - addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 3 SECONDS) - return - if(bound_weed) - old_hivenumber = bound_weed.hivenumber - RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) -/obj/structure/mineral_door/resin/weedbound/Destroy() - if(bound_weed) - UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) - bound_weed = null + if(bound_weed.hivenumber != bound_xeno.hivenumber) + qdel(src) - var/turf/Turf = get_turf(src) - if(Turf) - visible_message(SPAN_ALERT("The weedbound wall collapses into a puddle of sticky slime.")) - spawn_nutriplasm(Turf) + if(xeno.selected_design_mark) + mark_meaning = new xeno.selected_design_mark - return ..() + var/datum/hive_status/hive = GLOB.hive_datum[hivenumber] + if(hive) + hive.designer_marks += src + if(mark_meaning) + var/icon_state_to_use = get_marker_icon_state() + if(icon_state_to_use) + chosenMark = image(icon, src, icon_state_to_use, ABOVE_HUD_LAYER, "pixel_y" = 5) + chosenMark.plane = ABOVE_HUD_PLANE + chosenMark.appearance_flags = RESET_COLOR -/obj/structure/mineral_door/resin/weedbound/proc/spawn_nutriplasm(turf/Turf) - return + for(xeno in hive.totalXenos) + if(xeno.client) + xeno.hud_set_design_marks() + refresh_marker() -/obj/structure/mineral_door/resin/weedbound/proc/on_weed_expire() - SIGNAL_HANDLER +/obj/effect/alien/resin/design/Destroy() + var/datum/hive_status/hive = GLOB.hive_datum[hivenumber] + if(hive) + hive.designer_marks -= src + for(var/mob/living/carbon/xenomorph/xeno in hive.totalXenos) + if(xeno.client && chosenMark) + xeno.client.images -= chosenMark + xeno.hud_set_design_marks() - if(!old_hivenumber) - Dismantle() + if(!QDELETED(bound_xeno)) + bound_xeno.current_design.Remove(src) + unregister_weed_expiration_signal_design() + bound_xeno = null + bound_weed = null + chosenMark = null + return ..() + +/obj/effect/alien/resin/design/proc/refresh_marker() + if(!chosenMark || !mark_meaning) return - addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS) + if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) + chosenMark.icon_state = mark_meaning.icon_state -/obj/structure/mineral_door/resin/weedbound/proc/check_weed_replacement() - var/turf/Turf = get_turf(src) - if(!Turf) - Dismantle() - return +/obj/effect/alien/resin/design/proc/get_marker_icon_state() + if(!mark_meaning) + return null + return mark_meaning.icon_state - var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in Turf +/obj/effect/alien/resin/design/proc/on_weed_expire() + SIGNAL_HANDLER + qdel(src) - if(new_weed && new_weed.hivenumber == old_hivenumber) - bound_weed = new_weed - RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - else - playsound(src, "alien_resin_break", 25) - Dismantle() +/obj/effect/alien/resin/design/proc/on_xeno_expire() + SIGNAL_HANDLER + qdel(src) -/obj/structure/mineral_door/resin/weedbound/normal/spawn_nutriplasm(turf/Turf) - new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf) +/obj/effect/alien/resin/design/proc/check_hivenumber_match() + if(!bound_weed || !bound_xeno) + visible_message(SPAN_XENOWARNING("The node shudders and decays back into the weeds.")) + qdel(src) + else if(bound_weed.hivenumber != bound_xeno.hivenumber) + visible_message(SPAN_XENOWARNING("The node withers away.")) + qdel(src) -/obj/structure/mineral_door/resin/weedbound/normal/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this strange door appears to have merged with the resin below to hold itself together.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that this resin door will collapse if the weeds it is merged with disappear.") +/obj/effect/alien/resin/design/proc/unregister_weed_expiration_signal_design() + if(bound_weed) + UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) -/obj/structure/mineral_door/resin/weedbound/thick - name = "thick weedbound resin door" - desc = "A weird thick resin door that solidified strangely, forming a petal-like pattern." - icon_state = "thick weedbound resin" - mineralType = "thick weedbound resin" - health = HEALTH_DOOR_XENO_THICK - hardness = 1.9 +/obj/effect/alien/resin/design/proc/register_weed_expiration_signal_design(obj/effect/alien/weeds/new_weed) + RegisterSignal(new_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) + bound_weed = new_weed + check_hivenumber_match() -/obj/structure/mineral_door/resin/weedbound/thick/spawn_nutriplasm(turf/Turf) - new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf) +/obj/effect/alien/resin/design/proc/hud_set_queen_overwatch() + return -/obj/structure/mineral_door/resin/weedbound/thick/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this strange darker door appears to have merged with the resin below to hold itself together.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("You sense that this thick resin door will collapse if the weeds it is merged with disappear.") +//-----// Design Speed Node //-----// -/obj/effect/alien/resin/sticky/weak_nutriplasm - name = "thin sticky nutriplasm" - desc = "A thin layer of disgusting sticky slime." - icon_state = "weak_nutriplasm" - slow_amt = 5 +/obj/effect/alien/resin/design/speed_node + name = "Optimized Design Node (50)" + icon_state = "static_speednode" + plasma_cost = 50 + +/obj/effect/alien/resin/design/speed_node/refresh_marker() + if(!chosenMark || !mark_meaning) + return + + if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) + chosenMark.icon_state = mark_meaning.icon_state + "_speed" + else + ..() + +/obj/effect/alien/resin/design/speed_node/get_marker_icon_state() + if(!mark_meaning) + return null + return mark_meaning.icon_state + "_speed" -/obj/effect/alien/resin/sticky/weak_nutriplasm/get_examine_text(mob/user) +/obj/effect/alien/resin/design/speed_node/get_examine_text(mob/user) . = ..() if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this thin, sticky substance reminds you of sticky resin.") + . += SPAN_NOTICE("On closer examination, this node looks like it has a big green oozing bulb at its center, making the weeds under it twitch...") if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("We stare at the remains of weedbound walls - nutriplasm. As edible as it sounds, it's just another kind of sticky resin.") + . += SPAN_NOTICE("We sense that building on top of this node will speed up your construction speed by [SPAN_BOLDNOTICE("50%")].") -/obj/effect/alien/resin/sticky/strong_nutriplasm - name = "sticky nutriplasm" - desc = "A thick layer of disgusting sticky slime." - icon_state = "strong_nutriplasm" - slow_amt = 10 +//-----// Design Cost Node //-----// -/obj/effect/alien/resin/sticky/strong_nutriplasm/get_examine_text(mob/user) - . = ..() - if(ishuman(user)) - . += SPAN_NOTICE("On closer examination, this thick, sticky substance reminds you of sticky resin.") - if(isxeno(user) || isobserver(user)) - . += SPAN_NOTICE("We stare at thick nutriplasm, the remains from weedbound resin, it sound delicious but you remember, its just different sticky resin.") +/obj/effect/alien/resin/design/cost_node + name = "Flexible Design Node (50)" + icon_state = "static_costnode" + plasma_cost = 50 -/obj/effect/alien/resin/design/upgrade - name = "Thicken Resin (60)" - desc = "Channel our plasma and nutrients to thicken structures." - icon = 'icons/mob/hud/actions_xeno.dmi' - icon_state = "upgrade_resin" - plasma_cost = 60 +/obj/effect/alien/resin/design/cost_node/refresh_marker() + if(!chosenMark || !mark_meaning) + return -/obj/effect/alien/resin/design/remove - name = "Remove Design Node (25)" - desc = "Channel our plasma to revert design node back to weeds." - icon = 'icons/mob/hud/actions_xeno.dmi' - icon_state = "remove_node" - plasma_cost = 25 + if(bound_xeno.selected_design_mark == /datum/design_mark/resin_wall || bound_xeno.selected_design_mark == /datum/design_mark/resin_door) + chosenMark.icon_state = mark_meaning.icon_state + "_cost" + else + ..() -////////////////////////// -// Greater Resin Surge. // -////////////////////////// +/obj/effect/alien/resin/design/cost_node/get_marker_icon_state() + if(!mark_meaning) + return null + return mark_meaning.icon_state + "_cost" -/datum/action/xeno_action/verb/verb_greater_surge() - set category = "Alien" - set name = "Greater Resin Surge" - set hidden = TRUE - var/action_name = "Greater Resin Surge" - handle_xeno_macro(src, action_name) +/obj/effect/alien/resin/design/cost_node/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this node looks like its made of smaller blue bulbs grown together, making the weeds under them look soft and squishy.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that building on top of this node will decrease plasma cost of basic resin structures by [SPAN_BOLDNOTICE("50%")].") -/datum/action/xeno_action/activable/greater_resin_surge - name = "Greater Resin Surge (250)" - action_icon_state = "greater_resin_surge" - plasma_cost = 250 - xeno_cooldown = 30 SECONDS - macro_path = /datum/action/xeno_action/verb/verb_greater_surge - action_type = XENO_ACTION_CLICK - ability_primacy = XENO_PRIMARY_ACTION_5 +//-----// Design Construct Node //-----// -/datum/action/xeno_action/activable/greater_resin_surge/use_ability(atom/target_atom) - var/mob/living/carbon/xenomorph/xeno = owner - if(!action_cooldown_check()) - return +/obj/effect/alien/resin/design/construct_node + name = "Construct Design Node (50)" + icon_state = "static_constructnode" + plasma_cost = 50 + /// Amount of plasma to donate when interacting with node. + var/plasma_donation = 70 + /// Check if node is currently building something. + var/building = FALSE + /// Check if special condition for thick build is met. + var/thick_build = FALSE + var/obj/effect/resin_construct/build_overlay - if(!xeno.check_state()) - return +/obj/effect/alien/resin/design/construct_node/Initialize(mapload) + . = ..() + var/area/area = get_area(src) + if(area) + if(area.linked_lz) + AddComponent(/datum/component/resin_cleanup) + area.current_resin_count++ - if(!check_and_use_plasma_owner()) +/obj/effect/alien/resin/design/construct_node/proc/complete_construction(turf/target_turf, design_mark, mob/living/carbon/xenomorph/xeno) + if(QDELETED(src) || QDELETED(target_turf)) return - for(var/obj/effect/alien/resin/design/node in xeno.current_design) - if(get_dist(xeno, node) > 7) - continue + if(build_overlay && !QDELETED(build_overlay)) + qdel(build_overlay) + build_overlay = null - var/turf/node_loc = get_turf(node.loc) - if(node_loc) - create_animation_overlay(node_loc, /obj/effect/resin_construct/fastweak) + building = FALSE - addtimer(CALLBACK(src, PROC_REF(replace_nodes)), 1 SECONDS) - apply_cooldown() - xeno_cooldown = initial(xeno_cooldown) - return ..() + var/obj/effect/alien/weeds/node/node_weed = locate(/obj/effect/alien/weeds/node) in target_turf -/datum/action/xeno_action/activable/greater_resin_surge/proc/replace_nodes() - var/mob/living/carbon/xenomorph/xeno = owner - for(var/obj/effect/alien/resin/design/node in xeno.current_design.Copy()) - if(get_dist(xeno, node) > 7) - continue + if(node_weed) + if(istype(target_turf, /turf/closed/wall)) + to_chat(xeno, SPAN_WARNING("A wall already exists here.")) + return - var/turf/node_loc = get_turf(node.loc) - if(!node_loc) - continue + var/turf/node_wall + if(thick_build) + node_wall = target_turf.place_on_top(/turf/closed/wall/resin/weedbound/node/thick) + else + node_wall = target_turf.place_on_top(/turf/closed/wall/resin/weedbound/node/normal) - var/obj/effect/alien/weeds/target_weeds = node_loc.weeds - if(target_weeds && target_weeds.hivenumber == xeno.hivenumber) - xeno.visible_message(SPAN_XENODANGER("\The [xeno] surges the resin, creating an unstable wall!"), - SPAN_XENONOTICE("We surge the resin, creating an unstable wall!"), null, 5) + var/turf/closed/wall/resin/Res = node_wall + if(istype(Res)) + Res.hivenumber = src.hivenumber + set_hive_data(Res, Res.hivenumber) - node_loc.place_on_top(/turf/closed/wall/resin/reflective/weak) - var/turf/closed/wall/resin/reflective/weak/good_wall = node_loc - if(good_wall) - good_wall.hivenumber = xeno.hivenumber - set_hive_data(good_wall, xeno.hivenumber) - playsound(node_loc, "alien_resin_build", 25) + to_chat(xeno, SPAN_NOTICE("We create a nodebound wall.")) + playsound(node_wall, "alien_resin_build", 25) + else + to_chat(xeno, SPAN_WARNING("A wall already exists here.")) - qdel(node) - xeno.current_design -= node + if(istype(design_mark, /datum/design_mark/resin_wall)) + if(!istype(target_turf, /turf/closed/wall)) + var/turf/placed + if(thick_build) + placed = target_turf.place_on_top(/turf/closed/wall/resin/weedbound/thick) + else + placed = target_turf.place_on_top(/turf/closed/wall/resin/weedbound/normal) -/datum/action/xeno_action/activable/greater_resin_surge/proc/create_animation_overlay(turf/target_turf, animation_type) - if(!istype(target_turf, /turf)) - return + var/turf/closed/wall/resin/Res = get_turf(target_turf) + if(istype(Res)) + Res.hivenumber = src.hivenumber + set_hive_data(Res, Res.hivenumber) - if(!ispath(animation_type, /obj/effect/resin_construct/fastweak)) - return - var/obj/effect/resin_construct/fastweak/animation = new animation_type(target_turf) + to_chat(xeno, SPAN_NOTICE("We create a weedbound wall.")) + playsound(placed, "alien_resin_build", 25) + else + to_chat(xeno, SPAN_WARNING("A wall already exists here.")) - addtimer(CALLBACK(animation, TYPE_PROC_REF(/obj/effect/resin_construct/fastweak, delete_animation)), 2 SECONDS) + else if(istype(design_mark, /datum/design_mark/resin_door)) + if(!istype(target_turf, /obj/structure/mineral_door)) + var/obj/new_structure + if(thick_build) + new_structure = new /obj/structure/mineral_door/resin/weedbound/thick(target_turf) + else + new_structure = new /obj/structure/mineral_door/resin/weedbound/normal(target_turf) -/obj/effect/resin_construct/fastweak/proc/delete_animation() - if(!QDELETED(src)) - qdel(src) + var/obj/structure/mineral_door/resin/Res = locate(/obj/structure/mineral_door/resin) in get_turf(target_turf) + if(istype(Res)) + Res.hivenumber = src.hivenumber + set_hive_data(Res, Res.hivenumber) -///////////////////////////// -/// Place Design /// -///////////////////////////// + to_chat(xeno, SPAN_NOTICE("We create a weedbound door.")) + playsound(new_structure, "alien_resin_build", 25) + else + to_chat(xeno, SPAN_WARNING("A door already exists here.")) -/datum/action/xeno_action/activable/place_design - name = "Influence" - action_icon_state = "secrete_resin" - plasma_cost = 0 - macro_path = /datum/action/xeno_action/verb/place_design - action_type = XENO_ACTION_CLICK - ability_primacy = XENO_PRIMARY_ACTION_3 - xeno_cooldown = 0 - var/max_reach = 10 - var/design_toggle = TRUE + qdel(src) -/datum/action/xeno_action/verb/place_design() - set category = "Alien" - set name = "Place Design" - set hidden = TRUE - var/action_name = "Place Design" - handle_xeno_macro(src, action_name) +/obj/effect/alien/resin/design/construct_node/Destroy() + if(build_overlay && !QDELETED(build_overlay)) + qdel(build_overlay) + if(bound_weed) + unregister_weed_expiration_signal_design() + var/area/area = get_area(src) + area?.current_resin_count-- + return ..() -/datum/action/xeno_action/activable/place_design/use_ability(atom/target_atom, mods, use_plasma = TRUE, message = TRUE) - var/mob/living/carbon/xenomorph/xeno = owner - if(!can_remote_build()) - to_chat(owner, SPAN_XENONOTICE("We must be standing on weeds to channel our nutrients and influence.")) +/obj/effect/alien/resin/design/construct_node/attack_hand(mob/user) + if(!isxeno(user)) + to_chat(user, SPAN_WARNING("You don't understand how to interact with this strange node.")) return - if(!action_cooldown_check()) + var/mob/living/carbon/xenomorph/xeno = user + + if(!can_begin_construction(xeno)) return - if(!xeno.check_state()) + var/total_plasma_cost = get_total_plasma_cost(xeno) + if(xeno.plasma_stored < total_plasma_cost) + to_chat(xeno, SPAN_WARNING("We lack the plasma to feed this node. [xeno.plasma_stored]/[total_plasma_cost]")) return - if(mods["click_catcher"]) + xeno.plasma_stored -= total_plasma_cost + to_chat(xeno, SPAN_NOTICE("Our action activates the node, it latches onto us and it forcefully consumes [total_plasma_cost] of our plasma.")) + + begin_construction(xeno) + +/obj/effect/alien/resin/design/construct_node/attackby(obj/item/item, mob/user) + if(isxeno(user) && user.a_intent != INTENT_HARM) + //Do NOT call attack_hand here — that bypasses destruction + to_chat(user, SPAN_NOTICE("You examine the node curiously, but nothing happens.")) return - if(ismob(target_atom)) - if(!can_see(xeno, target_atom, max_reach)) - to_chat(xeno, SPAN_XENODANGER("We cannot see that location!")) - return + . = ..() + +/obj/effect/alien/resin/design/construct_node/attack_alien(mob/living/carbon/xenomorph/xeno) + if(xeno.a_intent != INTENT_HARM) + return attack_hand(xeno) else - if(get_dist(xeno, target_atom) > max_reach) - to_chat(xeno, SPAN_WARNING("That's too far away!")) - return + return ..() - var/turf/target_turf = get_turf(target_atom) - if(!istype(target_turf)) - to_chat(xeno, SPAN_WARNING("We cannot design without weeds.")) - return +/obj/effect/alien/resin/design/construct_node/proc/get_total_plasma_cost(mob/living/carbon/xenomorph/xeno) + var/area/target_area = get_area(get_turf(src)) + var/total_plasma_cost = plasma_donation - var/obj/effect/alien/weeds/target_weeds = locate(/obj/effect/alien/weeds) in target_turf - if(!target_weeds) - to_chat(xeno, SPAN_WARNING("There are no weeds to create a connection!")) - return + if(target_area && target_area.openable_turf_count) + var/density_ratio = target_area.current_resin_count / target_area.openable_turf_count + if(density_ratio > 0.4) + total_plasma_cost = ceil(total_plasma_cost * (density_ratio + 0.35) * 2) + if(total_plasma_cost > xeno.plasma_max && (XENO_RESIN_BASE_COST + plasma_donation) < xeno.plasma_max) + total_plasma_cost = xeno.plasma_max - if(target_weeds.hivenumber != xeno.hivenumber) - to_chat(xeno, SPAN_WARNING("These weeds do not belong to our hive; they reject our influence.")) - return + return total_plasma_cost - var/plasma_cost - if(xeno.selected_design && xeno.selected_design.plasma_cost) - plasma_cost = xeno.selected_design.plasma_cost +/obj/effect/alien/resin/design/construct_node/proc/can_begin_construction(mob/living/carbon/xenomorph/xeno) + if(building) + to_chat(xeno, SPAN_WARNING("This node is already being infused with plasma.")) + return FALSE - if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/upgrade)) - if(!(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane) || istype(target_atom, /obj/structure/mineral_door/resin))) - to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin walls, membrane and doors!")) - return + if(xeno.hivenumber != src.hivenumber) + to_chat(xeno, SPAN_WARNING("This construct node does not belong to your hive.")) + return FALSE + + if(!mark_meaning) + to_chat(xeno, SPAN_WARNING("This node has no valid design selected.")) + return FALSE + + var/turf/target_turf = get_turf(src) + if(!istype(target_turf)) + to_chat(xeno, SPAN_WARNING("This is not a valid location.")) + return FALSE + + return TRUE + +/obj/effect/alien/resin/design/construct_node/proc/begin_construction(mob/living/carbon/xenomorph/xeno) + if(!can_begin_construction(xeno)) + return - if(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane)) - var/turf/closed/wall/resin/wall = target_atom + var/turf/target_turf = get_turf(src) + building = TRUE - if(wall.hivenumber != xeno.hivenumber) - to_chat(xeno, SPAN_XENOWARNING("[wall] does not belong to our hive!")) - return + var/obj/effect/resin_construct/overlay - if(wall.upgrading_now) //<--- Prevent spam and waste of plasma - to_chat(xeno, SPAN_WARNING("This wall is already being reinforced!")) - return + if(istype(mark_meaning, /datum/design_mark/resin_wall)) + overlay = new /obj/effect/resin_construct/construct_wallslow(target_turf) + else if(istype(mark_meaning, /datum/design_mark/resin_door)) + overlay = new /obj/effect/resin_construct/construct_doorslow(target_turf) - wall.upgrading_now = TRUE + build_overlay = overlay - if(wall.type == /turf/closed/wall/resin) - var/obj/thick_wall = new /obj/effect/resin_construct/thickfast(target_turf, src, xeno) - if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) - qdel(thick_wall) - wall.upgrading_now = FALSE - return - qdel(thick_wall) - wall.ChangeTurf(/turf/closed/wall/resin/thick) + if(bound_weed.weed_strength >= WEED_LEVEL_HARDY) + thick_build = TRUE - else if(wall.type == /turf/closed/wall/resin/membrane) - var/obj/thick_membrane = new /obj/effect/resin_construct/transparent/thickfast(target_turf, src, xeno) - if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) - qdel(thick_membrane) - wall.upgrading_now = FALSE - return - qdel(thick_membrane) - wall.ChangeTurf(/turf/closed/wall/resin/membrane/thick) - else - to_chat(xeno, SPAN_XENOWARNING("[wall] can't be made thicker.")) - return + if(istype(xeno.strain, /datum/xeno_strain/gardener)) + thick_build = TRUE - wall.upgrading_now = FALSE + if((xeno.caste_type in XENO_CONSTRUCT_NODE_BOOST) && !istype(xeno.strain, /datum/xeno_strain/designer)) + thick_build = TRUE - else if(istype(target_atom, /obj/structure/mineral_door/resin)) - var/obj/structure/mineral_door/resin/door = target_atom + addtimer(CALLBACK(src, PROC_REF(complete_construction), target_turf, mark_meaning, xeno), 4 SECONDS) - if(door.hivenumber != xeno.hivenumber) - to_chat(xeno, SPAN_XENOWARNING("[door] does not belong to your hive!")) - return +/obj/effect/alien/resin/design/construct_node/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this node looks like big blub composed of smaller purple glowing cups, pumping some strange liquid trough weeds.") + if(isxeno(user) || isobserver(user)) + var/mob/living/carbon/xenomorph/xeno = user + var/total_plasma_cost = get_total_plasma_cost(xeno) + . += SPAN_NOTICE("We sense that feeding [SPAN_BOLDNOTICE("[total_plasma_cost]")] plasma with our hand to this node will secrete a [SPAN_BOLDNOTICE("[mark_meaning]")], you also heard that using plasma fruit works too.") - if(door.upgrading_now) - to_chat(xeno, SPAN_WARNING("This door is already being reinforced!")) - return +//-------------------------------------// +//-----// Weedbound Base (Wall) //-----// +//-------------------------------------// - if(door.hardness == 1.5) - door.upgrading_now = TRUE - var/obj/thick_door = new /obj/effect/resin_construct/thickdoorfast(target_turf, src, xeno) - if(!do_after(xeno, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD)) - qdel(thick_door) - door.upgrading_now = FALSE - return - qdel(thick_door) - var/oldloc = door.loc - qdel(door) - new /obj/structure/mineral_door/resin/thick(oldloc, door.hivenumber) - else - if(xeno.try_toggle_resin_door(door)) - if(!check_and_use_plasma_owner()) - return TRUE - return - return +//Should not be upgradable because it's not "stable" but special actions should create thick variant +/turf/closed/wall/resin/weedbound //NEVER use this variant, use subtypes + name = "weedbound resin wall" + desc = "An oddly solidified resin wall with a pattern that reminds you of flower buds." + icon_state = "weedboundresin" + walltype = WALL_WEEDBOUND_RESIN - else - to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin structures!")) - return + /// The weed this wall is bound to. + var/obj/effect/alien/weeds/bound_weed + /// The hivenumber of the weed this wall is build on. + var/old_hivenumber + /// Prevents sticky spawn if walls get swapped. + var/replacing = FALSE - if(!check_and_use_plasma_owner(plasma_cost)) - return +/turf/closed/wall/resin/weedbound/Initialize() + . = ..() + bound_weed = locate(/obj/effect/alien/weeds) in get_turf(src) + if(!bound_weed) + addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 3 SECONDS) + return + if(bound_weed) + old_hivenumber = bound_weed.hivenumber + RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - xeno.visible_message(SPAN_XENONOTICE("Weeds around [target_atom] start to twitch and pump substance towards it, thickening it in process!"), - SPAN_XENONOTICE("We start to channel nutrients towards [target_atom], using [plasma_cost] plasma."), null, 5) - playsound(target_atom, "alien_resin_build", 25) +/turf/closed/wall/resin/weedbound/Destroy() + if(bound_weed) + UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) + bound_weed = null + for(var/obj/effect/alien/weeds/node/weed_node in contents) + qdel(weed_node) - target_atom.add_hiddenprint(xeno) //Tracks who reinforced it for admins - return TRUE + var/turf/target_turf = get_turf(src) + if(target_turf) + spawn_nutriplasm(target_turf) - if(xeno.try_toggle_resin_door(target_atom)) - if(!check_and_use_plasma_owner()) - return TRUE - return + return ..() - if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/remove)) - var/obj/effect/alien/resin/design/target_node = locate(/obj/effect/alien/resin/design) in target_turf - if(!target_node) - to_chat(xeno, SPAN_XENOWARNING("There is no resin node here to remove!")) - return +/turf/closed/wall/resin/weedbound/proc/spawn_nutriplasm(turf/target_turf) + return - if(target_node.hivenumber != xeno.hivenumber) - to_chat(xeno, SPAN_XENOWARNING("This node does not belong to your hive!")) - return +/turf/closed/wall/resin/weedbound/proc/on_weed_expire() + SIGNAL_HANDLER - if(target_node.bound_xeno != xeno) - to_chat(xeno, SPAN_XENOWARNING("You cannot remove a node placed by another sister!")) - return + if(!old_hivenumber) + ScrapeAway() + return - qdel(target_node) - to_chat(xeno, SPAN_XENONOTICE("We sever the bond to the node, causing it to dissolve into the ground.")) - playsound(xeno.loc, "alien_resin_move2", 25) + addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS) + +/turf/closed/wall/resin/weedbound/proc/check_weed_replacement() + var/turf/target_turf = get_turf(src) + if(!target_turf) + ScrapeAway() return - if(length(xeno.current_design) >= xeno.max_design_nodes) //Check if there are more nodes than length that was defined - to_chat(xeno, SPAN_XENOWARNING("We cannot sustain another node, one will wither away to allow this one to live!")) - var/obj/effect/alien/resin/design/old_design = xeno.current_design[1] //Check with node is first for deletion on list - xeno.current_design.Remove(old_design) //Removes first node stored inside list - qdel(old_design) //Delete node. + var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in target_turf + if(new_weed && new_weed.hivenumber == old_hivenumber) + bound_weed = new_weed + RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) + change_bound_resin(new_weed) + else + playsound(src, "alien_resin_break", 25) + ScrapeAway() - var/selected_design = xeno.selected_design +/turf/closed/wall/resin/weedbound/proc/change_bound_resin(obj/effect/alien/weeds/new_weed) + var/turf/target_turf = get_turf(src) + if(!target_turf || QDELETED(src) || !new_weed) + return - if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/speed_node)) //Check path you selected from list - if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) - to_chat(src, SPAN_WARNING("There's something built here already.")) - return - var/obj/speed_warn = new /obj/effect/resin_construct/speed_node(target_turf, src, xeno) //Create "Animation" overlay - if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) - qdel(speed_warn) //Delete "Animation" overlay after defined time - return - qdel(speed_warn) //Delete again just in case overlay don't get deleted - if(!is_turf_clean(target_turf)) //Recheck the turf again just in case - to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) - return - if(!check_and_use_plasma_owner(plasma_cost)) - return - xeno.visible_message(SPAN_XENONOTICE("\The [xeno] channels nutrients and shapes it into a node!")) - var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) //Create node you selected from list - if(!design) - to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find node to place! Contact a coder!")) - return - playsound(xeno.loc, "alien_resin_build", 25) - xeno.current_design.Add(design) //Add Node to list. + var/target_type + var/node_weed = istype(new_weed, /obj/effect/alien/weeds/node) + var/strong_weed = (new_weed.weed_strength >= WEED_LEVEL_HARDY) - if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/cost_node)) - if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) - to_chat(src, SPAN_WARNING("There's something built here already.")) - return - var/obj/cost_warn = new /obj/effect/resin_construct/cost_node(target_turf, src, xeno) - if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) - qdel(cost_warn) - return - qdel(cost_warn) - if(!is_turf_clean(target_turf)) - to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) - return - if(!check_and_use_plasma_owner(plasma_cost)) - return - xeno.visible_message(SPAN_XENONOTICE("The [xeno] channels nutrients and shapes it into a node!")) - var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) - if(!design) - to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find node to place! Contact a coder!")) - return - playsound(xeno.loc, "alien_resin_build", 25) - xeno.current_design.Add(design) + if(node_weed) + if(strong_weed) + target_type = /turf/closed/wall/resin/weedbound/node/thick + else + target_type = /turf/closed/wall/resin/weedbound/node/normal + else + if(strong_weed) + target_type = /turf/closed/wall/resin/weedbound/thick + else + target_type = /turf/closed/wall/resin/weedbound/normal - if(ispath(xeno.selected_design, /obj/effect/alien/resin/design/construct_node)) - if(!is_turf_clean(target_turf, check_resin_doors = TRUE)) - to_chat(src, SPAN_WARNING("There's something built here already.")) - return - if(!xeno.check_alien_construction(target_turf, check_doors = FALSE)) - return FALSE - var/obj/const_warn = new /obj/effect/resin_construct/construct_node(target_turf, src, xeno) - if(!do_after(xeno, 0.5 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD) || selected_design != xeno.selected_design) - qdel(const_warn) - return - qdel(const_warn) - if(!is_turf_clean(target_turf)) - to_chat(xeno, SPAN_XENOWARNING("Something else has taken root here before us.")) - return - if(!check_and_use_plasma_owner(plasma_cost)) - return - xeno.visible_message(SPAN_XENONOTICE("The [xeno] channels nutrients and shapes it into a node!")) - var/obj/effect/alien/resin/design/design = new xeno.selected_design(target_weeds.loc, target_weeds, xeno) - if(!design) - to_chat(xeno, SPAN_XENOHIGHDANGER("Couldn't find node to place! Contact a coder!")) - return - playsound(xeno.loc, "alien_resin_build", 25) - xeno.current_design.Add(design) - apply_cooldown() - return ..() + replacing = TRUE + target_turf.place_on_top(target_type) + if(!target_turf) + return -/datum/action/xeno_action/activable/place_design/proc/can_remote_build() - if(!locate(/obj/effect/alien/weeds) in get_turf(owner)) - return FALSE - return TRUE + var/turf/closed/wall/resin/weedbound/bound = target_turf + if(!bound) + return -/mob/living/carbon/xenomorph/proc/try_toggle_resin_door(atom/target_atom) - if(!istype(target_atom, /obj/structure/mineral_door/resin)) - return FALSE + bound.bound_weed = new_weed + bound.old_hivenumber = new_weed.hivenumber + bound.replacing = FALSE - var/obj/structure/mineral_door/resin/resin_door = target_atom +//-----// Weedbound - Normal Wall //-----// - if(resin_door.hivenumber != hivenumber) - to_chat(src, SPAN_XENOWARNING("This door does not belong to our hive!")) - return TRUE +/turf/closed/wall/resin/weedbound/normal/spawn_nutriplasm(turf/target_turf) + if(!replacing) + new /obj/effect/alien/resin/sticky/weak_nutriplasm(target_turf) - if(resin_door.TryToSwitchState(src)) - if(resin_door.open) - to_chat(src, SPAN_XENONOTICE("We focus our connection to the resin and remotely close the resin door.")) - else - to_chat(src, SPAN_XENONOTICE("We focus our connection to the resin and remotely open the resin door.")) +/turf/closed/wall/resin/weedbound/normal/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this strange wall appears to have merged with the resin below to hold itself together.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this resin wall will collapse if the weeds it is merged with disappear.") - return TRUE +//-----// Weedbound - Thick Wall //-----// -/datum/action/xeno_action/activable/place_design/proc/is_turf_clean(turf/current_turf, check_resin_additions = FALSE, check_doors = FALSE, check_resin_doors = FALSE) - var/has_obstacle = FALSE - for(var/obj/target in current_turf) - if(check_doors) - if(istype(target, /obj/structure/machinery/door)) - to_chat(src, SPAN_WARNING("[target] is blocking the resin! There's not enough space to build that here.")) - return FALSE - if(check_resin_additions) - if(istype(target, /obj/effect/alien/resin/sticky) || istype(target, /obj/effect/alien/resin/spike) || istype(target, /obj/effect/alien/resin/sticky/fast)) - has_obstacle = TRUE - to_chat(src, SPAN_WARNING("[target] is blocking the resin!")) - return FALSE - if(check_resin_doors) - if(istype(target, /obj/structure/mineral_door/resin)) - to_chat(src, SPAN_WARNING("[target] is blocking the resin node! There's not enough space to build that here.")) - return FALSE - if(current_turf.density || has_obstacle || locate(/obj/effect/alien/resin/design) in current_turf) - return FALSE - return TRUE +/turf/closed/wall/resin/weedbound/thick + name = "thick weedbound resin wall" + desc = "An oddly solidified thick resin wall with a pattern that reminds you of flower buds." + icon_state = "thickweedboundresin" + damage_cap = HEALTH_WALL_XENO_THICK + walltype = WALL_THICK_WEEDBOUND_RESIN -/////////////////////////////// -/// Change Node Marker /// -/////////////////////////////// +/turf/closed/wall/resin/weedbound/thick/spawn_nutriplasm(turf/target_turf) + if(!replacing) + new /obj/effect/alien/resin/sticky/strong_nutriplasm(target_turf) -/datum/action/xeno_action/verb/verb_toggle_design_icons() - set category = "Alien" - set name = "Change Design Mark" - set hidden = TRUE - var/action_name = "Change Design Mark" - handle_xeno_macro(src, action_name) +/turf/closed/wall/resin/weedbound/thick/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this strange darker wall appears to have merged with the resin below to hold itself together.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this thick resin wall will collapse if the weeds it is merged with disappear.") -/datum/action/xeno_action/onclick/toggle_design_icons - name = "Change Design Mark" - action_icon_state = "design_mark_1" - plasma_cost = 0 - macro_path = /datum/action/xeno_action/verb/verb_toggle_design_icons - action_type = XENO_ACTION_CLICK - ability_primacy = XENO_PRIMARY_ACTION_4 +//------------------------------------------// +//-----// Nodebound - Weedbound Node //-----// +//------------------------------------------// -/datum/action/xeno_action/onclick/toggle_design_icons/can_use_action() - var/mob/living/carbon/xenomorph/xeno = owner - if(xeno && !xeno.buckled && !xeno.is_mob_incapacitated()) - return TRUE +/turf/closed/wall/resin/weedbound/node + name = "nodebound resin wall" + desc = "An strangely cracked resin wall with a pattern that reminds you of flower buds." + icon_state = "nodeboundresin" + walltype = WALL_NODEBOUND_RESIN + destroy_override = TRUE -/datum/action/xeno_action/onclick/toggle_design_icons/use_ability() - var/mob/living/carbon/xenomorph/xeno = owner +//-----// Nodebound - Normal Wall //-----// - if (!istype(xeno)) - return +/turf/closed/wall/resin/weedbound/node/normal/spawn_nutriplasm(turf/target_turf) + return - if(!xeno.check_state(TRUE)) - return +/turf/closed/wall/resin/weedbound/node/normal/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer observation, this strange wall appears to be merged with node below, pulsating in rhythm with the weeds.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this wall has merged itself with the resin node below, becoming part of it.") - var/datum/action/xeno_action/activable/place_design/cAction = get_action(xeno, /datum/action/xeno_action/activable/place_design) +//-----// Nodebound - Thick Wall //-----// - if(!istype(cAction)) +/turf/closed/wall/resin/weedbound/node/thick + name = "thick nodebound resin wall" + desc = "An strangely cracked thick resin wall with a pattern that reminds you of flower buds." + icon_state = "thicknodeboundresin" + damage_cap = HEALTH_WALL_XENO_THICK + walltype = WALL_THICK_NODEBOUND_RESIN + +/turf/closed/wall/resin/weedbound/node/thick/spawn_nutriplasm(turf/target_turf) + return + +/turf/closed/wall/resin/weedbound/node/thick/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer observation, this strange dark wall appears to be merged with node below, pulsating in rhythm with the weeds.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this thick wall has merged itself with the resin node below, becoming part of it.") + +//-----// Weedbound Base (Door) //-----// + +/obj/structure/mineral_door/resin/weedbound //NEVER use this variant, use subtypes + name = "weedbound resin door" + desc = "A weird resin door that solidified strangely, forming a petal-like pattern." + icon_state = "weedbound resin" + mineralType = "weedbound resin" + hardness = 1.4 + + var/obj/effect/alien/weeds/bound_weed + var/old_hivenumber + var/replacing = FALSE + +/obj/structure/mineral_door/resin/weedbound/Initialize() + . = ..() + bound_weed = locate(/obj/effect/alien/weeds) in get_turf(src) + if(!bound_weed) + addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 3 SECONDS) return + if(bound_weed) + old_hivenumber = bound_weed.hivenumber + RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) - cAction.design_toggle = !cAction.design_toggle +/obj/structure/mineral_door/resin/weedbound/Destroy() + if(bound_weed) + UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING) + bound_weed = null - var/action_icon_result - if(cAction.design_toggle) - action_icon_result = "design_mark_1" - to_chat(xeno, SPAN_INFO("We will now place wall markers.")) - xeno.selected_design_mark = /datum/design_mark/resin_wall - else - action_icon_result = "design_mark_2" - to_chat(xeno, SPAN_INFO("We will now place door markers.")) - xeno.selected_design_mark = /datum/design_mark/resin_door + var/turf/target_turf = get_turf(src) + if(target_turf) + spawn_nutriplasm(target_turf) - button.overlays.Cut() - button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, action_icon_result) return ..() -////////////////////////// -/// Change Design /// -////////////////////////// +/obj/structure/mineral_door/resin/weedbound/proc/spawn_nutriplasm(turf/target_turf) + return -/datum/action/xeno_action/verb/verb_change_design() - set category = "Alien" - set name = "Change Design Mark" - set hidden = TRUE - var/action_name = "Change Design Mark" - handle_xeno_macro(src, action_name) +/obj/structure/mineral_door/resin/weedbound/proc/on_weed_expire() + SIGNAL_HANDLER -/datum/action/xeno_action/onclick/change_design - name = "Choose Action" - action_icon_state = "static_speednode" - plasma_cost = 0 - xeno_cooldown = 0 - macro_path = /datum/action/xeno_action/verb/verb_change_design - action_type = XENO_ACTION_CLICK - ability_primacy = XENO_PRIMARY_ACTION_2 + if(!old_hivenumber) + Dismantle() + return -/datum/action/xeno_action/onclick/change_design/use_ability(atom/Atom) - var/mob/living/carbon/xenomorph/xeno = owner - if(!xeno.check_state()) + addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS) + +/obj/structure/mineral_door/resin/weedbound/proc/check_weed_replacement() + var/turf/target_turf = get_turf(src) + if(!target_turf) + Dismantle() return - var/static/list/options = list( - "Optimized Node (50)" = icon(/datum/action/xeno_action::icon_file, "static_speednode"), - "Construct Node (70)" = icon(/datum/action/xeno_action::icon_file, "static_constructnode"), - "Thicken Resin (60)" = icon(/datum/action/xeno_action::icon_file, "upgrade_resin"), - "Open Old UI" = icon(/datum/action/xeno_action::icon_file, "open_ui"), - "Remove Node (25)" = icon(/datum/action/xeno_action::icon_file, "remove_node"), - "Flexible Node (60)" = icon(/datum/action/xeno_action::icon_file, "static_costnode") - ) + var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in target_turf - var/choice - if(owner.client.prefs.no_radials_preference) - choice = tgui_input_list(owner, "Choose Design Option", "Pick", options, theme="hive_status") + if(new_weed && new_weed.hivenumber == old_hivenumber) + bound_weed = new_weed + RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire)) + change_bound_resin(new_weed) else - choice = show_radial_menu(owner, owner?.client.get_eye(), options, radius = 50) - - var/des = FALSE - var/rem = FALSE - plasma_cost = 0 - switch(choice) - if("Optimized Node (50)") - xeno.selected_design = /obj/effect/alien/resin/design/speed_node - des = TRUE - if("Flexible Node (60)") - xeno.selected_design = /obj/effect/alien/resin/design/cost_node - des = TRUE - if("Construct Node (70)") - xeno.selected_design = /obj/effect/alien/resin/design/construct_node - des = TRUE - if("Thicken Resin (60)") - xeno.selected_design = /obj/effect/alien/resin/design/upgrade - rem = TRUE - if("Remove Node (25)") - xeno.selected_design = /obj/effect/alien/resin/design/remove - rem = TRUE - if("Open Old UI") - tgui_interact(xeno) + playsound(src, "alien_resin_break", 25) + Dismantle() - if(des) - to_chat(xeno, SPAN_NOTICE("We will now build [xeno.selected_design.name].")) - if(rem) - to_chat(xeno, SPAN_NOTICE("We will now remotely [xeno.selected_design.name].")) +/obj/structure/mineral_door/resin/weedbound/proc/change_bound_resin(obj/effect/alien/weeds/new_weed) + var/turf/target_turf = get_turf(src) + if(!target_turf || QDELETED(src) || !new_weed) + return - xeno.update_icons() - button.overlays.Cut() - button.overlays += image(icon_file, button, xeno.selected_design.icon_state) + var/target_type + var/strong_weed = (new_weed.weed_strength >= WEED_LEVEL_HARDY) - return ..() + if(strong_weed) + target_type = /obj/structure/mineral_door/resin/weedbound/thick + else + target_type = /obj/structure/mineral_door/resin/weedbound/normal -// Below is UI for old players. + replacing = TRUE + var/obj/structure/mineral_door/resin/weedbound/new_door = new target_type(target_turf) + if(!new_door) + return -/datum/action/xeno_action/onclick/change_design/give_to(mob/living/carbon/xenomorph/xeno) - . = ..() + new_door.bound_weed = new_weed + new_door.old_hivenumber = new_weed.hivenumber + new_door.replacing = FALSE + qdel(src) - button.overlays.Cut() - button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, initial(xeno.selected_design.icon_state)) - button.overlays += image(icon_file, button, action_icon_state) +//-----// Weedbound - Normal Door //-----// -/datum/action/xeno_action/onclick/change_design/ui_assets(mob/user) - return list(get_asset_datum(/datum/asset/spritesheet/choose_design)) +/obj/structure/mineral_door/resin/weedbound/normal/spawn_nutriplasm(turf/target_turf) + if(!replacing) + new /obj/effect/alien/resin/sticky/weak_nutriplasm(target_turf) -/datum/action/xeno_action/onclick/change_design/ui_static_data(mob/user) - var/mob/living/carbon/xenomorph/xeno = user - if(!istype(xeno)) - return +/obj/structure/mineral_door/resin/weedbound/normal/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this strange door appears to have merged with the resin below to hold itself together.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this resin door will collapse if the weeds it is merged with disappear.") - . = list() +//-----// Weedbound - Thick Door //-----// - var/list/design_list = list() - for(var/obj/effect/alien/resin/design/design as anything in xeno.available_design) - var/list/entry = list() +/obj/structure/mineral_door/resin/weedbound/thick + name = "thick weedbound resin door" + desc = "A weird thick resin door that solidified strangely, forming a petal-like pattern." + icon_state = "thick weedbound resin" + mineralType = "thick weedbound resin" + health = HEALTH_DOOR_XENO_THICK + hardness = 1.9 - entry["name"] = initial(design.name) - entry["desc"] = initial(design.desc) - entry["image"] = replacetext(initial(design.icon_state), " ", "-") - entry["id"] = "[design]" - design_list += list(entry) +/obj/structure/mineral_door/resin/weedbound/thick/spawn_nutriplasm(turf/target_turf) + if(!replacing) + new /obj/effect/alien/resin/sticky/strong_nutriplasm(target_turf) - .["design"] = design_list +/obj/structure/mineral_door/resin/weedbound/thick/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this strange darker door appears to have merged with the resin below to hold itself together.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We sense that this thick resin door will collapse if the weeds it is merged with disappear.") -/datum/action/xeno_action/onclick/change_design/ui_data(mob/user) - var/mob/living/carbon/xenomorph/xeno = user - if(!istype(xeno)) - return +//-----// Weedbound - Weak Sticky //-----// - . = list() - .["selected_design"] = xeno.selected_design +/obj/effect/alien/resin/sticky/weak_nutriplasm + name = "thin sticky nutriplasm" + desc = "A thin layer of disgusting sticky slime." + icon_state = "weak_nutriplasm" + slow_amt = 6 -/datum/action/xeno_action/onclick/change_design/tgui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "ChooseDesign", "Choose Design") - ui.set_autoupdate(FALSE) - ui.open() +/obj/effect/alien/resin/sticky/weak_nutriplasm/get_examine_text(mob/user) + . = ..() + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this thin, sticky substance reminds you of sticky resin.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We stare at the remains of weedbound walls - nutriplasm. As edible as it sounds, it's just another kind of sticky resin.") -/datum/action/xeno_action/onclick/change_design/Destroy() - SStgui.close_uis(src) - return ..() +//-----// Weedbound - Strong Sticky //-----// -/datum/action/xeno_action/onclick/change_design/ui_state(mob/user) - return GLOB.always_state +/obj/effect/alien/resin/sticky/strong_nutriplasm + name = "sticky nutriplasm" + desc = "A thick layer of disgusting sticky slime." + icon_state = "strong_nutriplasm" + slow_amt = 10 -/datum/action/xeno_action/onclick/change_design/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) +/obj/effect/alien/resin/sticky/strong_nutriplasm/get_examine_text(mob/user) . = ..() - if(.) - return - - var/mob/living/carbon/xenomorph/xeno = ui.user - if(!istype(xeno)) - return + if(ishuman(user)) + . += SPAN_NOTICE("On closer examination, this thick, sticky substance reminds you of sticky resin.") + if(isxeno(user) || isobserver(user)) + . += SPAN_NOTICE("We stare at thick nutriplasm, the remains from weedbound resin, it sound delicious but you remember, its just different sticky resin.") - switch(action) - if("choose_design") - var/selected_type = text2path(params["type"]) - if(!(selected_type in xeno.available_design)) - return +//-----// Abilities Buttons (Do not Spawn in Game) //-----// - var/obj/effect/alien/resin/design/design = selected_type - to_chat(xeno, SPAN_NOTICE("We will now build [initial(design.name)] when designing.")) - //update the button's overlay with new choice - xeno.update_icons() - button.overlays.Cut() - button.overlays += image(icon_file, button, action_icon_state) - button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, initial(design.icon_state)) - xeno.selected_design = selected_type - . = TRUE +/obj/effect/alien/resin/design/upgrade + name = "Thicken Resin (60)" + desc = "Channel our plasma and nutrients to thicken structures." + icon = 'icons/mob/hud/actions_xeno.dmi' + icon_state = "upgrade_resin" + plasma_cost = 60 - if("refresh_ui") - . = TRUE +/obj/effect/alien/resin/design/remove + name = "Remove Design Node" + desc = "Channel our plasma to revert design node back to weeds." + icon = 'icons/mob/hud/actions_xeno.dmi' + icon_state = "remove_node" + plasma_cost = 0 diff --git a/colonialmarines.dme b/colonialmarines.dme index f9709a1bfd71..0a64b5ab92ae 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -2221,6 +2221,7 @@ #include "code\modules\mob\living\carbon\xenomorph\abilities\facehugger\facehugger_powers.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\hellhound\hellhound_abilities.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_abilities.dm" +#include "code\modules\mob\living\carbon\xenomorph\abilities\hivelord\hivelord_macros.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\king\king_abilities.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\king\king_macros.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\lesser_drone\lesser_drone_abilities.dm" diff --git a/icons/effects/mouse_pointer/designer/const_door_mouse.dmi b/icons/effects/mouse_pointer/designer/const_door_mouse.dmi new file mode 100644 index 000000000000..cc0fc5af7bfc Binary files /dev/null and b/icons/effects/mouse_pointer/designer/const_door_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/const_wall_mouse.dmi b/icons/effects/mouse_pointer/designer/const_wall_mouse.dmi new file mode 100644 index 000000000000..af1a68818431 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/const_wall_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/cost_door_mouse.dmi b/icons/effects/mouse_pointer/designer/cost_door_mouse.dmi new file mode 100644 index 000000000000..8165919b0645 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/cost_door_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/cost_wall_mouse.dmi b/icons/effects/mouse_pointer/designer/cost_wall_mouse.dmi new file mode 100644 index 000000000000..94968129d4a0 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/cost_wall_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/remove_mouse.dmi b/icons/effects/mouse_pointer/designer/remove_mouse.dmi new file mode 100644 index 000000000000..b39ca13591ca Binary files /dev/null and b/icons/effects/mouse_pointer/designer/remove_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/spd_door_mouse.dmi b/icons/effects/mouse_pointer/designer/spd_door_mouse.dmi new file mode 100644 index 000000000000..1c2de23f67b0 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/spd_door_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/spd_wall_mouse.dmi b/icons/effects/mouse_pointer/designer/spd_wall_mouse.dmi new file mode 100644 index 000000000000..994733a1c5f2 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/spd_wall_mouse.dmi differ diff --git a/icons/effects/mouse_pointer/designer/upgrade_mouse.dmi b/icons/effects/mouse_pointer/designer/upgrade_mouse.dmi new file mode 100644 index 000000000000..a03342775229 Binary files /dev/null and b/icons/effects/mouse_pointer/designer/upgrade_mouse.dmi differ diff --git a/icons/mob/xenos/effects.dmi b/icons/mob/xenos/effects.dmi index 7eef581cdf21..0d3706114566 100644 Binary files a/icons/mob/xenos/effects.dmi and b/icons/mob/xenos/effects.dmi differ diff --git a/icons/mob/xenos/structures.dmi b/icons/mob/xenos/structures.dmi index cae6db90357e..e90d965eccfd 100644 Binary files a/icons/mob/xenos/structures.dmi and b/icons/mob/xenos/structures.dmi differ