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/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..611d01747583 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
@@ -63,52 +63,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
@@ -117,579 +71,489 @@
delay = 0
//////////////////////////
-/// Designer... Paths. ///
+/// Change Design ///
//////////////////////////
-/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
-
- 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
+/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
- var/image/chosenMark
+/datum/action/xeno_action/onclick/change_design/use_ability(atom/Atom)
+ var/mob/living/carbon/xenomorph/xeno = owner
+ if(!xeno.check_state())
+ 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
+ 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")
+ )
- bound_xeno = xeno
- bound_weed = weeds
- hivenumber = xeno.hivenumber
+ var/choice
+ if(owner.client.prefs.no_radials_preference)
+ choice = tgui_input_list(owner, "Choose Design Option", "Pick", options, theme="hive_status")
+ else
+ choice = show_radial_menu(owner, owner?.client.get_eye(), options, radius = 50)
- RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire))
- RegisterSignal(bound_xeno, COMSIG_PARENT_QDELETING, PROC_REF(on_xeno_expire))
+ 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)
- set_hive_data(src, hivenumber)
- . = ..()
+ 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]."))
- if(bound_weed.hivenumber != bound_xeno.hivenumber)
- qdel(src)
+ xeno.update_icons()
+ button.overlays.Cut()
+ button.overlays += image(icon_file, button, xeno.selected_design.icon_state)
- 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
+// Below is UI for old players.
- for(xeno in hive.totalXenos)
- if(xeno.client)
- xeno.hud_set_design_marks()
- refresh_marker()
+/datum/action/xeno_action/onclick/change_design/give_to(mob/living/carbon/xenomorph/xeno)
+ . = ..()
-/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()
+ 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)
- if(!QDELETED(bound_xeno))
- bound_xeno.current_design.Remove(src)
- unregister_weed_expiration_signal_design()
- bound_xeno = null
- bound_weed = null
- chosenMark = null
- return ..()
+/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/refresh_marker()
- if(!chosenMark || !mark_meaning)
+/datum/action/xeno_action/onclick/change_design/ui_static_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
+ . = list()
-/obj/effect/alien/resin/design/proc/get_marker_icon_state()
- if(!mark_meaning)
- return null
- return mark_meaning.icon_state
+ 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/on_weed_expire()
- SIGNAL_HANDLER
- qdel(src)
+ 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/proc/on_xeno_expire()
- SIGNAL_HANDLER
- qdel(src)
+ .["design"] = design_list
-/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_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()
+ .["selected_design"] = xeno.selected_design
-/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()
+/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/proc/hud_set_queen_overwatch()
- return
+/datum/action/xeno_action/onclick/change_design/Destroy()
+ SStgui.close_uis(src)
+ return ..()
-/obj/effect/alien/resin/design/speed_node
- name = "Design Optimized Node (50)"
- icon_state = "static_speednode"
- plasma_cost = 50
+/datum/action/xeno_action/onclick/change_design/ui_state(mob/user)
+ return GLOB.always_state
-/obj/effect/alien/resin/design/speed_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 + "_speed"
- else
- ..()
+ var/mob/living/carbon/xenomorph/xeno = ui.user
+ if(!istype(xeno))
+ return
-/obj/effect/alien/resin/design/speed_node/get_marker_icon_state()
- if(!mark_meaning)
- return null
- return mark_meaning.icon_state + "_speed"
+ switch(action)
+ if("choose_design")
+ var/selected_type = text2path(params["type"])
+ if(!(selected_type in xeno.available_design))
+ return
-/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%")].")
+ 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/cost_node
- name = "Design Flexible Node (60)"
- icon_state = "static_costnode"
- plasma_cost = 60
+ if("refresh_ui")
+ . = TRUE
-/obj/effect/alien/resin/design/cost_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 + "_cost"
- else
- ..()
-
-/obj/effect/alien/resin/design/cost_node/get_marker_icon_state()
- if(!mark_meaning)
- return null
- return mark_meaning.icon_state + "_cost"
-
-/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%")].")
-
-/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
+/////////////////////////////
+/// Place Design ///
+/////////////////////////////
-/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++
+/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
-/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(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)
-
- var/turf/closed/wall/resin/Res = get_turf(Turf)
- if(istype(Res))
- Res.hivenumber = src.hivenumber
- set_hive_data(Res, Res.hivenumber)
-
- 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."))
-
- 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/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)
-
- 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."))
-
- qdel(src)
-
-/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 ..()
-
-/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."))
+ if(!action_cooldown_check())
return
- var/mob/living/carbon/xenomorph/xeno = user
-
- if(!can_begin_construction(xeno))
+ if(!xeno.check_state())
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]"))
+ if(mods["click_catcher"])
return
- 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."))
-
- begin_construction(xeno)
-
-/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
-
- var/mob/living/carbon/xenomorph/xeno = user
- begin_construction(xeno)
+ if(ismob(target_atom))
+ if(!can_see(xeno, target_atom, max_reach))
+ to_chat(xeno, SPAN_XENODANGER("We cannot see that location!"))
return
-
- //Do NOT call attack_hand here — that bypasses destruction
- to_chat(user, SPAN_NOTICE("You examine the node curiously, but nothing happens."))
- 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 ..()
-
-/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
-
- 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
-
- return total_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(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/Turf = get_turf(src)
- if(!istype(Turf))
- to_chat(xeno, SPAN_WARNING("This is not a valid location."))
- return FALSE
+ if(get_dist(xeno, target_atom) > max_reach)
+ to_chat(xeno, SPAN_WARNING("That's too far away!"))
+ return
- return TRUE
+ 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/begin_construction(mob/living/carbon/xenomorph/xeno)
- if(!can_begin_construction(xeno))
+ 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
- var/turf/Turf = get_turf(src)
- building = TRUE
+ if(target_weeds.hivenumber != xeno.hivenumber)
+ to_chat(xeno, SPAN_WARNING("These weeds do not belong to our hive; they reject our influence."))
+ return
- var/obj/effect/resin_construct/overlay
+ var/plasma_cost
+ if(xeno.selected_design && xeno.selected_design.plasma_cost)
+ plasma_cost = xeno.selected_design.plasma_cost
- 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)
+ 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
- build_overlay = overlay
+ if(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane))
+ var/turf/closed/wall/resin/wall = target_atom
- if(bound_weed.weed_strength >= WEED_LEVEL_HIVE)
- thick_build = TRUE
+ if(wall.hivenumber != xeno.hivenumber)
+ to_chat(xeno, SPAN_XENOWARNING("[wall] does not belong to our hive!"))
+ return
- if((xeno.caste_type in XENO_CONSTRUCT_NODE_BOOST) && !istype(xeno.strain, /datum/xeno_strain/designer))
- thick_build = TRUE
+ if(wall.upgrading_now) //<--- Prevent spam and waste of plasma
+ to_chat(xeno, SPAN_WARNING("This wall is already being reinforced!"))
+ return
- addtimer(CALLBACK(src, PROC_REF(complete_construction), Turf, mark_meaning, xeno), 4 SECONDS)
+ wall.upgrading_now = TRUE
-/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.")
+ 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)
-//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
+ 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
- var/obj/effect/alien/weeds/bound_weed
- var/old_hivenumber
+ wall.upgrading_now = FALSE
-/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))
+ else if(istype(target_atom, /obj/structure/mineral_door/resin))
+ var/obj/structure/mineral_door/resin/door = target_atom
-/turf/closed/wall/resin/weedbound/Destroy()
- if(bound_weed)
- UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING)
- bound_weed = null
+ if(door.hivenumber != xeno.hivenumber)
+ to_chat(xeno, SPAN_XENOWARNING("[door] does not belong to your hive!"))
+ 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)
+ if(door.upgrading_now)
+ to_chat(xeno, SPAN_WARNING("This door is already being reinforced!"))
+ return
- 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
-/turf/closed/wall/resin/weedbound/proc/spawn_nutriplasm(turf/Turf)
- return
+ else
+ to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin structures!"))
+ return
-/turf/closed/wall/resin/weedbound/proc/on_weed_expire()
- SIGNAL_HANDLER
+ if(!check_and_use_plasma_owner(plasma_cost))
+ return
- if(!old_hivenumber)
- ScrapeAway()
- return
+ 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)
- addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS)
+ target_atom.add_hiddenprint(xeno) //Tracks who reinforced it for admins
+ return TRUE
-/turf/closed/wall/resin/weedbound/proc/check_weed_replacement()
- var/turf/Turf = get_turf(src)
- if(!Turf)
- ScrapeAway()
+ if(xeno.try_toggle_resin_door(target_atom))
+ if(!check_and_use_plasma_owner())
+ return TRUE
return
- var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in Turf
-
- 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()
-
-/turf/closed/wall/resin/weedbound/normal/spawn_nutriplasm(turf/Turf)
- new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf)
-
-/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.")
-
-/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
+ 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/thick/spawn_nutriplasm(turf/Turf)
- new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf)
+ 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/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.")
+ if(target_node.bound_xeno != xeno)
+ to_chat(xeno, SPAN_XENOWARNING("You cannot remove a node placed by another sister!"))
+ return
-/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(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/obj/effect/alien/weeds/bound_weed
- var/old_hivenumber
+ 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.
-/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))
+ var/selected_design = xeno.selected_design
-/obj/structure/mineral_door/resin/weedbound/Destroy()
- if(bound_weed)
- UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING)
- bound_weed = null
+ 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/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(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(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 ..()
-/obj/structure/mineral_door/resin/weedbound/proc/spawn_nutriplasm(turf/Turf)
- return
-
-/obj/structure/mineral_door/resin/weedbound/proc/on_weed_expire()
- SIGNAL_HANDLER
+/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
- if(!old_hivenumber)
- Dismantle()
- return
+/mob/living/carbon/xenomorph/proc/try_toggle_resin_door(atom/target_atom)
+ if(!istype(target_atom, /obj/structure/mineral_door/resin))
+ return FALSE
- addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS)
+ var/obj/structure/mineral_door/resin/resin_door = target_atom
-/obj/structure/mineral_door/resin/weedbound/proc/check_weed_replacement()
- var/turf/Turf = get_turf(src)
- if(!Turf)
- Dismantle()
- return
+ if(resin_door.hivenumber != hivenumber)
+ to_chat(src, SPAN_XENOWARNING("This door does not belong to our hive!"))
+ return TRUE
- var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in 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."))
- 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()
+ return TRUE
-/obj/structure/mineral_door/resin/weedbound/normal/spawn_nutriplasm(turf/Turf)
- new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf)
+/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
-/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.")
+///////////////////////////////
+/// Change Node Marker ///
+///////////////////////////////
-/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
+/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
-/obj/structure/mineral_door/resin/weedbound/thick/spawn_nutriplasm(turf/Turf)
- new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf)
+/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
-/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.")
+/datum/action/xeno_action/onclick/toggle_design_icons/use_ability()
+ var/mob/living/carbon/xenomorph/xeno = owner
-/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
+ if (!istype(xeno))
+ return
-/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.")
+ if(!xeno.check_state(TRUE))
+ return
-/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
+ var/datum/action/xeno_action/activable/place_design/cAction = get_action(xeno, /datum/action/xeno_action/activable/place_design)
-/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.")
+ if(!istype(cAction))
+ return
-/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
+ cAction.design_toggle = !cAction.design_toggle
-/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
+ 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
+
+ button.overlays.Cut()
+ button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, action_icon_result)
+ return ..()
//////////////////////////
// Greater Resin Surge. //
//////////////////////////
-/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)
-
/datum/action/xeno_action/activable/greater_resin_surge
name = "Greater Resin Surge (250)"
action_icon_state = "greater_resin_surge"
@@ -748,517 +612,596 @@
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
+/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"
+
+//////////////////////////
+/// 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
+
+ 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
+
+ var/image/chosenMark
+
+/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)
+ . = ..()
+
+ if(bound_weed.hivenumber != bound_xeno.hivenumber)
+ qdel(src)
+
+ if(xeno.selected_design_mark)
+ mark_meaning = new xeno.selected_design_mark
+
+ 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
+
+ for(xeno in hive.totalXenos)
+ if(xeno.client)
+ xeno.hud_set_design_marks()
+ refresh_marker()
+
+/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(!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
+
+ 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/effect/alien/resin/design/proc/get_marker_icon_state()
+ if(!mark_meaning)
+ return null
+ return mark_meaning.icon_state
- if(!ispath(animation_type, /obj/effect/resin_construct/fastweak))
- return
- var/obj/effect/resin_construct/fastweak/animation = new animation_type(target_turf)
+/obj/effect/alien/resin/design/proc/on_weed_expire()
+ SIGNAL_HANDLER
+ qdel(src)
- addtimer(CALLBACK(animation, TYPE_PROC_REF(/obj/effect/resin_construct/fastweak, delete_animation)), 2 SECONDS)
+/obj/effect/alien/resin/design/proc/on_xeno_expire()
+ SIGNAL_HANDLER
+ qdel(src)
-/obj/effect/resin_construct/fastweak/proc/delete_animation()
- if(!QDELETED(src))
+/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)
-/////////////////////////////
-/// Place Design ///
-/////////////////////////////
+/obj/effect/alien/resin/design/proc/unregister_weed_expiration_signal_design()
+ if(bound_weed)
+ UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING)
-/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
+/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()
-/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/proc/hud_set_queen_overwatch()
+ 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."))
- return
+/obj/effect/alien/resin/design/speed_node
+ name = "Design Optimized Node (50)"
+ icon_state = "static_speednode"
+ plasma_cost = 50
- if(!action_cooldown_check())
+/obj/effect/alien/resin/design/speed_node/refresh_marker()
+ if(!chosenMark || !mark_meaning)
return
- if(!xeno.check_state())
- 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
+ ..()
- if(mods["click_catcher"])
- return
+/obj/effect/alien/resin/design/speed_node/get_marker_icon_state()
+ if(!mark_meaning)
+ return null
+ return mark_meaning.icon_state + "_speed"
- 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
+/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%")].")
- 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/cost_node
+ name = "Design Flexible Node (60)"
+ icon_state = "static_costnode"
+ plasma_cost = 60
- 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!"))
+/obj/effect/alien/resin/design/cost_node/refresh_marker()
+ if(!chosenMark || !mark_meaning)
return
- if(target_weeds.hivenumber != xeno.hivenumber)
- to_chat(xeno, SPAN_WARNING("These weeds do not belong to our hive; they reject our influence."))
- 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/plasma_cost
- if(xeno.selected_design && xeno.selected_design.plasma_cost)
- plasma_cost = xeno.selected_design.plasma_cost
+/obj/effect/alien/resin/design/cost_node/get_marker_icon_state()
+ if(!mark_meaning)
+ return null
+ return mark_meaning.icon_state + "_cost"
- 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/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%")].")
- if(istype(target_atom, /turf/closed/wall/resin) || istype(target_atom, /turf/closed/wall/resin/membrane))
- var/turf/closed/wall/resin/wall = target_atom
+/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(wall.hivenumber != xeno.hivenumber)
- to_chat(xeno, SPAN_XENOWARNING("[wall] does not belong to our hive!"))
- 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(wall.upgrading_now) //<--- Prevent spam and waste of plasma
- to_chat(xeno, SPAN_WARNING("This wall is already being reinforced!"))
- return
+/obj/effect/alien/resin/design/construct_node/proc/complete_construction(turf/Turf, design_mark, mob/living/carbon/xenomorph/xeno)
+ if(QDELETED(src) || QDELETED(Turf))
+ return
- wall.upgrading_now = TRUE
+ if(build_overlay && !QDELETED(build_overlay))
+ qdel(build_overlay)
+ build_overlay = null
- 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)
+ building = FALSE
- 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)
+ 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
- to_chat(xeno, SPAN_XENOWARNING("[wall] can't be made thicker."))
- return
-
- wall.upgrading_now = FALSE
-
- else if(istype(target_atom, /obj/structure/mineral_door/resin))
- var/obj/structure/mineral_door/resin/door = target_atom
+ placed = Turf.place_on_top(/turf/closed/wall/resin/weedbound/normal)
- if(door.hivenumber != xeno.hivenumber)
- to_chat(xeno, SPAN_XENOWARNING("[door] does not belong to your hive!"))
- return
+ var/turf/closed/wall/resin/Res = get_turf(Turf)
+ if(istype(Res))
+ Res.hivenumber = src.hivenumber
+ set_hive_data(Res, Res.hivenumber)
- if(door.upgrading_now)
- to_chat(xeno, SPAN_WARNING("This door is already being reinforced!"))
- 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(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(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
- if(xeno.try_toggle_resin_door(door))
- if(!check_and_use_plasma_owner())
- return TRUE
- return
- return
+ new_structure = new /obj/structure/mineral_door/resin/weedbound/normal(Turf)
+
+ 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)
+ to_chat(xeno, SPAN_NOTICE("We create a weedbound door."))
+ playsound(new_structure, "alien_resin_build", 25)
else
- to_chat(xeno, SPAN_XENOWARNING("We can only upgrade resin structures!"))
- return
+ to_chat(xeno, SPAN_WARNING("A door already exists here."))
- if(!check_and_use_plasma_owner(plasma_cost))
- return
+ qdel(src)
- 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/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 ..()
- target_atom.add_hiddenprint(xeno) //Tracks who reinforced it for admins
- return TRUE
+/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(xeno.try_toggle_resin_door(target_atom))
- if(!check_and_use_plasma_owner())
- return TRUE
+ var/mob/living/carbon/xenomorph/xeno = user
+
+ if(!can_begin_construction(xeno))
+ 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
- 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
+ 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(target_node.hivenumber != xeno.hivenumber)
- to_chat(xeno, SPAN_XENOWARNING("This node does not belong to your hive!"))
- return
+ begin_construction(xeno)
- if(target_node.bound_xeno != xeno)
- to_chat(xeno, SPAN_XENOWARNING("You cannot remove a node placed by another sister!"))
+/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
+
+ var/mob/living/carbon/xenomorph/xeno = user
+ begin_construction(xeno)
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)
+ //Do NOT call attack_hand here — that bypasses destruction
+ to_chat(user, SPAN_NOTICE("You examine the node curiously, but nothing happens."))
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/selected_design = xeno.selected_design
+/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(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.
+/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
- 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(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(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 ..()
+ return total_plasma_cost
-/datum/action/xeno_action/activable/place_design/proc/can_remote_build()
- if(!locate(/obj/effect/alien/weeds) in get_turf(owner))
+/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
- return TRUE
-/mob/living/carbon/xenomorph/proc/try_toggle_resin_door(atom/target_atom)
- if(!istype(target_atom, /obj/structure/mineral_door/resin))
+ if(xeno.hivenumber != src.hivenumber)
+ to_chat(xeno, SPAN_WARNING("This construct node does not belong to your hive."))
return FALSE
- var/obj/structure/mineral_door/resin/resin_door = target_atom
-
- if(resin_door.hivenumber != hivenumber)
- to_chat(src, SPAN_XENOWARNING("This door does not belong to our hive!"))
- return TRUE
+ if(!mark_meaning)
+ to_chat(xeno, SPAN_WARNING("This node has no valid design selected."))
+ return FALSE
- 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."))
+ var/turf/Turf = get_turf(src)
+ if(!istype(Turf))
+ to_chat(xeno, SPAN_WARNING("This is not a valid location."))
+ return FALSE
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
+/obj/effect/alien/resin/design/construct_node/proc/begin_construction(mob/living/carbon/xenomorph/xeno)
+ if(!can_begin_construction(xeno))
+ return
-///////////////////////////////
-/// Change Node Marker ///
-///////////////////////////////
+ var/turf/Turf = get_turf(src)
+ building = TRUE
-/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)
+ var/obj/effect/resin_construct/overlay
-/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
+ 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)
-/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
+ build_overlay = overlay
-/datum/action/xeno_action/onclick/toggle_design_icons/use_ability()
- var/mob/living/carbon/xenomorph/xeno = owner
+ if(bound_weed.weed_strength >= WEED_LEVEL_HIVE)
+ thick_build = TRUE
- if (!istype(xeno))
+ if((xeno.caste_type in XENO_CONSTRUCT_NODE_BOOST) && !istype(xeno.strain, /datum/xeno_strain/designer))
+ thick_build = TRUE
+
+ addtimer(CALLBACK(src, PROC_REF(complete_construction), Turf, mark_meaning, xeno), 4 SECONDS)
+
+/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.")
+
+//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
+
+ var/obj/effect/alien/weeds/bound_weed
+ var/old_hivenumber
+
+/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))
- if(!xeno.check_state(TRUE))
+/turf/closed/wall/resin/weedbound/Destroy()
+ if(bound_weed)
+ UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING)
+ bound_weed = null
+
+ 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)
+
+ return ..()
+
+/turf/closed/wall/resin/weedbound/proc/spawn_nutriplasm(turf/Turf)
+ return
+
+/turf/closed/wall/resin/weedbound/proc/on_weed_expire()
+ SIGNAL_HANDLER
+
+ if(!old_hivenumber)
+ ScrapeAway()
return
- var/datum/action/xeno_action/activable/place_design/cAction = get_action(xeno, /datum/action/xeno_action/activable/place_design)
+ addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS)
- if(!istype(cAction))
+/turf/closed/wall/resin/weedbound/proc/check_weed_replacement()
+ var/turf/Turf = get_turf(src)
+ if(!Turf)
+ ScrapeAway()
return
- cAction.design_toggle = !cAction.design_toggle
+ var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in Turf
- 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
+ if(new_weed && new_weed.hivenumber == old_hivenumber)
+ bound_weed = new_weed
+ RegisterSignal(bound_weed, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_expire))
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
+ playsound(src, "alien_resin_break", 25)
+ ScrapeAway()
- button.overlays.Cut()
- button.overlays += image('icons/mob/hud/actions_xeno.dmi', button, action_icon_result)
- return ..()
+/turf/closed/wall/resin/weedbound/normal/spawn_nutriplasm(turf/Turf)
+ new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf)
-//////////////////////////
-/// Change Design ///
-//////////////////////////
+/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/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)
+/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
-/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
+/turf/closed/wall/resin/weedbound/thick/spawn_nutriplasm(turf/Turf)
+ new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf)
-/datum/action/xeno_action/onclick/change_design/use_ability(atom/Atom)
- var/mob/living/carbon/xenomorph/xeno = owner
- if(!xeno.check_state())
- return
+/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.")
- 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")
- )
+/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/choice
- if(owner.client.prefs.no_radials_preference)
- choice = tgui_input_list(owner, "Choose Design Option", "Pick", options, theme="hive_status")
- else
- choice = show_radial_menu(owner, owner?.client.get_eye(), options, radius = 50)
+ var/obj/effect/alien/weeds/bound_weed
+ var/old_hivenumber
- 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)
+/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))
- 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/Destroy()
+ if(bound_weed)
+ UnregisterSignal(bound_weed, COMSIG_PARENT_QDELETING)
+ bound_weed = null
- xeno.update_icons()
- button.overlays.Cut()
- button.overlays += image(icon_file, button, xeno.selected_design.icon_state)
+ 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)
return ..()
-// Below is UI for old players.
+/obj/structure/mineral_door/resin/weedbound/proc/spawn_nutriplasm(turf/Turf)
+ return
-/datum/action/xeno_action/onclick/change_design/give_to(mob/living/carbon/xenomorph/xeno)
- . = ..()
+/obj/structure/mineral_door/resin/weedbound/proc/on_weed_expire()
+ SIGNAL_HANDLER
- 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)
+ if(!old_hivenumber)
+ Dismantle()
+ return
-/datum/action/xeno_action/onclick/change_design/ui_assets(mob/user)
- return list(get_asset_datum(/datum/asset/spritesheet/choose_design))
+ addtimer(CALLBACK(src, PROC_REF(check_weed_replacement)), 1 DECISECONDS)
-/datum/action/xeno_action/onclick/change_design/ui_static_data(mob/user)
- var/mob/living/carbon/xenomorph/xeno = user
- if(!istype(xeno))
+/obj/structure/mineral_door/resin/weedbound/proc/check_weed_replacement()
+ var/turf/Turf = get_turf(src)
+ if(!Turf)
+ Dismantle()
return
- . = list()
-
- var/list/design_list = list()
- for(var/obj/effect/alien/resin/design/design as anything in xeno.available_design)
- var/list/entry = list()
+ var/obj/effect/alien/weeds/new_weed = locate(/obj/effect/alien/weeds) in Turf
- entry["name"] = initial(design.name)
- entry["desc"] = initial(design.desc)
- entry["image"] = replacetext(initial(design.icon_state), " ", "-")
- entry["id"] = "[design]"
- design_list += list(entry)
+ 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()
- .["design"] = design_list
+/obj/structure/mineral_door/resin/weedbound/normal/spawn_nutriplasm(turf/Turf)
+ new /obj/effect/alien/resin/sticky/weak_nutriplasm(Turf)
-/datum/action/xeno_action/onclick/change_design/ui_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("You sense that this resin door will collapse if the weeds it is merged with disappear.")
- . = list()
- .["selected_design"] = xeno.selected_design
+/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
-/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/structure/mineral_door/resin/weedbound/thick/spawn_nutriplasm(turf/Turf)
+ new /obj/effect/alien/resin/sticky/strong_nutriplasm(Turf)
-/datum/action/xeno_action/onclick/change_design/Destroy()
- SStgui.close_uis(src)
- 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.")
-/datum/action/xeno_action/onclick/change_design/ui_state(mob/user)
- return GLOB.always_state
+/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
-/datum/action/xeno_action/onclick/change_design/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+/obj/effect/alien/resin/sticky/weak_nutriplasm/get_examine_text(mob/user)
. = ..()
- if(.)
- return
+ 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.")
- var/mob/living/carbon/xenomorph/xeno = ui.user
- if(!istype(xeno))
- return
+/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
- switch(action)
- if("choose_design")
- var/selected_type = text2path(params["type"])
- if(!(selected_type in xeno.available_design))
- return
+/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.")
- 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 (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
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 06628b4475f4..ce5ceb1c965a 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -2227,6 +2227,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"