mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-29 06:57:42 +01:00
Merge remote-tracking branch 'upstream/master' into tgui-portable-pump
This commit is contained in:
@@ -152,7 +152,7 @@
|
||||
return 1
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user)
|
||||
if(..())
|
||||
@@ -163,62 +163,62 @@
|
||||
return
|
||||
|
||||
add_fingerprint(user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state)
|
||||
/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
user.set_machine(src)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state)
|
||||
ui = new(user, src, ui_key, "AtmosMixer", name, 330, 165, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["on"] = on
|
||||
data["pressure"] = round(target_pressure)
|
||||
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
|
||||
data["node1_concentration"] = round(node1_concentration*100)
|
||||
data["node2_concentration"] = round(node2_concentration*100)
|
||||
/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"on" = on,
|
||||
"pressure" = round(target_pressure, 0.01),
|
||||
"max_pressure" = MAX_OUTPUT_PRESSURE,
|
||||
"node1_concentration" = round(node1_concentration * 100),
|
||||
"node2_concentration" = round(node2_concentration * 100)
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list)
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["power"])
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
|
||||
. = TRUE
|
||||
if(href_list["pressure"])
|
||||
var/pressure = href_list["pressure"]
|
||||
if(pressure == "max")
|
||||
pressure = MAX_OUTPUT_PRESSURE
|
||||
. = TRUE
|
||||
else if(pressure == "input")
|
||||
pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null
|
||||
if(!isnull(pressure) && !..())
|
||||
. = TRUE
|
||||
else if(text2num(pressure) != null)
|
||||
pressure = text2num(pressure)
|
||||
. = TRUE
|
||||
if(.)
|
||||
target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
|
||||
if(href_list["node1"])
|
||||
var/value = text2num(href_list["node1"])
|
||||
node1_concentration = max(0, min(1, node1_concentration + value))
|
||||
node2_concentration = max(0, min(1, node2_concentration - value))
|
||||
investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos")
|
||||
. = TRUE
|
||||
if(href_list["node2"])
|
||||
var/value = text2num(href_list["node2"])
|
||||
node2_concentration = max(0, min(1, node2_concentration + value))
|
||||
node1_concentration = max(0, min(1, node1_concentration - value))
|
||||
investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos")
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("power")
|
||||
toggle()
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
|
||||
return TRUE
|
||||
|
||||
update_icon()
|
||||
SSnanoui.update_uis(src)
|
||||
if("set_node")
|
||||
if(params["node_name"] == "Node 1")
|
||||
node1_concentration = clamp(round(text2num(params["concentration"]), 0.01), 0, 1)
|
||||
node2_concentration = round(1 - node1_concentration, 0.01)
|
||||
investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos")
|
||||
return TRUE
|
||||
else
|
||||
node2_concentration = clamp(round(text2num(params["concentration"]), 0.01), 0, 1)
|
||||
node1_concentration = round(1 - node2_concentration, 0.01)
|
||||
investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos")
|
||||
return TRUE
|
||||
|
||||
if("max_pressure")
|
||||
target_pressure = MAX_OUTPUT_PRESSURE
|
||||
. = TRUE
|
||||
|
||||
if("min_pressure")
|
||||
target_pressure = 0
|
||||
. = TRUE
|
||||
|
||||
if("custom_pressure")
|
||||
target_pressure = clamp(text2num(params["pressure"]), 0, MAX_OUTPUT_PRESSURE)
|
||||
. = TRUE
|
||||
if(.)
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
|
||||
|
||||
/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params)
|
||||
if(istype(W, /obj/item/pen))
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#define MARTIAL_COMBO_FAIL 0 // If the combo failed
|
||||
#define MARTIAL_COMBO_CONTINUE 1 // If the combo should continue
|
||||
#define MARTIAL_COMBO_DONE 2 // If the combo is successful and done
|
||||
#define MARTIAL_COMBO_DONE_NO_CLEAR 3 // If the combo is successful and done but the others should have a chance to finish
|
||||
#define MARTIAL_COMBO_DONE_BASIC_HIT 4 // If the combo should do a basic hit after it's done
|
||||
#define MARTIAL_COMBO_DONE_CLEAR_COMBOS 5 // If the combo should do a basic hit after it's done
|
||||
|
||||
#define MARTIAL_ARTS_CANNOT_USE -1
|
||||
|
||||
#define MARTIAL_COMBO_STEP_HARM "Harm"
|
||||
#define MARTIAL_COMBO_STEP_DISARM "Disarm"
|
||||
#define MARTIAL_COMBO_STEP_GRAB "Grab"
|
||||
#define MARTIAL_COMBO_STEP_HELP "Help"
|
||||
|
||||
// A check used for all act types. Such as disarm_act
|
||||
#define MARTIAL_ARTS_ACT_CHECK if((. = ..()) != FALSE) return .
|
||||
@@ -208,6 +208,7 @@
|
||||
#define isguardian(A) (istype((A), /mob/living/simple_animal/hostile/guardian))
|
||||
#define isnymph(A) (istype((A), /mob/living/simple_animal/diona))
|
||||
#define ishostile(A) (istype(A, /mob/living/simple_animal/hostile))
|
||||
#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider))
|
||||
|
||||
#define issilicon(A) (istype((A), /mob/living/silicon))
|
||||
#define isAI(A) (istype((A), /mob/living/silicon/ai))
|
||||
|
||||
@@ -92,6 +92,7 @@ GLOBAL_VAR(map_name) // Self explanatory
|
||||
GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc
|
||||
|
||||
GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled
|
||||
GLOBAL_VAR_INIT(pending_server_update, FALSE)
|
||||
|
||||
//Database connections
|
||||
//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.).
|
||||
|
||||
Vendored
+5
-1
@@ -1,3 +1,5 @@
|
||||
#define AIR_ALARM_DATA_CACHE_DURATION 10 SECONDS
|
||||
|
||||
GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new())
|
||||
|
||||
/datum/repository/air_alarm/proc/air_alarm_data(var/list/monitored_alarms, var/refresh = 0, var/obj/machinery/alarm/passed_alarm)
|
||||
@@ -8,7 +10,7 @@ GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new())
|
||||
cache_entry = new/datum/cache_entry
|
||||
cache_data = cache_entry
|
||||
|
||||
if(!refresh)
|
||||
if(!refresh && cache_entry.timestamp + AIR_ALARM_DATA_CACHE_DURATION > world.time)
|
||||
return cache_entry.data
|
||||
|
||||
if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time
|
||||
@@ -29,3 +31,5 @@ GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new())
|
||||
|
||||
/datum/repository/air_alarm/proc/update_cache(var/obj/machinery/alarm/alarm)
|
||||
return air_alarm_data(refresh = 1, passed_alarm = alarm)
|
||||
|
||||
#undef AIR_ALARM_DATA_CACHE_DURATION
|
||||
|
||||
@@ -204,3 +204,7 @@
|
||||
D.mutations.Add(BREATHLESS)
|
||||
D.atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
D.minbodytemp = 0
|
||||
|
||||
/datum/dog_fashion/head/fried_vox_empty
|
||||
name = "Colonel REAL_NAME"
|
||||
desc = "Keep away from live vox."
|
||||
|
||||
+3
-2
@@ -35,6 +35,7 @@
|
||||
var/list/restricted_roles = list()
|
||||
|
||||
var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button.
|
||||
var/datum/martial_art/martial_art
|
||||
|
||||
var/role_alt_title
|
||||
|
||||
@@ -1102,8 +1103,8 @@
|
||||
special_role = null
|
||||
to_chat(current,"<span class='userdanger'>Your infernal link has been severed! You are no longer a devil!</span>")
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/fireball/hellish)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/summon_contract)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater)
|
||||
RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended)
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
/obj/item/organ/internal/cyberimp/eyes/shield,
|
||||
/obj/item/organ/internal/cyberimp/eyes/hud/security,
|
||||
/obj/item/organ/internal/cyberimp/eyes/xray,
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun,
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened,
|
||||
/obj/item/organ/internal/cyberimp/chest/nutriment/plus,
|
||||
/obj/item/organ/internal/cyberimp/arm/combat/centcom
|
||||
)
|
||||
|
||||
+157
-68
@@ -24,6 +24,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
user.face_atom(A)
|
||||
return FALSE
|
||||
|
||||
/datum/click_intercept/proc_holder
|
||||
var/obj/effect/proc_holder/spell
|
||||
|
||||
/datum/click_intercept/proc_holder/New(client/C, obj/effect/proc_holder/spell_to_cast)
|
||||
. = ..()
|
||||
spell = spell_to_cast
|
||||
|
||||
/datum/click_intercept/proc_holder/InterceptClickOn(user, params, atom/object)
|
||||
spell.InterceptClickOn(user, params, object)
|
||||
|
||||
/datum/click_intercept/proc_holder/quit()
|
||||
spell.remove_ranged_ability(spell.ranged_ability_user)
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg)
|
||||
if(!user || !user.client)
|
||||
return
|
||||
@@ -32,7 +46,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
user.ranged_ability.remove_ranged_ability(user)
|
||||
user.ranged_ability = src
|
||||
ranged_ability_user = user
|
||||
user.client.click_intercept = user.ranged_ability
|
||||
user.client.click_intercept = new /datum/click_intercept/proc_holder(user.client, user.ranged_ability)
|
||||
add_mousepointer(user.client)
|
||||
active = TRUE
|
||||
if(msg)
|
||||
@@ -48,15 +62,17 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
C.mouse_pointer_icon = initial(C.mouse_pointer_icon)
|
||||
|
||||
/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg)
|
||||
if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability
|
||||
if(!user || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability
|
||||
return
|
||||
user.ranged_ability = null
|
||||
ranged_ability_user = null
|
||||
user.client.click_intercept = null
|
||||
remove_mousepointer(user.client)
|
||||
active = FALSE
|
||||
if(msg)
|
||||
to_chat(user, msg)
|
||||
if(user.client)
|
||||
qdel(user.client.click_intercept)
|
||||
user.client.click_intercept = null
|
||||
remove_mousepointer(user.client)
|
||||
if(msg)
|
||||
to_chat(user, msg)
|
||||
update_icon()
|
||||
|
||||
/obj/effect/proc_holder/spell
|
||||
@@ -114,10 +130,14 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
|
||||
var/sound = null //The sound the spell makes when it is cast
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
|
||||
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
|
||||
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
|
||||
return 0
|
||||
/* Checks if the user can cast the spell
|
||||
* @param charge_check If the proc should do the cooldown check
|
||||
* @param start_recharge If the proc should set the cooldown
|
||||
* @param user The caster of the spell
|
||||
*/
|
||||
/obj/effect/proc_holder/spell/proc/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell
|
||||
if(!can_cast(user, charge_check, TRUE))
|
||||
return FALSE
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/caster = user
|
||||
@@ -126,49 +146,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
caster.reset_perspective(0)
|
||||
return 0
|
||||
|
||||
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
|
||||
return 0
|
||||
|
||||
if(!skipcharge)
|
||||
switch(charge_type)
|
||||
if("recharge")
|
||||
if(charge_counter < charge_max)
|
||||
to_chat(user, still_recharging_msg)
|
||||
return 0
|
||||
if("charges")
|
||||
if(!charge_counter)
|
||||
to_chat(user, "<span class='notice'>[name] has no charges left.</span>")
|
||||
return 0
|
||||
|
||||
if(!ghost)
|
||||
if(user.stat && !stat_allowed)
|
||||
to_chat(user, "<span class='notice'>You can't cast this spell while incapacitated.</span>")
|
||||
return 0
|
||||
if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled())
|
||||
to_chat(user, "Mmmf mrrfff!")
|
||||
return 0
|
||||
|
||||
var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list()))
|
||||
if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/obj/item/clothing/robe = H.wear_suit
|
||||
var/obj/item/clothing/hat = H.head
|
||||
var/obj/item/clothing/shoes = H.shoes
|
||||
if(!robe || !hat || !shoes)
|
||||
to_chat(user, "<span class='notice'>Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.</span>")
|
||||
return 0
|
||||
if(!robe.magical || !hat.magical || !shoes.magical)
|
||||
to_chat(user, "<span class='notice'>Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.</span>")
|
||||
return 0
|
||||
else if(!ishuman(user))
|
||||
if(clothes_req || human_req)
|
||||
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
|
||||
return 0
|
||||
if(nonabstract_req && (isbrain(user) || ispAI(user)))
|
||||
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
|
||||
return 0
|
||||
|
||||
if(!skipcharge)
|
||||
if(start_recharge)
|
||||
switch(charge_type)
|
||||
if("recharge")
|
||||
charge_counter = 0 //doesn't start recharging until the targets selecting ends
|
||||
@@ -442,6 +420,100 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click
|
||||
var/click_radius = 1 // How big the radius around the clicked atom is to find a suitable target. -1 is only the selected atom is considered
|
||||
var/selection_activated_message = "<span class='notice'>Click on a target to cast the spell.</span>"
|
||||
var/selection_deactivated_message = "<span class='notice'>You choose to not cast this spell.</span>"
|
||||
var/allowed_type = /mob/living // Which type the targets have to be
|
||||
var/auto_target_single = TRUE // If the spell should auto select a target if only one is found
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/Click()
|
||||
var/mob/living/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
|
||||
if(active)
|
||||
remove_ranged_ability(user, selection_deactivated_message)
|
||||
else
|
||||
if(cast_check(TRUE, FALSE, user))
|
||||
if(auto_target_single && attempt_auto_target(user))
|
||||
return
|
||||
|
||||
add_ranged_ability(user, selection_activated_message)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src] is not ready to be used yet.</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/proc/attempt_auto_target(mob/user)
|
||||
var/atom/target
|
||||
for(var/atom/A in view_or_range(range, user, selection_type))
|
||||
if(valid_target(A, user))
|
||||
if(target)
|
||||
return FALSE // Two targets found. ABORT
|
||||
target = A
|
||||
|
||||
if(target && cast_check(TRUE, TRUE, user)) // Singular target found. Cast it instantly
|
||||
to_chat(user, "<span class='warning'>Only one target found. Casting [src] on [target]!</span>")
|
||||
perform(list(target), user = user)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/InterceptClickOn(mob/living/user, params, atom/A)
|
||||
if(..() || !cast_check(TRUE, TRUE, user))
|
||||
remove_ranged_ability(user)
|
||||
revert_cast(user)
|
||||
return TRUE
|
||||
|
||||
var/list/targets = list()
|
||||
if(valid_target(A, user))
|
||||
targets.Add(A)
|
||||
|
||||
if((!max_targets || max_targets > targets.len) && click_radius >= 0)
|
||||
var/list/found_others = list()
|
||||
for(var/atom/target in range(click_radius, A))
|
||||
if(valid_target(target, user))
|
||||
found_others |= target
|
||||
if(!max_targets)
|
||||
targets.Add(found_others)
|
||||
else
|
||||
if(max_targets <= found_others.len + targets.len)
|
||||
targets.Add(found_others)
|
||||
else
|
||||
switch(random_target_priority) //Add in the rest
|
||||
if(TARGET_RANDOM)
|
||||
while(targets.len < max_targets && found_others.len) // Add the others
|
||||
targets.Add(pick_n_take(found_others))
|
||||
if(TARGET_CLOSEST)
|
||||
var/list/distances = list()
|
||||
for(var/target in found_others)
|
||||
distances[target] = get_dist(user, target)
|
||||
sortTim(distances, /proc/cmp_numeric_asc, TRUE) // Sort on distance
|
||||
for(var/target in distances)
|
||||
targets.Add(target)
|
||||
if(targets.len >= max_targets)
|
||||
break
|
||||
|
||||
|
||||
if(!targets.len)
|
||||
to_chat(user, "<span class='warning'>No suitable target found.</span>")
|
||||
revert_cast(user)
|
||||
return FALSE
|
||||
|
||||
perform(targets, user = user)
|
||||
remove_ranged_ability(user)
|
||||
return TRUE
|
||||
|
||||
/* Checks if a target is valid
|
||||
* Should not include to_chats or other types of messages since this is used often on tons of targets.
|
||||
* @param target The target to check
|
||||
* @param user The user of the spell
|
||||
*/
|
||||
/obj/effect/proc_holder/spell/targeted/click/proc/valid_target(target, user)
|
||||
return istype(target, allowed_type) && (include_user || target != user) && \
|
||||
(target in view_or_range(range, user, selection_type))
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/choose_targets(mob/living/user, atom/A) // Not used
|
||||
return
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr)
|
||||
var/list/targets = list()
|
||||
|
||||
@@ -475,30 +547,39 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
qdel(dummy)
|
||||
return 1
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list))
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
|
||||
return 0
|
||||
|
||||
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
|
||||
return 0
|
||||
|
||||
switch(charge_type)
|
||||
if("recharge")
|
||||
if(charge_counter < charge_max)
|
||||
return 0
|
||||
if("charges")
|
||||
if(!charge_counter)
|
||||
return 0
|
||||
|
||||
if(user.stat && !stat_allowed)
|
||||
return 0
|
||||
if(charge_check)
|
||||
switch(charge_type)
|
||||
if("recharge")
|
||||
if(charge_counter < charge_max)
|
||||
if(show_message)
|
||||
to_chat(user, still_recharging_msg)
|
||||
return 0
|
||||
if("charges")
|
||||
if(!charge_counter)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>[name] has no charges left.</span>")
|
||||
return 0
|
||||
if(!ghost)
|
||||
if(user.stat && !stat_allowed)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>You can't cast this spell while incapacitated.</span>")
|
||||
return 0
|
||||
if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled())
|
||||
if(show_message)
|
||||
to_chat(user, "Mmmf mrrfff!")
|
||||
return 0
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
|
||||
if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled())
|
||||
return 0
|
||||
|
||||
var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list
|
||||
var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list)
|
||||
if(clothes_req && !clothcheck && !clothcheck2) //clothes check
|
||||
@@ -506,12 +587,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
var/obj/item/clothing/hat = H.head
|
||||
var/obj/item/clothing/shoes = H.shoes
|
||||
if(!robe || !hat || !shoes)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.</span>")
|
||||
return 0
|
||||
if(!robe.magical || !hat.magical || !shoes.magical)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.</span>")
|
||||
return 0
|
||||
else
|
||||
if(clothes_req || human_req)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>This spell can only be cast by humans!</span>")
|
||||
return 0
|
||||
if(nonabstract_req && (isbrain(user) || ispAI(user)))
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='notice'>This spell can only be cast by physical beings!</span>")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr)
|
||||
var/thearea = before_cast(targets)
|
||||
if(!thearea || !cast_check(1))
|
||||
if(!thearea || !cast_check(TRUE, FALSE, user))
|
||||
revert_cast()
|
||||
return
|
||||
invocation(thearea)
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/chaplain_bless
|
||||
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless
|
||||
name = "Bless"
|
||||
desc = "Blesses a single person."
|
||||
|
||||
school = "transmutation"
|
||||
charge_max = 60
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
invocation = "none"
|
||||
invocation_type = "none"
|
||||
|
||||
max_targets = 1
|
||||
include_user = 0
|
||||
humans_only = 1
|
||||
|
||||
include_user = FALSE
|
||||
allowed_type = /mob/living/carbon/human
|
||||
selection_activated_message = "<span class='notice'>You prepare a blessing. Click on a target to start blessing.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>The crew will be blessed another time.</span>"
|
||||
range = 1
|
||||
click_radius = -1 // Only precision clicking
|
||||
cooldown_min = 20
|
||||
action_icon_state = "shield"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/chaplain_bless/cast(list/targets, mob/living/user = usr, distanceoverride)
|
||||
return target.mind && target.ckey && !target.stat
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/cast(list/targets, mob/living/user = usr)
|
||||
if(!istype(user))
|
||||
to_chat(user, "Somehow, you are not a living mob. This should never happen. Report this bug.")
|
||||
revert_cast()
|
||||
@@ -35,32 +41,7 @@
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/target = targets[range]
|
||||
|
||||
if(!istype(target))
|
||||
to_chat(user, "No target.")
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
|
||||
to_chat(user, "[target] is too far away!")
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
if(!target.mind)
|
||||
to_chat(user, "[target] appears to be catatonic. Your blessing would have no effect.")
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
if(!target.ckey)
|
||||
to_chat(user, "[target] appears to be too out of it to benefit from this.")
|
||||
revert_cast()
|
||||
return
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user, "[target] is already dead. There is no point.")
|
||||
revert_cast()
|
||||
return
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
spawn(0) // allows cast to complete even if recipient ignores the prompt
|
||||
if(alert(target, "[user] wants to bless you, in the name of [user.p_their()] religion. Accept?", "Accept Blessing?", "Yes", "No") == "Yes") // prevents forced conversions
|
||||
|
||||
@@ -21,13 +21,18 @@
|
||||
action_background_icon_state = "bg_demon"
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/summon_contract
|
||||
/obj/effect/proc_holder/spell/targeted/click/summon_contract
|
||||
name = "Summon infernal contract"
|
||||
desc = "Skip making a contract by hand, just do it by magic."
|
||||
invocation_type = "whisper"
|
||||
invocation = "Just sign on the dotted line."
|
||||
include_user = 0
|
||||
selection_activated_message = "<span class='notice'>You prepare a detailed contract. Click on a target to summon the contract in his hands.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>You archive the contract for later use.</span>"
|
||||
include_user = FALSE
|
||||
range = 5
|
||||
auto_target_single = FALSE // Prevent an accidental contract from summoning
|
||||
click_radius = -1 // Precision clicking required
|
||||
allowed_type = /mob/living/carbon
|
||||
clothes_req = FALSE
|
||||
school = "conjuration"
|
||||
charge_max = 150
|
||||
@@ -35,8 +40,9 @@
|
||||
action_icon_state = "spell_default"
|
||||
action_background_icon_state = "bg_demon"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/summon_contract/cast(list/targets, mob/user = usr)
|
||||
for(var/mob/living/carbon/C in targets)
|
||||
/obj/effect/proc_holder/spell/targeted/click/summon_contract/cast(list/targets, mob/user = usr)
|
||||
for(var/target in targets)
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.mind && user.mind)
|
||||
if(C.stat == DEAD)
|
||||
if(user.drop_item())
|
||||
@@ -63,7 +69,7 @@
|
||||
to_chat(user,"<span class='notice'>[C] seems to not be sentient. You are unable to summon a contract for them.</span>")
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/hellish
|
||||
/obj/effect/proc_holder/spell/targeted/click/fireball/hellish
|
||||
name = "Hellfire"
|
||||
desc = "This spell launches hellfire at the target."
|
||||
school = "evocation"
|
||||
@@ -74,7 +80,7 @@
|
||||
fireball_type = /obj/item/projectile/magic/fireball/infernal
|
||||
action_background_icon_state = "bg_demon"
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/hellish/cast(list/targets, mob/living/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/fireball/hellish/cast(list/targets, mob/living/user = usr)
|
||||
add_attack_logs(user, targets, "has fired a Hellfire ball", ATKLOG_FEW)
|
||||
.=..()
|
||||
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
/obj/effect/proc_holder/spell/targeted/horsemask
|
||||
/obj/effect/proc_holder/spell/targeted/click/horsemask
|
||||
name = "Curse of the Horseman"
|
||||
desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes."
|
||||
school = "transmutation"
|
||||
charge_type = "recharge"
|
||||
charge_max = 150
|
||||
charge_counter = 0
|
||||
clothes_req = 0
|
||||
stat_allowed = 0
|
||||
clothes_req = FALSE
|
||||
stat_allowed = FALSE
|
||||
invocation = "KN'A FTAGHU, PUCK 'BTHNK!"
|
||||
invocation_type = "shout"
|
||||
range = 7
|
||||
cooldown_min = 30 //30 deciseconds reduction per rank
|
||||
selection_type = "range"
|
||||
|
||||
selection_activated_message = "<span class='notice'>You start to quietly neigh an incantation. Click on or near a target to cast the spell.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>You stop neighing to yourself.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
action_icon_state = "barn"
|
||||
sound = 'sound/magic/HorseHead_curse.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/horsemask/cast(list/targets, mob/user = usr)
|
||||
if(!targets.len)
|
||||
to_chat(user, "<span class='notice'>No target found in range.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/carbon/target = targets[1]
|
||||
|
||||
if(!target)
|
||||
return
|
||||
|
||||
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='notice'>It'd be stupid to curse [target] with a horse's head!</span>")
|
||||
return
|
||||
|
||||
if(!(target in oview(range)))//If they are not in overview after selection.
|
||||
to_chat(user, "<span class='notice'>They are too far away!</span>")
|
||||
return
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead
|
||||
magichead.flags |= NODROP | DROPDEL //curses!
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/lightning/Click()
|
||||
if(!ready && start_time == 0)
|
||||
if(cast_check())
|
||||
if(cast_check(TRUE, FALSE, usr))
|
||||
StartChargeup()
|
||||
else
|
||||
if(ready && cast_check(skipcharge=1))
|
||||
if(ready && cast_check(TRUE, TRUE, usr))
|
||||
choose_targets()
|
||||
return 1
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/magnet/Click()
|
||||
if(!ready && start_time == 0)
|
||||
if(cast_check())
|
||||
if(cast_check(TRUE, FALSE, usr))
|
||||
StartChargeup()
|
||||
else
|
||||
if(ready && cast_check(skipcharge=1))
|
||||
if(ready && cast_check(TRUE, TRUE, usr))
|
||||
choose_targets()
|
||||
return 1
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
/obj/effect/proc_holder/spell/targeted/click/mind_transfer
|
||||
name = "Mind Transfer"
|
||||
desc = "This spell allows the user to switch bodies with a target."
|
||||
|
||||
@@ -8,33 +8,29 @@
|
||||
invocation = "GIN'YU CAPAN"
|
||||
invocation_type = "whisper"
|
||||
range = 1
|
||||
click_radius = 0 // Still gotta be pretty accurate
|
||||
selection_activated_message = "<span class='notice'>You prepare to transfer your mind. Click on a target to cast the spell.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>You decide that your current form is good enough.</span>"
|
||||
cooldown_min = 200 //100 deciseconds reduction per rank
|
||||
var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
|
||||
var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell
|
||||
var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell
|
||||
action_icon_state = "mindswap"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/mind_transfer/valid_target(mob/living/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
return target.stat != DEAD && target.key && target.mind
|
||||
|
||||
/*
|
||||
Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
|
||||
Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering.
|
||||
Also, you never added distance checking after target is selected. I've went ahead and did that.
|
||||
*/
|
||||
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride)
|
||||
/obj/effect/proc_holder/spell/targeted/click/mind_transfer/cast(list/targets, mob/user = usr)
|
||||
|
||||
var/mob/living/target = targets[range]
|
||||
|
||||
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
|
||||
to_chat(user, "They are too far away!")
|
||||
return
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user, "You don't particularly want to be dead.")
|
||||
return
|
||||
|
||||
if(!target.key || !target.mind)
|
||||
to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.")
|
||||
return
|
||||
|
||||
if(user.suiciding)
|
||||
to_chat(user, "<span class='warning'>You're killing yourself! You can't concentrate enough to do this!</span>")
|
||||
return
|
||||
|
||||
@@ -308,76 +308,50 @@
|
||||
duration = 300
|
||||
sound = 'sound/magic/blind.ogg'
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball
|
||||
/obj/effect/proc_holder/spell/targeted/click/fireball
|
||||
name = "Fireball"
|
||||
desc = "This spell fires a fireball at a target and does not require wizard garb."
|
||||
|
||||
school = "evocation"
|
||||
charge_max = 60
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
invocation = "ONI SOMA"
|
||||
invocation_type = "shout"
|
||||
auto_target_single = FALSE // Having this true won't ever find a single target and is just lost processing power
|
||||
range = 20
|
||||
cooldown_min = 20 //10 deciseconds reduction per rank
|
||||
|
||||
click_radius = -1
|
||||
selection_activated_message = "<span class='notice'>Your prepare to cast your fireball spell! <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>You extinguish your fireball...for now.</span>"
|
||||
allowed_type = /atom // FIRE AT EVERYTHING
|
||||
|
||||
var/fireball_type = /obj/item/projectile/magic/fireball
|
||||
action_icon_state = "fireball0"
|
||||
sound = 'sound/magic/fireball.ogg'
|
||||
|
||||
active = FALSE
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/Click()
|
||||
var/mob/living/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
|
||||
var/msg
|
||||
|
||||
if(!can_cast(user))
|
||||
msg = "<span class='warning'>You can no longer cast Fireball.</span>"
|
||||
remove_ranged_ability(user, msg)
|
||||
return
|
||||
|
||||
if(active)
|
||||
msg = "<span class='notice'>You extinguish your fireball...for now.</span>"
|
||||
remove_ranged_ability(user, msg)
|
||||
else
|
||||
msg = "<span class='notice'>Your prepare to cast your fireball spell! <B>Left-click to cast at a target!</B></span>"
|
||||
add_ranged_ability(user, msg)
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/update_icon()
|
||||
/obj/effect/proc_holder/spell/targeted/click/fireball/update_icon()
|
||||
if(!action)
|
||||
return
|
||||
action.button_icon_state = "fireball[active]"
|
||||
action.UpdateButtonIcon()
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target)
|
||||
if(..())
|
||||
return FALSE
|
||||
|
||||
if(!cast_check(0, user))
|
||||
remove_ranged_ability(user)
|
||||
return FALSE
|
||||
|
||||
var/list/targets = list(target)
|
||||
perform(targets, user = user)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/fireball/cast(list/targets, mob/living/user = usr)
|
||||
var/target = targets[1] //There is only ever one target for fireball
|
||||
var/turf/T = user.loc
|
||||
var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction
|
||||
if(!isturf(U) || !isturf(T))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc)
|
||||
FB.current = get_turf(user)
|
||||
FB.preparePixelProjectile(target, get_turf(target), user)
|
||||
FB.fire()
|
||||
user.newtonian_move(get_dir(U, T))
|
||||
remove_ranged_ability(user)
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/repulse
|
||||
name = "Repulse"
|
||||
|
||||
@@ -1563,11 +1563,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
|
||||
cost = 10
|
||||
|
||||
/datum/uplink_item/cyber_implants/antistun
|
||||
name = "CNS Rebooter Implant"
|
||||
desc = "This implant will help you get back up on your feet faster after being stunned. \
|
||||
name = "Hardened CNS Rebooter Implant"
|
||||
desc = "This implant will help you get back up on your feet faster after being stunned. It is invulnerable to EMPs. \
|
||||
Comes with an automated implanting tool."
|
||||
reference = "CIAS"
|
||||
item = /obj/item/organ/internal/cyberimp/brain/anti_stun
|
||||
item = /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened
|
||||
cost = 12
|
||||
|
||||
/datum/uplink_item/cyber_implants/reviver
|
||||
|
||||
@@ -123,13 +123,13 @@
|
||||
instability = GENE_INSTABILITY_MODERATE
|
||||
mutation = CRYO
|
||||
|
||||
spelltype = /obj/effect/proc_holder/spell/targeted/cryokinesis
|
||||
spelltype = /obj/effect/proc_holder/spell/targeted/click/cryokinesis
|
||||
|
||||
/datum/dna/gene/basic/grant_spell/cryo/New()
|
||||
..()
|
||||
block = GLOB.cryoblock
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/cryokinesis
|
||||
/obj/effect/proc_holder/spell/targeted/click/cryokinesis
|
||||
name = "Cryokinesis"
|
||||
desc = "Drops the bodytemperature of another person."
|
||||
panel = "Abilities"
|
||||
@@ -137,45 +137,44 @@
|
||||
charge_type = "recharge"
|
||||
charge_max = 1200
|
||||
|
||||
clothes_req = 0
|
||||
stat_allowed = 0
|
||||
clothes_req = FALSE
|
||||
stat_allowed = FALSE
|
||||
|
||||
click_radius = 0
|
||||
auto_target_single = FALSE // Give the clueless geneticists a way out and to have them not target themselves
|
||||
selection_activated_message = "<span class='notice'>Your mind grow cold. Click on a target to cast the spell.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind returns to normal.</span>"
|
||||
allowed_type = /mob/living/carbon
|
||||
invocation_type = "none"
|
||||
range = 7
|
||||
selection_type = "range"
|
||||
include_user = 1
|
||||
include_user = TRUE
|
||||
var/list/compatible_mobs = list(/mob/living/carbon/human)
|
||||
|
||||
action_icon_state = "genetic_cryo"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/cryokinesis/cast(list/targets, mob/user = usr)
|
||||
if(!targets.len)
|
||||
to_chat(user, "<span class='notice'>No target found in range.</span>")
|
||||
return
|
||||
/obj/effect/proc_holder/spell/targeted/click/cryokinesis/cast(list/targets, mob/user = usr)
|
||||
|
||||
var/mob/living/carbon/C = targets[1]
|
||||
|
||||
if(!iscarbon(C))
|
||||
to_chat(user, "<span class='warning'>This will only work on normal organic beings.</span>")
|
||||
return
|
||||
|
||||
if(COLDRES in C.mutations)
|
||||
C.visible_message("<span class='warning'>A cloud of fine ice crystals engulfs [C.name], but disappears almost instantly!</span>")
|
||||
return
|
||||
var/handle_suit = 0
|
||||
var/handle_suit = FALSE
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(istype(H.head, /obj/item/clothing/head/helmet/space))
|
||||
if(istype(H.wear_suit, /obj/item/clothing/suit/space))
|
||||
handle_suit = 1
|
||||
handle_suit = TRUE
|
||||
if(H.internal)
|
||||
H.visible_message("<span class='warning'>[user] sprays a cloud of fine ice crystals, engulfing [H]!</span>",
|
||||
"<span class='notice'>[user] sprays a cloud of fine ice crystals over your [H.head]'s visor.</span>")
|
||||
add_attack_logs(user, C, "Cryokinesis")
|
||||
else
|
||||
H.visible_message("<span class='warning'>[user] sprays a cloud of fine ice crystals engulfing, [H]!</span>",
|
||||
"<span class='warning'>[user] sprays a cloud of fine ice crystals cover your [H.head]'s visor and make it into your air vents!.</span>")
|
||||
add_attack_logs(user, C, "Cryokinesis")
|
||||
|
||||
H.bodytemperature = max(0, H.bodytemperature - 100)
|
||||
add_attack_logs(user, C, "Cryokinesis")
|
||||
if(!handle_suit)
|
||||
C.bodytemperature = max(0, C.bodytemperature - 200)
|
||||
C.ExtinguishMob()
|
||||
@@ -454,7 +453,7 @@
|
||||
name = "Polymorphism"
|
||||
desc = "Enables the subject to reconfigure their appearance to mimic that of others."
|
||||
|
||||
spelltype =/obj/effect/proc_holder/spell/targeted/polymorph
|
||||
spelltype =/obj/effect/proc_holder/spell/targeted/click/polymorph
|
||||
//cooldown = 1800
|
||||
activation_messages = list("You don't feel entirely like yourself somehow.")
|
||||
deactivation_messages = list("You feel secure in your identity.")
|
||||
@@ -465,34 +464,36 @@
|
||||
..()
|
||||
block = GLOB.polymorphblock
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/polymorph
|
||||
/obj/effect/proc_holder/spell/targeted/click/polymorph
|
||||
name = "Polymorph"
|
||||
desc = "Mimic the appearance of others!"
|
||||
panel = "Abilities"
|
||||
charge_max = 1800
|
||||
|
||||
clothes_req = 0
|
||||
human_req = 1
|
||||
stat_allowed = 0
|
||||
clothes_req = FALSE
|
||||
stat_allowed = FALSE
|
||||
|
||||
click_radius = -1 // Precision required
|
||||
auto_target_single = FALSE // Safety to not turn into monkey (420)
|
||||
selection_activated_message = "<span class='notice'>You body becomes unstable. Click on a target to cast transform into them.</span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your body calms down again.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
invocation_type = "none"
|
||||
range = 1
|
||||
selection_type = "range"
|
||||
|
||||
action_icon_state = "genetic_poly"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/polymorph/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/M = targets[1]
|
||||
if(!ishuman(M))
|
||||
to_chat(usr, "<span class='warning'>You can only change your appearance to that of another human.</span>")
|
||||
return
|
||||
/obj/effect/proc_holder/spell/targeted/click/polymorph/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
user.visible_message("<span class='warning'>[user]'s body shifts and contorts.</span>")
|
||||
|
||||
spawn(10)
|
||||
if(M && user)
|
||||
if(target && user)
|
||||
playsound(user.loc, 'sound/goonstation/effects/gib.ogg', 50, 1)
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/mob/living/carbon/human/target = M
|
||||
H.UpdateAppearance(target.dna.UI)
|
||||
H.real_name = target.real_name
|
||||
H.name = target.name
|
||||
|
||||
@@ -214,25 +214,21 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/remotetalk/choose_targets(mob/user = usr)
|
||||
var/list/targets = new /list()
|
||||
var/list/validtargets = new /list()
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/mob/living/M in range(14, T))
|
||||
if(M && M.mind)
|
||||
if(M == user)
|
||||
continue
|
||||
validtargets += M
|
||||
var/list/validtargets = user.get_telepathic_targets()
|
||||
|
||||
if(!validtargets.len)
|
||||
if(!length(validtargets))
|
||||
to_chat(user, "<span class='warning'>There are no valid targets!</span>")
|
||||
start_recharge()
|
||||
return
|
||||
|
||||
targets += input("Choose the target to talk to.", "Targeting") as null|mob in validtargets
|
||||
var/target_name = input("Choose the target to talk to.", "Targeting") as null|anything in validtargets
|
||||
|
||||
if(!targets.len || !targets[1]) //doesn't waste the spell
|
||||
var/mob/living/target
|
||||
if(!target_name || !(target = validtargets[target_name]))
|
||||
revert_cast(user)
|
||||
return
|
||||
|
||||
targets += target
|
||||
perform(targets, user = user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/remotetalk/cast(list/targets, mob/user = usr)
|
||||
@@ -249,7 +245,7 @@
|
||||
target.show_message("<span class='abductor'>You hear [user.real_name]'s voice: [say]</span>")
|
||||
else
|
||||
target.show_message("<span class='abductor'>You hear a voice that seems to echo around the room: [say]</span>")
|
||||
user.show_message("<span class='abductor'>You project your mind into [target.name]: [say]</span>")
|
||||
user.show_message("<span class='abductor'>You project your mind into [(target in user.get_visible_mobs()) ? target.name : "the unknown entity"]: [say]</span>")
|
||||
for(var/mob/dead/observer/G in GLOB.player_list)
|
||||
G.show_message("<i>Telepathic message from <b>[user]</b> ([ghost_follow_link(user, ghost=G)]) to <b>[target]</b> ([ghost_follow_link(target, ghost=G)]): [say]</i>")
|
||||
|
||||
@@ -266,26 +262,22 @@
|
||||
var/list/available_targets = list()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/mindscan/choose_targets(mob/user = usr)
|
||||
var/list/targets = new /list()
|
||||
var/list/validtargets = new /list()
|
||||
var/turf/T = get_turf(user)
|
||||
for(var/mob/living/M in range(14, T))
|
||||
if(M && M.mind)
|
||||
if(M == user)
|
||||
continue
|
||||
validtargets += M
|
||||
var/list/targets = list()
|
||||
var/list/validtargets = user.get_telepathic_targets()
|
||||
|
||||
if(!validtargets.len)
|
||||
if(!length(validtargets))
|
||||
to_chat(user, "<span class='warning'>There are no valid targets!</span>")
|
||||
start_recharge()
|
||||
return
|
||||
|
||||
targets += input("Choose the target to listen to.", "Targeting") as null|mob in validtargets
|
||||
var/target_name = input("Choose the target to listen to.", "Targeting") as null|anything in validtargets
|
||||
|
||||
if(!targets.len || !targets[1]) //doesn't waste the spell
|
||||
var/mob/living/target
|
||||
if(!target_name || !(target = validtargets[target_name]))
|
||||
revert_cast(user)
|
||||
return
|
||||
|
||||
targets += target
|
||||
perform(targets, user = user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/mindscan/cast(list/targets, mob/user = usr)
|
||||
@@ -295,7 +287,7 @@
|
||||
var/message = "You feel your mind expand briefly... (Click to send a message.)"
|
||||
if(REMOTE_TALK in target.mutations)
|
||||
message = "You feel [user.real_name] request a response from you... (Click here to project mind.)"
|
||||
user.show_message("<span class='abductor'>You offer your mind to [target.name].</span>")
|
||||
user.show_message("<span class='abductor'>You offer your mind to [(target in user.get_visible_mobs()) ? target.name : "the unknown entity"].</span>")
|
||||
target.show_message("<span class='abductor'><A href='?src=[UID()];target=[target.UID()];user=[user.UID()]'>[message]</a></span>")
|
||||
available_targets += target
|
||||
addtimer(CALLBACK(src, .proc/removeAvailability, target), 100)
|
||||
|
||||
@@ -92,7 +92,7 @@ GLOBAL_LIST_INIT(lawlorify, list (
|
||||
var/form = BASIC_DEVIL
|
||||
var/exists = 0
|
||||
var/static/list/dont_remove_spells = list(
|
||||
/obj/effect/proc_holder/spell/targeted/summon_contract,
|
||||
/obj/effect/proc_holder/spell/targeted/click/summon_contract,
|
||||
/obj/effect/proc_holder/spell/targeted/conjure_item/violin,
|
||||
/obj/effect/proc_holder/spell/targeted/summon_dancefloor)
|
||||
var/ascendable = FALSE
|
||||
@@ -326,12 +326,12 @@ GLOBAL_LIST_INIT(lawlorify, list (
|
||||
owner.RemoveSpell(S)
|
||||
|
||||
/datum/devilinfo/proc/give_summon_contract()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/summon_contract(null))
|
||||
|
||||
|
||||
/datum/devilinfo/proc/give_base_spells(give_summon_contract = 0)
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork(null))
|
||||
if(give_summon_contract)
|
||||
give_summon_contract()
|
||||
@@ -343,13 +343,13 @@ GLOBAL_LIST_INIT(lawlorify, list (
|
||||
/datum/devilinfo/proc/give_lizard_spells()
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
|
||||
/datum/devilinfo/proc/give_true_spells()
|
||||
remove_spells()
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null))
|
||||
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null))
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
else
|
||||
name = "[initial(name)] ([cast_amount]E)"
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr)
|
||||
/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(user.inhibited)
|
||||
return 0
|
||||
if(charge_counter < charge_max)
|
||||
|
||||
@@ -18,80 +18,56 @@
|
||||
return 0
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/glare //Stuns and mutes a human target, depending on the distance relative to the shadowling
|
||||
/obj/effect/proc_holder/spell/targeted/click/glare //Stuns and mutes a human target, depending on the distance relative to the shadowling
|
||||
name = "Glare"
|
||||
desc = "Stuns and mutes a target for a decent duration. Duration depends on the proximity to the target."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 300
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
range = 10 //has no effect beyond this range, so setting this makes invalid/useless targets not show up in popup
|
||||
action_icon_state = "glare"
|
||||
humans_only = 1 //useless since we override chose_targets, but might be used for other code later??? Might remove, idk
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/glare/choose_targets(mob/user)
|
||||
var/list/possible_targets = list()
|
||||
for(var/mob/living/carbon/human/target in view_or_range(range, user, "view"))
|
||||
if(target.stat)
|
||||
continue
|
||||
if(is_shadow_or_thrall(target))
|
||||
continue
|
||||
possible_targets += target
|
||||
var/mob/living/carbon/human/M
|
||||
var/list/targets = list()
|
||||
if(possible_targets.len == 1)//no choice involved
|
||||
targets = possible_targets
|
||||
else
|
||||
M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets
|
||||
if(M in view_or_range(range, user, "view"))
|
||||
targets += M
|
||||
selection_activated_message = "<span class='notice'>Your prepare to your eyes for a stunning glare! <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your eyes relax... for now.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/glare/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
if(!targets.len) //doesn't waste the spell
|
||||
revert_cast(user)
|
||||
/obj/effect/proc_holder/spell/targeted/click/glare/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
return !target.stat && !is_shadow_or_thrall(target)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/glare/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/H = targets[1]
|
||||
|
||||
user.visible_message("<span class='warning'><b>[user]'s eyes flash a blinding red!</b></span>")
|
||||
var/distance = get_dist(H, user)
|
||||
if (distance <= 1) //Melee glare
|
||||
H.visible_message("<span class='danger'>[H] freezes in place, [H.p_their()] eyes glazing over...</span>", \
|
||||
"<span class='userdanger'>Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by [user.p_their()] heavenly beauty...</span>")
|
||||
H.Stun(10)
|
||||
H.AdjustSilence(10)
|
||||
else //Distant glare
|
||||
var/loss = 10 - distance
|
||||
var/duration = 10 - loss
|
||||
if(loss <= 0)
|
||||
to_chat(user, "<span class='danger'>Your glare had no effect over a such long distance!</span>")
|
||||
return
|
||||
H.slowed = duration
|
||||
H.AdjustSilence(10)
|
||||
to_chat(H, "<span class='userdanger'>A red light flashes across your vision, and your mind tries to resist them.. you are exhausted.. you are not able to speak..</span>")
|
||||
addtimer(CALLBACK(src, .proc/do_stun, H, user, loss), duration SECONDS)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/glare/proc/do_stun(mob/living/carbon/human/target, user, stun_time)
|
||||
if(!istype(target) || target.stat)
|
||||
return
|
||||
|
||||
perform(targets, user = user)
|
||||
return
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/glare/cast(list/targets, mob/user = usr)
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You may only glare at humans!</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!shadowling_check(user))
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>[target] must be conscious!</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='danger'>You don't see why you would want to paralyze an ally.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
var/mob/living/carbon/human/M = target
|
||||
user.visible_message("<span class='warning'><b>[user]'s eyes flash a blinding red!</b></span>")
|
||||
var/distance = get_dist(target, user)
|
||||
if (distance <= 1) //Melee glare
|
||||
target.visible_message("<span class='danger'>[target] freezes in place, [target.p_their()] eyes glazing over...</span>")
|
||||
to_chat(target, "<span class='userdanger'>Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by [user.p_their()] heavenly beauty...</span>")
|
||||
target.Stun(10)
|
||||
M.AdjustSilence(10)
|
||||
else //Distant glare
|
||||
var/loss = 10 - distance
|
||||
var/duration = 10 - loss
|
||||
if(loss <= 0)
|
||||
to_chat(user, "<span class='danger'>Your glare had no effect over a such long distance!</span>")
|
||||
return
|
||||
target.slowed = duration
|
||||
M.AdjustSilence(10)
|
||||
to_chat(target, "<span class='userdanger'>A red light flashes across your vision, and your mind tries to resist them.. you are exhausted.. you are not able to speak..</span>")
|
||||
sleep(duration*10)
|
||||
target.Stun(loss)
|
||||
target.visible_message("<span class='danger'>[target] freezes in place, [target.p_their()] eyes glazing over...</span>")
|
||||
to_chat(target, "<span class='userdanger'>Red lights suddenly dance in your vision, and you are mesmerized by the heavenly lights...</span>")
|
||||
target.Stun(stun_time)
|
||||
target.visible_message("<span class='danger'>[target] freezes in place, [target.p_their()] eyes glazing over...</span>",\
|
||||
"<span class='userdanger'>Red lights suddenly dance in your vision, and you are mesmerized by the heavenly lights...</span>")
|
||||
|
||||
/obj/effect/proc_holder/spell/aoe_turf/veil
|
||||
name = "Veil"
|
||||
@@ -228,96 +204,80 @@
|
||||
M.reagents.add_reagent("frostoil", 15) //Half of a cryosting
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties
|
||||
/obj/effect/proc_holder/spell/targeted/click/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties
|
||||
name = "Enthrall"
|
||||
desc = "Allows you to enslave a conscious, non-braindead, non-catatonic human to your will. This takes some time to cast."
|
||||
panel = "Shadowling Abilities"
|
||||
charge_max = 0
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
range = 1 //Adjacent to user
|
||||
var/enthralling = 0
|
||||
var/enthralling = FALSE
|
||||
action_icon_state = "enthrall"
|
||||
humans_only = 1
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets, mob/user = usr)
|
||||
click_radius = -1 // Precision baby
|
||||
selection_activated_message = "<span class='notice'>Your prepare your mind to entrall a mortal. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind relaxes.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/enthrall/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(enthralling || !shadowling_check(user))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/enthrall/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
return target.key && target.mind && !target.stat && !is_shadow_or_thrall(target) && target.client
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/enthrall/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/ling = user
|
||||
listclearnulls(SSticker.mode.shadowling_thralls)
|
||||
if(!(ling.mind in SSticker.mode.shadows))
|
||||
return
|
||||
if(!isshadowling(ling))
|
||||
if(SSticker.mode.shadowling_thralls.len >= 5)
|
||||
charge_counter = charge_max
|
||||
return
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(!in_range(user, target))
|
||||
to_chat(user, "<span class='warning'>You need to be closer to enthrall [target].</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!target.key || !target.mind)
|
||||
to_chat(user, "<span class='warning'>The target has no mind.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>The target must be conscious.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>You can not enthrall allies.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You can only enthrall humans.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(enthralling)
|
||||
to_chat(user, "<span class='warning'>You are already enthralling!</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!target.client)
|
||||
to_chat(user, "<span class='warning'>[target]'s mind is vacant of activity.</span>")
|
||||
enthralling = 1
|
||||
to_chat(user, "<span class='danger'>This target is valid. You begin the enthralling.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[user] stares at you. You feel your head begin to pulse.</span>")
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
enthralling = TRUE
|
||||
to_chat(user, "<span class='danger'>This target is valid. You begin the enthralling.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[user] stares at you. You feel your head begin to pulse.</span>")
|
||||
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You place your hands to [target]'s head...</span>")
|
||||
user.visible_message("<span class='warning'>[user] places [user.p_their()] hands onto the sides of [target]'s head!</span>")
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You begin preparing [target]'s mind as a blank slate...</span>")
|
||||
user.visible_message("<span class='warning'>[user]'s palms flare a bright red against [target]'s temples!</span>")
|
||||
to_chat(target, "<span class='danger'>A terrible red light floods your mind. You collapse as conscious thought is wiped away.</span>")
|
||||
target.Weaken(12)
|
||||
sleep(20)
|
||||
if(ismindshielded(target))
|
||||
to_chat(user, "<span class='notice'>They have a mindshield implant. You begin to deactivate it - this will take some time.</span>")
|
||||
user.visible_message("<span class='warning'>[user] pauses, then dips [user.p_their()] head in concentration!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>Your mindshield implant becomes hot as it comes under attack!</span>")
|
||||
sleep(100) //10 seconds - not spawn() so the enthralling takes longer
|
||||
to_chat(user, "<span class='notice'>The nanobots composing the mindshield implant have been rendered inert. Now to continue.</span>")
|
||||
user.visible_message("<span class='warning'>[user] relaxes again.</span>")
|
||||
for(var/obj/item/implant/mindshield/L in target)
|
||||
if(L && L.implanted)
|
||||
qdel(L)
|
||||
to_chat(target, "<span class='boldannounce'>Your mental protection implant unexpectedly falters, dims, dies.</span>")
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You begin planting the tumor that will control the new thrall...</span>")
|
||||
user.visible_message("<span class='warning'>A strange energy passes from [user]'s hands into [target]'s head!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>You feel your memories twisting, morphing. A sense of horror dominates your mind.</span>")
|
||||
if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a mindshield implant
|
||||
to_chat(user, "<span class='warning'>The enthralling has been interrupted - your target's mind returns to its previous state.</span>")
|
||||
to_chat(target, "<span class='userdanger'>You wrest yourself away from [user]'s hands and compose yourself</span>")
|
||||
enthralling = 0
|
||||
return
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You place your hands to [target]'s head...</span>")
|
||||
user.visible_message("<span class='warning'>[user] places [user.p_their()] hands onto the sides of [target]'s head!</span>")
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You begin preparing [target]'s mind as a blank slate...</span>")
|
||||
user.visible_message("<span class='warning'>[user]'s palms flare a bright red against [target]'s temples!</span>")
|
||||
to_chat(target, "<span class='danger'>A terrible red light floods your mind. You collapse as conscious thought is wiped away.</span>")
|
||||
target.Weaken(12)
|
||||
sleep(20)
|
||||
if(ismindshielded(target))
|
||||
to_chat(user, "<span class='notice'>They have a mindshield implant. You begin to deactivate it - this will take some time.</span>")
|
||||
user.visible_message("<span class='warning'>[user] pauses, then dips [user.p_their()] head in concentration!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>Your mindshield implant becomes hot as it comes under attack!</span>")
|
||||
sleep(100) //10 seconds - not spawn() so the enthralling takes longer
|
||||
to_chat(user, "<span class='notice'>The nanobots composing the mindshield implant have been rendered inert. Now to continue.</span>")
|
||||
user.visible_message("<span class='warning'>[user] relaxes again.</span>")
|
||||
for(var/obj/item/implant/mindshield/L in target)
|
||||
if(L && L.implanted)
|
||||
qdel(L)
|
||||
to_chat(target, "<span class='boldannounce'>Your mental protection implant unexpectedly falters, dims, dies.</span>")
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You begin planting the tumor that will control the new thrall...</span>")
|
||||
user.visible_message("<span class='warning'>A strange energy passes from [user]'s hands into [target]'s head!</span>")
|
||||
to_chat(target, "<span class='boldannounce'>You feel your memories twisting, morphing. A sense of horror dominates your mind.</span>")
|
||||
if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a mindshield implant
|
||||
to_chat(user, "<span class='warning'>The enthralling has been interrupted - your target's mind returns to its previous state.</span>")
|
||||
to_chat(target, "<span class='userdanger'>You wrest yourself away from [user]'s hands and compose yourself</span>")
|
||||
enthralling = FALSE
|
||||
return
|
||||
|
||||
enthralling = 0
|
||||
to_chat(user, "<span class='shadowling'>You have enthralled <b>[target]</b>!</span>")
|
||||
target.visible_message("<span class='big'>[target] looks to have experienced a revelation!</span>", \
|
||||
"<span class='warning'>False faces all d<b>ark not real not real not--</b></span>")
|
||||
target.setOxyLoss(0) //In case the shadowling was choking them out
|
||||
SSticker.mode.add_thrall(target.mind)
|
||||
target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL
|
||||
enthralling = FALSE
|
||||
to_chat(user, "<span class='shadowling'>You have enthralled <b>[target]</b>!</span>")
|
||||
target.visible_message("<span class='big'>[target] looks to have experienced a revelation!</span>", \
|
||||
"<span class='warning'>False faces all d<b>ark not real not real not--</b></span>")
|
||||
target.setOxyLoss(0) //In case the shadowling was choking them out
|
||||
SSticker.mode.add_thrall(target.mind)
|
||||
target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shadowling_regenarmor //Resets a shadowling's species to normal, removes genetic defects, and re-equips their armor
|
||||
name = "Rapid Re-Hatch"
|
||||
@@ -405,7 +365,7 @@
|
||||
reviveThrallAcquired = 1
|
||||
to_chat(target, "<span class='shadowling'><i>The power of your thralls has granted you the <b>Black Recuperation</b> ability. \
|
||||
This will, after a short time, bring a dead thrall completely back to life with no bodily defects.</i></span>")
|
||||
target.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/reviveThrall(null))
|
||||
target.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/reviveThrall(null))
|
||||
|
||||
if(thralls < victory_threshold)
|
||||
to_chat(target, "<span class='shadowling'>You do not have the power to ascend. You require [victory_threshold] thralls, but only [thralls] living thralls are present.</span>")
|
||||
@@ -571,169 +531,170 @@
|
||||
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/reviveThrall
|
||||
/obj/effect/proc_holder/spell/targeted/click/reviveThrall
|
||||
name = "Black Recuperation"
|
||||
desc = "Revives or empowers a thrall."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 1
|
||||
charge_max = 600
|
||||
clothes_req = 0
|
||||
include_user = 0
|
||||
clothes_req = FALSE
|
||||
include_user = FALSE
|
||||
action_icon_state = "revive_thrall"
|
||||
humans_only = 1
|
||||
click_radius = -1 // Precision baby
|
||||
selection_activated_message = "<span class='notice'>You start focusing your powers on mending wounds of allies. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind relaxes.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/reviveThrall/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/reviveThrall/can_cast(mob/user = usr)
|
||||
if(!shadowling_check(user))
|
||||
charge_counter = charge_max
|
||||
return
|
||||
for(var/mob/living/carbon/human/thrallToRevive in targets)
|
||||
var/choice = alert(user,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel")
|
||||
switch(choice)
|
||||
if("Empower")
|
||||
if(!is_thrall(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not a thrall.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(thrallToRevive.stat != CONSCIOUS)
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] must be conscious to become empowered.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(isshadowlinglesser(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is already empowered.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
var/empowered_thralls = 0
|
||||
for(var/datum/mind/M in SSticker.mode.shadowling_thralls)
|
||||
if(!ishuman(M.current))
|
||||
return
|
||||
var/mob/living/carbon/human/H = M.current
|
||||
if(isshadowlinglesser(H))
|
||||
empowered_thralls++
|
||||
if(empowered_thralls >= EMPOWERED_THRALL_LIMIT)
|
||||
to_chat(user, "<span class='warning'>You cannot spare this much energy. There are too many empowered thralls.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user] places [user.p_their()] hands over [thrallToRevive]'s face, red light shining from beneath.</span>", \
|
||||
"<span class='shadowling'>You place your hands on [thrallToRevive]'s face and begin gathering energy...</span>")
|
||||
to_chat(thrallToRevive, "<span class='userdanger'>[user] places [user.p_their()] hands over your face. You feel energy gathering. Stand still...</span>")
|
||||
if(!do_mob(user, thrallToRevive, 80))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges into [thrallToRevive]'s face!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
|
||||
thrallToRevive.Weaken(5)
|
||||
thrallToRevive.visible_message("<span class='warning'><b>[thrallToRevive] collapses, [thrallToRevive.p_their()] skin and face distorting!</span>", \
|
||||
"<span class='userdanger'><i>AAAAAAAAAAAAAAAAAAAGH-</i></span>")
|
||||
sleep(20)
|
||||
thrallToRevive.visible_message("<span class='warning'>[thrallToRevive] slowly rises, no longer recognizable as human.</span>", \
|
||||
"<span class='shadowling'><b>You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \
|
||||
darkness but wither slowly in light. In addition, you now have glare and true shadow walk.</b></span>")
|
||||
thrallToRevive.set_species(/datum/species/shadow/ling/lesser)
|
||||
thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_shadow_walk)
|
||||
thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null))
|
||||
thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null))
|
||||
if("Revive")
|
||||
if(!is_thrall(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not a thrall.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(thrallToRevive.stat != DEAD)
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is not dead.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user] kneels over [thrallToRevive], placing [user.p_their()] hands on [thrallToRevive.p_their()] chest.</span>", \
|
||||
"<span class='shadowling'>You crouch over the body of your thrall and begin gathering energy...</span>")
|
||||
thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive)
|
||||
if(!do_mob(user, thrallToRevive, 30))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
|
||||
sleep(10)
|
||||
if(thrallToRevive.revive())
|
||||
thrallToRevive.visible_message("<span class='boldannounce'>[thrallToRevive] heaves in breath, dim red light shining in [thrallToRevive.p_their()] eyes.</span>", \
|
||||
"<span class='shadowling'><b><i>You have returned. One of your masters has brought you from the darkness beyond.</b></i></span>")
|
||||
thrallToRevive.Weaken(4)
|
||||
thrallToRevive.emote("gasp")
|
||||
playsound(thrallToRevive, "bodyfall", 50, 1)
|
||||
else
|
||||
charge_counter = charge_max
|
||||
return
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle
|
||||
/obj/effect/proc_holder/spell/targeted/click/reviveThrall/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
return is_thrall(target)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/reviveThrall/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/thrallToRevive = targets[1]
|
||||
if(thrallToRevive.stat == CONSCIOUS)
|
||||
if(isshadowlinglesser(thrallToRevive))
|
||||
to_chat(user, "<span class='warning'>[thrallToRevive] is already empowered.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
var/empowered_thralls = 0
|
||||
for(var/datum/mind/M in SSticker.mode.shadowling_thralls)
|
||||
if(!ishuman(M.current))
|
||||
return
|
||||
var/mob/living/carbon/human/H = M.current
|
||||
if(isshadowlinglesser(H))
|
||||
empowered_thralls++
|
||||
if(empowered_thralls >= EMPOWERED_THRALL_LIMIT)
|
||||
to_chat(user, "<span class='warning'>You cannot spare this much energy. There are too many empowered thralls.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
user.visible_message("<span class='danger'>[user] places [user.p_their()] hands over [thrallToRevive]'s face, red light shining from beneath.</span>", \
|
||||
"<span class='shadowling'>You place your hands on [thrallToRevive]'s face and begin gathering energy...</span>")
|
||||
to_chat(thrallToRevive, "<span class='userdanger'>[user] places [user.p_their()] hands over your face. You feel energy gathering. Stand still...</span>")
|
||||
if(!do_mob(user, thrallToRevive, 80))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges into [thrallToRevive]'s face!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
|
||||
thrallToRevive.Weaken(5)
|
||||
thrallToRevive.visible_message("<span class='warning'><b>[thrallToRevive] collapses, [thrallToRevive.p_their()] skin and face distorting!</span>", \
|
||||
"<span class='userdanger'><i>AAAAAAAAAAAAAAAAAAAGH-</i></span>")
|
||||
sleep(20)
|
||||
thrallToRevive.visible_message("<span class='warning'>[thrallToRevive] slowly rises, no longer recognizable as human.</span>", \
|
||||
"<span class='shadowling'><b>You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \
|
||||
darkness but wither slowly in light. In addition, you now have glare and true shadow walk.</b></span>")
|
||||
thrallToRevive.set_species(/datum/species/shadow/ling/lesser)
|
||||
thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_shadow_walk)
|
||||
thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/glare(null))
|
||||
thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null))
|
||||
else if(thrallToRevive.stat == DEAD)
|
||||
user.visible_message("<span class='danger'>[user] kneels over [thrallToRevive], placing [user.p_their()] hands on [thrallToRevive.p_their()] chest.</span>", \
|
||||
"<span class='shadowling'>You crouch over the body of your thrall and begin gathering energy...</span>")
|
||||
thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive)
|
||||
if(!do_mob(user, thrallToRevive, 30))
|
||||
to_chat(user, "<span class='warning'>Your concentration snaps. The flow of energy ebbs.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
to_chat(user, "<span class='shadowling'><b><i>You release a massive surge of power into [thrallToRevive]!</b></i></span>")
|
||||
user.visible_message("<span class='boldannounce'><i>Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!</i></span>")
|
||||
playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1)
|
||||
playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
|
||||
user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
|
||||
sleep(10)
|
||||
if(thrallToRevive.revive())
|
||||
thrallToRevive.visible_message("<span class='boldannounce'>[thrallToRevive] heaves in breath, dim red light shining in [thrallToRevive.p_their()] eyes.</span>", \
|
||||
"<span class='shadowling'><b><i>You have returned. One of your masters has brought you from the darkness beyond.</b></i></span>")
|
||||
thrallToRevive.Weaken(4)
|
||||
thrallToRevive.emote("gasp")
|
||||
playsound(thrallToRevive, "bodyfall", 50, 1)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The target must be awake to empower or dead to revive.</span>")
|
||||
revert_cast(user)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle
|
||||
name = "Destroy Engines"
|
||||
desc = "Extends the time of the emergency shuttle's arrival by ten minutes using a life force of our enemy. Shuttle will be unable to be recalled. This can only be used once."
|
||||
panel = "Shadowling Abilities"
|
||||
range = 1
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
charge_max = 600
|
||||
click_radius = -1 // Precision baby
|
||||
selection_activated_message = "<span class='notice'>You start gathering destructive powers to delay the shuttle. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind relaxes.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
action_icon_state = "extend_shuttle"
|
||||
var/global/extendlimit = 0
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(!shadowling_check(user))
|
||||
charge_counter = charge_max
|
||||
return
|
||||
return FALSE
|
||||
if(extendlimit == 1)
|
||||
to_chat(user, "<span class='warning'>Shuttle was already delayed.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(target.stat)
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>[target] must not be an ally.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='warning'>Shuttle was already delayed.</span>")
|
||||
return FALSE
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='warning'>The shuttle must be inbound only to the station.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
var/mob/living/carbon/human/M = target
|
||||
user.visible_message("<span class='warning'>[user]'s eyes flash a bright red!</span>", \
|
||||
"<span class='notice'>You begin to draw [M]'s life force.</span>")
|
||||
M.visible_message("<span class='warning'>[M]'s face falls slack, [M.p_their()] jaw slightly distending.</span>", \
|
||||
"<span class='boldannounce'>You are suddenly transported... far, far away...</span>")
|
||||
extendlimit = 1
|
||||
if(!do_after(user, 150, target = M))
|
||||
extendlimit = 0
|
||||
to_chat(M, "<span class='warning'>You are snapped back to reality, your haze dissipating!</span>")
|
||||
to_chat(user, "<span class='warning'>You have been interrupted. The draw has failed.</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You project [M]'s life force toward the approaching shuttle, extending its arrival duration!</span>")
|
||||
M.visible_message("<span class='warning'>[M]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.</span>", \
|
||||
"<span class='warning'><b>...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...</span>")
|
||||
M.death()
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/more_minutes = 6000
|
||||
var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes
|
||||
GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg')
|
||||
SSshuttle.emergency.setTimer(timer)
|
||||
SSshuttle.emergency.canRecall = FALSE
|
||||
user.mind.spell_list.Remove(src) //Can only be used once!
|
||||
qdel(src)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
return !target.stat && !is_shadow_or_thrall(target)
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
user.visible_message("<span class='warning'>[user]'s eyes flash a bright red!</span>", \
|
||||
"<span class='notice'>You begin to draw [target]'s life force.</span>")
|
||||
target.visible_message("<span class='warning'>[target]'s face falls slack, [target.p_their()] jaw slightly distending.</span>", \
|
||||
"<span class='boldannounce'>You are suddenly transported... far, far away...</span>")
|
||||
extendlimit = 1
|
||||
if(!do_after(user, 150, target = target))
|
||||
extendlimit = 0
|
||||
to_chat(target, "<span class='warning'>You are snapped back to reality, your haze dissipating!</span>")
|
||||
to_chat(user, "<span class='warning'>You have been interrupted. The draw has failed.</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You project [target]'s life force toward the approaching shuttle, extending its arrival duration!</span>")
|
||||
target.visible_message("<span class='warning'>[target]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.</span>", \
|
||||
"<span class='warning'><b>...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...</span>")
|
||||
target.death()
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/more_minutes = 6000
|
||||
var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes
|
||||
GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg')
|
||||
SSshuttle.emergency.setTimer(timer)
|
||||
SSshuttle.emergency.canRecall = FALSE
|
||||
user.mind.spell_list.Remove(src) //Can only be used once!
|
||||
qdel(src)
|
||||
|
||||
// ASCENDANT ABILITIES BEYOND THIS POINT //
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/annihilate
|
||||
/obj/effect/proc_holder/spell/targeted/click/annihilate
|
||||
name = "Annihilate"
|
||||
desc = "Gibs someone instantly."
|
||||
panel = "Ascendant"
|
||||
range = 7
|
||||
charge_max = 0
|
||||
clothes_req = 0
|
||||
charge_max = FALSE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "annihilate"
|
||||
selection_activated_message = "<span class='notice'>You start thinking about gibs. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind relaxes.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/annihilate/cast(list/targets, mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/targeted/click/annihilate/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/simple_animal/ascendant_shadowling/SHA = user
|
||||
if(SHA.phasing)
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
@@ -756,45 +717,42 @@
|
||||
|
||||
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/hypnosis
|
||||
/obj/effect/proc_holder/spell/targeted/click/hypnosis
|
||||
name = "Hypnosis"
|
||||
desc = "Instantly enthralls a human."
|
||||
panel = "Ascendant"
|
||||
range = 7
|
||||
charge_max = 0
|
||||
clothes_req = 0
|
||||
charge_max = FALSE
|
||||
clothes_req = FALSE
|
||||
action_icon_state = "enthrall"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/hypnosis/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/simple_animal/ascendant_shadowling/SHA = user
|
||||
if(SHA.phasing)
|
||||
charge_counter = charge_max
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
return
|
||||
click_radius = -1
|
||||
selection_activated_message = "<span class='notice'>You start preparing to mindwash over a mortal mind. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>Your mind relaxes.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
if(is_shadow_or_thrall(target))
|
||||
to_chat(user, "<span class='warning'>You cannot enthrall an ally.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!target.ckey || !target.mind)
|
||||
to_chat(user, "<span class='warning'>The target has no mind.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>The target must be conscious.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You can only enthrall humans.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
/obj/effect/proc_holder/spell/targeted/click/hypnosis/can_cast(mob/living/simple_animal/ascendant_shadowling/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(!istype(user))
|
||||
return FALSE
|
||||
if(user.phasing)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='warning'>You are not in the same plane of existence. Unphase first.</span>")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
to_chat(user, "<span class='shadowling'>You instantly rearrange <b>[target]</b>'s memories, hyptonitizing [target.p_them()] into a thrall.</span>")
|
||||
to_chat(target, "<span class='userdanger'><font size=3>An agonizing spike of pain drives into your mind, and--</font></span>")
|
||||
SSticker.mode.add_thrall(target.mind)
|
||||
target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL
|
||||
target.add_language("Shadowling Hivemind")
|
||||
/obj/effect/proc_holder/spell/targeted/click/hypnosis/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
return !is_shadow_or_thrall(target) && target.ckey && target.mind && !target.stat
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/hypnosis/cast(list/targets, mob/user = usr)
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
|
||||
to_chat(user, "<span class='shadowling'>You instantly rearrange <b>[target]</b>'s memories, hyptonitizing [target.p_them()] into a thrall.</span>")
|
||||
to_chat(target, "<span class='userdanger'><font size=3>An agonizing spike of pain drives into your mind, and--</font></span>")
|
||||
SSticker.mode.add_thrall(target.mind)
|
||||
target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL
|
||||
target.add_language("Shadowling Hivemind")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -99,14 +99,14 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u
|
||||
to_chat(H, "<span class='shadowling'><b><i>Your powers are awoken. You may now live to your fullest extent. Remember your goal. Cooperate with your thralls and allies.</b></i></span>")
|
||||
H.ExtinguishMob()
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_vision(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/enthrall(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/enthrall(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/glare(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/veil(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/flashfreeze(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/collective_mind(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_regenarmor(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle(null))
|
||||
H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle(null))
|
||||
|
||||
QDEL_NULL(H.hud_used)
|
||||
H.hud_used = new /datum/hud/human(H, ui_style2icon(H.client.prefs.UI_style), H.client.prefs.UI_style_color, H.client.prefs.UI_style_alpha)
|
||||
@@ -172,8 +172,8 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u
|
||||
H.mind.transfer_to(A)
|
||||
A.name = H.real_name
|
||||
A.languages = H.languages
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/annihilate(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/hypnosis(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/annihilate(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/hypnosis(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_phase_shift(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/ascendant_storm(null))
|
||||
A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowlingAscendantTransmit(null))
|
||||
|
||||
@@ -119,10 +119,10 @@
|
||||
|
||||
|
||||
if(traitorwin)
|
||||
text += "<br><font color='green'><B>The [special_role_text] was successful!</B></font>"
|
||||
text += "<br><font color='green'><B>The [special_role_text] was successful!</B></font><br>"
|
||||
feedback_add_details("traitor_success","SUCCESS")
|
||||
else
|
||||
text += "<br><font color='red'><B>The [special_role_text] has failed!</B></font>"
|
||||
text += "<br><font color='red'><B>The [special_role_text] has failed!</B></font><br>"
|
||||
feedback_add_details("traitor_success","FAIL")
|
||||
|
||||
if(length(SSticker.mode.implanted))
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
if(!gain_desc)
|
||||
gain_desc = "You have gained \the [src] ability."
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/cast_check(skipcharge = 0, mob/living/user = usr)
|
||||
/obj/effect/proc_holder/spell/vampire/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr)
|
||||
if(!user.mind)
|
||||
return 0
|
||||
if(!ishuman(user))
|
||||
@@ -45,7 +45,7 @@
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/vampire/can_cast(mob/user = usr)
|
||||
/obj/effect/proc_holder/spell/vampire/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(!user.mind)
|
||||
return 0
|
||||
if(!ishuman(user))
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
switch(href_list["school"])
|
||||
if("destruction")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball(null))
|
||||
to_chat(M, "<B>Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.")
|
||||
if("bluespace")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null))
|
||||
@@ -74,7 +74,7 @@
|
||||
to_chat(M, "<B>Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
|
||||
if("robeless")
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null))
|
||||
M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/mind_transfer(null))
|
||||
to_chat(M, "<B>Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.")
|
||||
|
||||
M.equip_to_slot_or_del(new /obj/item/radio/headset(M), slot_l_ear)
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
/datum/spellbook_entry/horseman
|
||||
name = "Curse of the Horseman"
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/horsemask
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/click/horsemask
|
||||
log_name = "HH"
|
||||
category = "Offensive"
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
|
||||
/datum/spellbook_entry/fireball
|
||||
name = "Fireball"
|
||||
spell_type = /obj/effect/proc_holder/spell/fireball
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/click/fireball
|
||||
log_name = "FB"
|
||||
category = "Offensive"
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
/datum/spellbook_entry/mindswap
|
||||
name = "Mindswap"
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
spell_type = /obj/effect/proc_holder/spell/targeted/click/mind_transfer
|
||||
log_name = "MT"
|
||||
category = "Mobility"
|
||||
|
||||
@@ -877,7 +877,7 @@
|
||||
return
|
||||
|
||||
/obj/item/spellbook/oneuse/fireball
|
||||
spell = /obj/effect/proc_holder/spell/fireball
|
||||
spell = /obj/effect/proc_holder/spell/targeted/click/fireball
|
||||
spellname = "fireball"
|
||||
icon_state = "bookfireball"
|
||||
desc = "This book feels warm to the touch."
|
||||
@@ -910,7 +910,7 @@
|
||||
user.EyeBlind(10)
|
||||
|
||||
/obj/item/spellbook/oneuse/mindswap
|
||||
spell = /obj/effect/proc_holder/spell/targeted/mind_transfer
|
||||
spell = /obj/effect/proc_holder/spell/targeted/click/mind_transfer
|
||||
spellname = "mindswap"
|
||||
icon_state = "bookmindswap"
|
||||
desc = "This book's cover is pristine, though its pages look ragged and torn."
|
||||
@@ -934,8 +934,8 @@
|
||||
to_chat(user, "<span class='notice'>You stare at the book some more, but there doesn't seem to be anything else to learn...</span>")
|
||||
return
|
||||
|
||||
var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new
|
||||
swapper.cast(user, stored_swap, 1)
|
||||
var/obj/effect/proc_holder/spell/targeted/click/mind_transfer/swapper = new
|
||||
swapper.cast(user, stored_swap)
|
||||
|
||||
to_chat(stored_swap, "<span class='warning'>You're suddenly somewhere else... and someone else?!</span>")
|
||||
to_chat(user, "<span class='warning'>Suddenly you're staring at [src] again... where are you, who are you?!</span>")
|
||||
@@ -966,7 +966,7 @@
|
||||
user.Weaken(20)
|
||||
|
||||
/obj/item/spellbook/oneuse/horsemask
|
||||
spell = /obj/effect/proc_holder/spell/targeted/horsemask
|
||||
spell = /obj/effect/proc_holder/spell/targeted/click/horsemask
|
||||
spellname = "horses"
|
||||
icon_state = "bookhorses"
|
||||
desc = "This book is more horse than your mind has room for."
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
Care should be taken in hiding the item you choose as your phylactery after using Bind Soul, as you cannot revive if it destroyed or too far from your body! <br><br> \
|
||||
</i>Provides Bind Soul, Ethereal Jaunt, Fireball, Rod Form, Disable Tech, and Greater Forcewall.<i>"
|
||||
log_name = "DL"
|
||||
spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/fireball, \
|
||||
spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/click/fireball, \
|
||||
/obj/effect/proc_holder/spell/targeted/rod_form, /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech, /obj/effect/proc_holder/spell/targeted/forcewall/greater)
|
||||
is_ragin_restricted = TRUE
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
)
|
||||
cybernetic_implants = list(
|
||||
/obj/item/organ/internal/cyberimp/eyes/xray,
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun,
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened,
|
||||
/obj/item/organ/internal/cyberimp/chest/nutriment/plus,
|
||||
/obj/item/organ/internal/cyberimp/arm/combat/centcom
|
||||
)
|
||||
|
||||
@@ -458,6 +458,7 @@
|
||||
total_positions = 0
|
||||
spawn_positions = 0
|
||||
supervisors = "the head of personnel"
|
||||
department_head = list("Head of Personnel")
|
||||
selection_color = "#dddddd"
|
||||
access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS)
|
||||
minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/obj/item/camera/spooky = 1,
|
||||
/obj/item/nullrod = 1
|
||||
)
|
||||
|
||||
|
||||
/datum/outfit/job/chaplain/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
|
||||
. = ..()
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
new_deity = deity_name
|
||||
B.deity_name = new_deity
|
||||
|
||||
H.AddSpell(new /obj/effect/proc_holder/spell/targeted/chaplain_bless(null))
|
||||
H.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/chaplain_bless(null))
|
||||
|
||||
var/accepted = 0
|
||||
var/outoftime = 0
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
return FALSE
|
||||
if(R.scrambledcodes)
|
||||
return FALSE
|
||||
if(!atoms_share_level(src, R))
|
||||
if(!atoms_share_level(get_turf(src), get_turf(R)))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -1272,7 +1272,7 @@
|
||||
ads_list = list("We like plants!","Don't you want some?","The greenest thumbs ever.","We like big plants.","Soft soil...")
|
||||
icon_state = "nutri"
|
||||
icon_deny = "nutri-deny"
|
||||
products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 30,/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 20,/obj/item/reagent_containers/glass/bottle/nutrient/rh = 10,/obj/item/reagent_containers/spray/pestspray = 20,
|
||||
products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 20,/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 13,/obj/item/reagent_containers/glass/bottle/nutrient/rh = 6,/obj/item/reagent_containers/spray/pestspray = 20,
|
||||
/obj/item/reagent_containers/syringe = 5,/obj/item/storage/bag/plants = 5,/obj/item/cultivator = 3,/obj/item/shovel/spade = 3,/obj/item/plant_analyzer = 4)
|
||||
contraband = list(/obj/item/reagent_containers/glass/bottle/ammonia = 10,/obj/item/reagent_containers/glass/bottle/diethylamine = 5)
|
||||
refill_canister = /obj/item/vending_refill/hydronutrients
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if(height == 0)
|
||||
return TRUE
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider))
|
||||
if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider) || isterrorspider(mover))
|
||||
return TRUE
|
||||
else if(istype(mover, /mob/living))
|
||||
if(prob(50))
|
||||
|
||||
@@ -37,70 +37,83 @@
|
||||
overlays.Cut()
|
||||
|
||||
/obj/item/aicard/attack_self(mob/user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
|
||||
/obj/item/aicard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/item/aicard/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "aicard.tmpl", "[name]", 600, 400, state = state)
|
||||
ui = new(user, src, ui_key, "AICard", "[name]", 600, 394, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
|
||||
/obj/item/aicard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state)
|
||||
/obj/item/aicard/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state)
|
||||
var/data[0]
|
||||
|
||||
var/mob/living/silicon/ai/AI = locate() in src
|
||||
if(istype(AI))
|
||||
data["has_ai"] = 1
|
||||
data["has_ai"] = TRUE
|
||||
data["name"] = AI.name
|
||||
data["hardware_integrity"] = ((AI.health + 100) / 2)
|
||||
data["integrity"] = ((AI.health + 100) / 2)
|
||||
data["radio"] = !AI.aiRadio.disabledAi
|
||||
data["wireless"] = !AI.control_disabled
|
||||
data["operational"] = AI.stat != DEAD
|
||||
data["flushing"] = flush
|
||||
|
||||
var/laws[0]
|
||||
for(var/datum/ai_law/AL in AI.laws.all_laws())
|
||||
laws[++laws.len] = list("index" = AL.get_index(), "law" = sanitize(AL.law))
|
||||
for(var/datum/ai_law/law in AI.laws.all_laws())
|
||||
if(law in AI.laws.ion_laws) // If we're an ion law, give it an ion index code
|
||||
laws.Add(ionnum() + ". " + law.law)
|
||||
else
|
||||
laws.Add(num2text(law.get_index()) + ". " + law.law)
|
||||
data["laws"] = laws
|
||||
data["has_laws"] = laws.len
|
||||
data["has_laws"] = length(AI.laws.all_laws())
|
||||
|
||||
else
|
||||
data["has_ai"] = FALSE // If this isn't passed to tgui, it won't show there isn't a AI in the card.
|
||||
|
||||
return data
|
||||
|
||||
|
||||
/obj/item/aicard/Topic(href, href_list, nowindow, state)
|
||||
/obj/item/aicard/tgui_act(action, params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
var/mob/living/silicon/ai/AI = locate() in src
|
||||
if(!istype(AI))
|
||||
return 1
|
||||
return
|
||||
|
||||
var/user = usr
|
||||
switch(action)
|
||||
if("wipe")
|
||||
if(flush) // Don't doublewipe.
|
||||
to_chat(user, "<span class='warning'>You are already wiping this AI!</span>")
|
||||
return
|
||||
var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No")
|
||||
if(confirm == "Yes" && (tgui_status(user, GLOB.tgui_inventory_state) == STATUS_INTERACTIVE)) // And make doubly sure they want to wipe (three total clicks)
|
||||
msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].", ATKLOG_FEW)
|
||||
add_attack_logs(user, AI, "Wiped with [src].")
|
||||
INVOKE_ASYNC(src, .proc/wipe_ai)
|
||||
|
||||
if(href_list["wipe"])
|
||||
var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No")
|
||||
if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE))
|
||||
add_attack_logs(user, AI, "Wiped with [src].", ATKLOG_FEW)
|
||||
flush = 1
|
||||
AI.suiciding = 1
|
||||
to_chat(AI, "Your core files are being wiped!")
|
||||
while(AI && AI.stat != DEAD)
|
||||
AI.adjustOxyLoss(2)
|
||||
sleep(10)
|
||||
flush = 0
|
||||
if("radio")
|
||||
AI.aiRadio.disabledAi = !AI.aiRadio.disabledAi
|
||||
to_chat(AI, "<span class='warning'>Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!</span>")
|
||||
to_chat(user, "<span class='notice'>You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.</span>")
|
||||
|
||||
if(href_list["radio"])
|
||||
AI.aiRadio.disabledAi = text2num(href_list["radio"])
|
||||
to_chat(AI, "<span class='warning'>Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!</span>")
|
||||
to_chat(user, "<span class='notice'>You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.</span>")
|
||||
if("wireless")
|
||||
AI.control_disabled = !AI.control_disabled
|
||||
to_chat(AI, "<span class='warning'>Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!</span>")
|
||||
to_chat(user, "<span class='notice'>You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.</span>")
|
||||
update_icon()
|
||||
|
||||
if(href_list["wireless"])
|
||||
AI.control_disabled = text2num(href_list["wireless"])
|
||||
to_chat(AI, "<span class='warning'>Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!</span>")
|
||||
to_chat(user, "<span class='notice'>You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.</span>")
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
return 1
|
||||
/obj/item/aicard/proc/wipe_ai()
|
||||
var/mob/living/silicon/ai/AI = locate() in src
|
||||
flush = TRUE
|
||||
AI.suiciding = TRUE
|
||||
to_chat(AI, "Your core files are being wiped!")
|
||||
while(AI && AI.stat != DEAD)
|
||||
AI.adjustOxyLoss(2)
|
||||
sleep(10)
|
||||
flush = FALSE
|
||||
|
||||
@@ -45,9 +45,6 @@
|
||||
QDEL_NULL(keyslot2)
|
||||
return ..()
|
||||
|
||||
/obj/item/radio/headset/list_channels(var/mob/user)
|
||||
return list_secure_channels()
|
||||
|
||||
/obj/item/radio/headset/examine(mob/user)
|
||||
. = ..()
|
||||
if(in_range(src, user) && radio_desc)
|
||||
|
||||
@@ -245,14 +245,7 @@
|
||||
usesound = 'sound/items/deconstruct.ogg'
|
||||
|
||||
/obj/item/radio/intercom/locked
|
||||
var/locked_frequency
|
||||
|
||||
/obj/item/radio/intercom/locked/set_frequency(var/frequency)
|
||||
if(frequency == locked_frequency)
|
||||
..(locked_frequency)
|
||||
|
||||
/obj/item/radio/intercom/locked/list_channels()
|
||||
return ""
|
||||
freqlock = TRUE
|
||||
|
||||
/obj/item/radio/intercom/locked/ai_private
|
||||
name = "\improper AI intercom"
|
||||
|
||||
@@ -129,7 +129,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
/obj/item/radio/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "Radio", name, 360, 150 + (length(channels) * 20), master_ui, state)
|
||||
var/list/schannels = list_secure_channels(user)
|
||||
var/list/ichannels = list_internal_channels(user)
|
||||
var/calc_height = 150 + (schannels.len * 20) + (ichannels.len * 10)
|
||||
ui = new(user, src, ui_key, "Radio", name, 400, calc_height, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/item/radio/tgui_data(mob/user)
|
||||
@@ -142,9 +145,8 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
data["maxFrequency"] = freerange ? RADIO_HIGH_FREQ : PUBLIC_HIGH_FREQ
|
||||
data["canReset"] = frequency == initial(frequency) ? FALSE : TRUE
|
||||
data["freqlock"] = freqlock
|
||||
data["channels"] = list()
|
||||
for(var/channel in channels)
|
||||
data["channels"][channel] = channels[channel] & FREQ_LISTENING
|
||||
data["schannels"] = list_secure_channels(user)
|
||||
data["ichannels"] = list_internal_channels(user)
|
||||
|
||||
data["has_loudspeaker"] = has_loudspeaker
|
||||
data["loudspeaker"] = loudspeaker
|
||||
@@ -173,6 +175,12 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
usr << browse(null, "window=radio")
|
||||
if(.)
|
||||
set_frequency(sanitize_frequency(tune, freerange))
|
||||
if("ichannel") // change primary frequency to an internal channel authorized by access
|
||||
if(freqlock)
|
||||
return
|
||||
var/freq = params["ichannel"]
|
||||
if(has_channel_access(usr, freq))
|
||||
set_frequency(text2num(freq))
|
||||
if("listen")
|
||||
listening = !listening
|
||||
if("broadcast")
|
||||
@@ -198,34 +206,32 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
if(.)
|
||||
add_fingerprint(usr)
|
||||
|
||||
/obj/item/radio/proc/list_channels(var/mob/user)
|
||||
return list_internal_channels(user)
|
||||
|
||||
/obj/item/radio/proc/list_secure_channels(var/mob/user)
|
||||
var/dat[0]
|
||||
|
||||
for(var/ch_name in channels)
|
||||
var/chan_stat = channels[ch_name]
|
||||
var/listening = !!(chan_stat & FREQ_LISTENING) != 0
|
||||
|
||||
dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "chan_span" = SSradio.frequency_span_class(SSradio.radiochannels[ch_name]))))
|
||||
|
||||
/obj/item/radio/proc/list_secure_channels(mob/user)
|
||||
var/list/dat = list()
|
||||
for(var/channel in channels)
|
||||
dat[channel] = channels[channel] & FREQ_LISTENING
|
||||
return dat
|
||||
|
||||
/obj/item/radio/proc/list_internal_channels(var/mob/user)
|
||||
var/dat[0]
|
||||
/obj/item/radio/proc/list_internal_channels(mob/user)
|
||||
var/list/dat = list()
|
||||
if(freqlock)
|
||||
return dat
|
||||
for(var/internal_chan in internal_channels)
|
||||
var/freqnum = text2num(internal_chan)
|
||||
var/freqname = get_frequency_name(freqnum)
|
||||
if(has_channel_access(user, internal_chan))
|
||||
dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "chan_span" = SSradio.frequency_span_class(text2num(internal_chan)))))
|
||||
|
||||
dat[freqname] = freqnum // unlike secure_channels, this is set to the freq number so Radio.js can use it as an arg
|
||||
return dat
|
||||
|
||||
/obj/item/radio/proc/has_channel_access(var/mob/user, var/freq)
|
||||
/obj/item/radio/proc/has_channel_access(mob/user, freq)
|
||||
if(!user)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(!(freq in internal_channels))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(isrobot(user))
|
||||
return FALSE // cyborgs and drones are not allowed to remotely re-tune intercomms, etc
|
||||
|
||||
return user.has_internal_radio_channel_access(user, internal_channels[freq])
|
||||
|
||||
@@ -603,13 +609,14 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
/obj/item/radio/borg
|
||||
name = "Cyborg Radio"
|
||||
var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks
|
||||
var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key
|
||||
var/obj/item/encryptionkey/keyslot // Borg radios can handle a single encryption key
|
||||
icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component.
|
||||
icon_state = "radio"
|
||||
has_loudspeaker = TRUE
|
||||
loudspeaker = FALSE
|
||||
canhear_range = 0
|
||||
dog_fashion = null
|
||||
freqlock = TRUE // don't let cyborgs change the default channel of their internal radio away from common
|
||||
|
||||
/obj/item/radio/borg/syndicate
|
||||
keyslot = new /obj/item/encryptionkey/syndicate/nukeops
|
||||
@@ -623,9 +630,6 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
myborg = null
|
||||
return ..()
|
||||
|
||||
/obj/item/radio/borg/list_channels(var/mob/user)
|
||||
return list_secure_channels(user)
|
||||
|
||||
/obj/item/radio/borg/syndicate/New()
|
||||
..()
|
||||
syndiekey = keyslot
|
||||
|
||||
@@ -59,6 +59,14 @@
|
||||
/obj/item/trash/fried_vox
|
||||
name = "Kentucky Fried Vox"
|
||||
icon_state = "fried_vox_empty"
|
||||
item_state = "fried_vox_empty"
|
||||
slot_flags = SLOT_HEAD
|
||||
dog_fashion = /datum/dog_fashion/head/fried_vox_empty
|
||||
sprite_sheets = list(
|
||||
"Skrell" = 'icons/mob/species/skrell/head.dmi',
|
||||
"Drask" = 'icons/mob/species/drask/head.dmi',
|
||||
"Kidan" = 'icons/mob/species/kidan/head.dmi'
|
||||
)
|
||||
|
||||
/obj/item/trash/pistachios
|
||||
name = "Pistachios pack"
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/claymore/highlander/equipped(mob/user, slot)
|
||||
if(!ishuman(user))
|
||||
if(!ishuman(user) || !user.mind)
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(slot == slot_r_hand || slot == slot_l_hand)
|
||||
if(H.martial_art && H.martial_art != style)
|
||||
style.teach(H, 1)
|
||||
if(H.mind.martial_art && H.mind.martial_art != style)
|
||||
style.teach(H, TRUE)
|
||||
to_chat(H, "<span class='notice'>THERE CAN ONLY BE ONE!</span>")
|
||||
else if(H.martial_art && H.martial_art == style)
|
||||
else if(H.mind.martial_art && H.mind.martial_art == style)
|
||||
style.remove(H)
|
||||
var/obj/item/claymore/highlander/sword = H.is_in_hands(/obj/item/claymore/highlander)
|
||||
if(sword)
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
|
||||
/obj/item/implant/krav_maga/activate()
|
||||
var/mob/living/carbon/human/H = imp_in
|
||||
if(!ishuman(H))
|
||||
if(!ishuman(H) || !H.mind)
|
||||
return
|
||||
if(istype(H.martial_art, /datum/martial_art/krav_maga))
|
||||
if(istype(H.mind.martial_art, /datum/martial_art/krav_maga))
|
||||
style.remove(H)
|
||||
else
|
||||
style.teach(H,1)
|
||||
style.teach(H, TRUE)
|
||||
|
||||
/obj/item/implanter/krav_maga
|
||||
name = "implanter (krav maga)"
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
..()
|
||||
if(climber)
|
||||
climber.Weaken(2)
|
||||
climber.visible_message("<span class='warning'>[climber.name] has been knocked off the table", "You've been knocked off the table", "You see [climber.name] get knocked off the table</span>")
|
||||
climber.visible_message("<span class='warning'>[climber.name] has been knocked off the table", "You've been knocked off the table", "You hear [climber.name] get knocked off the table</span>")
|
||||
else if(Adjacent(user) && user.pulling && user.pulling.pass_flags & PASSTABLE)
|
||||
user.Move_Pulled(src)
|
||||
if(user.pulling.loc == loc)
|
||||
|
||||
+11
-3
@@ -222,8 +222,12 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
|
||||
if(input["key"] != config.comms_password)
|
||||
return "Bad Key"
|
||||
else
|
||||
var/prtext = input["announce"]
|
||||
var/pr_substring = copytext(prtext, 1, 23)
|
||||
if(pr_substring == "Pull Request merged by")
|
||||
GLOB.pending_server_update = TRUE
|
||||
for(var/client/C in GLOB.clients)
|
||||
to_chat(C, "<span class='announce'>PR: [input["announce"]]</span>")
|
||||
to_chat(C, "<span class='announce'>PR: [prtext]</span>")
|
||||
|
||||
else if("kick" in input)
|
||||
/*
|
||||
@@ -270,7 +274,7 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
|
||||
else if("hostannounce" in input)
|
||||
if(!key_valid)
|
||||
return keySpamProtect(addr)
|
||||
|
||||
GLOB.pending_server_update = TRUE
|
||||
to_chat(world, "<hr><span style='color: #12A5F4'><b>Server Announcement:</b> [input["message"]]</span><hr>")
|
||||
|
||||
/proc/keySpamProtect(var/addr)
|
||||
@@ -339,8 +343,12 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday)
|
||||
return
|
||||
#endif
|
||||
|
||||
var/secs_before_auto_reconnect = 10
|
||||
if(GLOB.pending_server_update)
|
||||
secs_before_auto_reconnect = 60
|
||||
to_chat(world, "<span class='boldannounce'>Reboot will take a little longer, due to pending updates.</span>")
|
||||
for(var/client/C in GLOB.clients)
|
||||
var/secs_before_auto_reconnect = 10 // TODO: make it higher if server is due for an update @AffectedArc07
|
||||
|
||||
C << output(list2params(list(secs_before_auto_reconnect)), "browseroutput:reboot")
|
||||
if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
|
||||
C << link("byond://[config.server]")
|
||||
|
||||
@@ -203,6 +203,12 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new())
|
||||
typepath = /obj/item/toy/toy_xeno
|
||||
cost = 80
|
||||
|
||||
/datum/prize_item/rubberducky
|
||||
name = "Rubber Ducky"
|
||||
desc = "Your favorite bathtime buddy, all squeaks and quacks quality assured."
|
||||
typepath = /obj/item/bikehorn/rubberducky
|
||||
cost = 80
|
||||
|
||||
/datum/prize_item/tacticool
|
||||
name = "Tacticool Turtleneck"
|
||||
desc = "A cool-looking turtleneck."
|
||||
|
||||
@@ -36,7 +36,10 @@
|
||||
////////////
|
||||
//SECURITY//
|
||||
////////////
|
||||
var/next_allowed_topic_time = 10
|
||||
|
||||
///Used for limiting the rate of topic sends by the client to avoid abuse
|
||||
var/list/topiclimiter
|
||||
|
||||
// comment out the line below when debugging locally to enable the options & messages menu
|
||||
//control_freak = 1
|
||||
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
#define SUGGESTED_CLIENT_VERSION 511 // only integers (e.g: 510, 511) useful here. Does not properly handle minor versions (e.g: 510.58, 511.848)
|
||||
#define SSD_WARNING_TIMER 30 // cycles, not seconds, so 30=60s
|
||||
|
||||
#define LIMITER_SIZE 5
|
||||
#define CURRENT_SECOND 1
|
||||
#define SECOND_COUNT 2
|
||||
#define CURRENT_MINUTE 3
|
||||
#define MINUTE_COUNT 4
|
||||
#define ADMINSWARNED_AT 5
|
||||
|
||||
/*
|
||||
When somebody clicks a link in game, this Topic is called first.
|
||||
It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever]
|
||||
@@ -59,10 +66,38 @@
|
||||
if(href_list["_src_"] == "chat")
|
||||
return chatOutput.Topic(href, href_list)
|
||||
|
||||
//Reduces spamming of links by dropping calls that happen during the delay period
|
||||
if(next_allowed_topic_time > world.time)
|
||||
return
|
||||
next_allowed_topic_time = world.time + TOPIC_SPAM_DELAY
|
||||
// Rate limiting
|
||||
var/mtl = 100 // 100 topics per minute
|
||||
if (!holder) // Admins are allowed to spam click, deal with it.
|
||||
var/minute = round(world.time, 600)
|
||||
if (!topiclimiter)
|
||||
topiclimiter = new(LIMITER_SIZE)
|
||||
if (minute != topiclimiter[CURRENT_MINUTE])
|
||||
topiclimiter[CURRENT_MINUTE] = minute
|
||||
topiclimiter[MINUTE_COUNT] = 0
|
||||
topiclimiter[MINUTE_COUNT] += 1
|
||||
if (topiclimiter[MINUTE_COUNT] > mtl)
|
||||
var/msg = "Your previous action was ignored because you've done too many in a minute."
|
||||
if (minute != topiclimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them)
|
||||
topiclimiter[ADMINSWARNED_AT] = minute
|
||||
msg += " Administrators have been informed."
|
||||
log_game("[key_name(src)] Has hit the per-minute topic limit of [mtl] topic calls in a given game minute")
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] Has hit the per-minute topic limit of [mtl] topic calls in a given game minute")
|
||||
to_chat(src, "<span class='danger'>[msg]</span>")
|
||||
return
|
||||
|
||||
var/stl = 10 // 10 topics a second
|
||||
if (!holder) // Admins are allowed to spam click, deal with it.
|
||||
var/second = round(world.time, 10)
|
||||
if (!topiclimiter)
|
||||
topiclimiter = new(LIMITER_SIZE)
|
||||
if (second != topiclimiter[CURRENT_SECOND])
|
||||
topiclimiter[CURRENT_SECOND] = second
|
||||
topiclimiter[SECOND_COUNT] = 0
|
||||
topiclimiter[SECOND_COUNT] += 1
|
||||
if (topiclimiter[SECOND_COUNT] > stl)
|
||||
to_chat(src, "<span class='danger'>Your previous action was ignored because you've done too many in a second</span>")
|
||||
return
|
||||
|
||||
//search the href for script injection
|
||||
if( findtext(href,"<script",1,0) )
|
||||
@@ -945,3 +980,10 @@
|
||||
|
||||
to_chat(usr, "<span class='notice'>UI resource files resent successfully. If you are still having issues, please try manually clearing your BYOND cache. <b>This can be achieved by opening your BYOND launcher, pressing the cog in the top right, selecting preferences, going to the Games tab, and pressing 'Clear Cache'.</b></span>")
|
||||
|
||||
|
||||
#undef LIMITER_SIZE
|
||||
#undef CURRENT_SECOND
|
||||
#undef SECOND_COUNT
|
||||
#undef CURRENT_MINUTE
|
||||
#undef MINUTE_COUNT
|
||||
#undef ADMINSWARNED_AT
|
||||
|
||||
@@ -344,3 +344,10 @@
|
||||
recharging_time = world.time + recharging_rate
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Something prevents you from dashing forward!</span>")
|
||||
|
||||
/obj/item/clothing/shoes/ducky
|
||||
name = "rubber ducky shoes"
|
||||
desc = "These shoes are made for quacking, and thats just what they'll do."
|
||||
icon_state = "ducky"
|
||||
item_state = "ducky"
|
||||
shoe_sound = "sound/items/squeaktoy.ogg"
|
||||
|
||||
@@ -143,3 +143,12 @@
|
||||
reqs = list(/obj/item/stack/sheet/animalhide/lizard = 1, /obj/item/stack/sheet/leather = 1)
|
||||
time = 60
|
||||
category = CAT_CLOTHING
|
||||
|
||||
/datum/crafting_recipe/rubberduckyshoes
|
||||
name = "Rubber Ducky Shoes"
|
||||
result = /obj/item/clothing/shoes/ducky
|
||||
time = 45
|
||||
reqs = list(/obj/item/bikehorn/rubberducky = 2,
|
||||
/obj/item/clothing/shoes/sandal = 1)
|
||||
tools = list(TOOL_WIRECUTTER)
|
||||
category = CAT_CLOTHING
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
spawncount = 2
|
||||
if(4)
|
||||
// Pretty strong.
|
||||
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/princess
|
||||
spawncount = 2
|
||||
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen/princess
|
||||
spawncount = 3
|
||||
if(5)
|
||||
// Strongest, only used during highpop.
|
||||
spider_type = /mob/living/simple_animal/hostile/poison/terror_spider/queen
|
||||
|
||||
@@ -27,10 +27,7 @@
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throw_speed = 3
|
||||
throw_range = 10
|
||||
|
||||
/obj/item/reagent_containers/spray/weedspray/New()
|
||||
..()
|
||||
reagents.add_reagent("atrazine", 100)
|
||||
list_reagents = list("atrazine" = 100)
|
||||
|
||||
/obj/item/reagent_containers/spray/weedspray/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] is huffing the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.</span>")
|
||||
@@ -49,10 +46,7 @@
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throw_speed = 3
|
||||
throw_range = 10
|
||||
|
||||
/obj/item/reagent_containers/spray/pestspray/New()
|
||||
..()
|
||||
reagents.add_reagent("pestkiller", 100)
|
||||
list_reagents = list("pestkiller" = 100)
|
||||
|
||||
/obj/item/reagent_containers/spray/pestspray/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] is huffing the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.</span>")
|
||||
@@ -222,79 +216,112 @@
|
||||
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient
|
||||
name = "bottle of nutrient"
|
||||
name = "jug of nutrient"
|
||||
desc = "A decent sized plastic jug."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
volume = 50
|
||||
icon_state = "plastic_jug"
|
||||
item_state = "plastic_jug"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
amount_per_transfer_from_this = 10
|
||||
possible_transfer_amounts = list(1,2,5,10,15,25,50)
|
||||
possible_transfer_amounts = list(1,2,5,10,20,40,80)
|
||||
container_type = OPENCONTAINER
|
||||
volume = 80
|
||||
hitsound = 'sound/weapons/jug_empty_impact.ogg'
|
||||
throwhitsound = 'sound/weapons/jug_empty_impact.ogg'
|
||||
force = 0.2
|
||||
throwforce = 0.2
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/New()
|
||||
..()
|
||||
add_lid()
|
||||
pixel_x = rand(-5, 5)
|
||||
pixel_y = rand(-5, 5)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/on_reagent_change()
|
||||
. = ..()
|
||||
update_icon()
|
||||
if(reagents.total_volume)
|
||||
hitsound = 'sound/weapons/jug_filled_impact.ogg'
|
||||
throwhitsound = 'sound/weapons/jug_filled_impact.ogg'
|
||||
else
|
||||
hitsound = 'sound/weapons/jug_empty_impact.ogg'
|
||||
throwhitsound = 'sound/weapons/jug_empty_impact.ogg'
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/update_icon()
|
||||
cut_overlays()
|
||||
|
||||
if(reagents.total_volume)
|
||||
var/image/filling = image('icons/obj/reagentfillings.dmi', src, "plastic_jug10")
|
||||
|
||||
var/percent = round((reagents.total_volume / volume) * 100)
|
||||
switch(percent)
|
||||
if(0 to 10)
|
||||
filling.icon_state = "plastic_jug-10"
|
||||
if(11 to 29)
|
||||
filling.icon_state = "plastic_jug25"
|
||||
if(30 to 45)
|
||||
filling.icon_state = "plastic_jug40"
|
||||
if(46 to 61)
|
||||
filling.icon_state = "plastic_jug55"
|
||||
if(62 to 77)
|
||||
filling.icon_state = "plastic_jug70"
|
||||
if(78 to 92)
|
||||
filling.icon_state = "plastic_jug85"
|
||||
if(93 to INFINITY)
|
||||
filling.icon_state = "plastic_jug100"
|
||||
|
||||
filling.icon += mix_color_from_reagents(reagents.reagent_list)
|
||||
add_overlay(filling)
|
||||
|
||||
if(!is_open_container())
|
||||
add_overlay("lid_jug")
|
||||
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/ez
|
||||
name = "jug of E-Z-Nutrient"
|
||||
desc = "Contains a fertilizer that causes mild mutations with each harvest."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "plastic_jug_ez"
|
||||
list_reagents = list("eznutriment" = 80)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/l4z
|
||||
name = "jug of Left 4 Zed"
|
||||
desc = "Contains a fertilizer that limits plant yields to no more than one and causes significant mutations in plants."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "plastic_jug_l4z"
|
||||
list_reagents = list("left4zednutriment" = 80)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/rh
|
||||
name = "jug of Robust Harvest"
|
||||
desc = "Contains a fertilizer that increases the yield of a plant by 30% while causing no mutations."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "plastic_jug_rh"
|
||||
list_reagents = list("robustharvestnutriment" = 80)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/empty
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "plastic_jug"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/killer
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "plastic_jug_k"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/killer/New()
|
||||
..()
|
||||
pixel_x = rand(-5, 5)
|
||||
pixel_y = rand(-5, 5)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/ez
|
||||
name = "bottle of E-Z-Nutrient"
|
||||
desc = "Contains a fertilizer that causes mild mutations with each harvest."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/ez/New()
|
||||
..()
|
||||
reagents.add_reagent("eznutriment", 50)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/l4z
|
||||
name = "bottle of Left 4 Zed"
|
||||
desc = "Contains a fertilizer that limits plant yields to no more than one and causes significant mutations in plants."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle18"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/l4z/New()
|
||||
..()
|
||||
reagents.add_reagent("left4zednutriment", 50)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/rh
|
||||
name = "bottle of Robust Harvest"
|
||||
desc = "Contains a fertilizer that increases the yield of a plant by 30% while causing no mutations."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle15"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/rh/New()
|
||||
..()
|
||||
reagents.add_reagent("robustharvestnutriment", 50)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/empty
|
||||
name = "bottle"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/killer
|
||||
name = "bottle"
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle16"
|
||||
volume = 50
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
amount_per_transfer_from_this = 10
|
||||
possible_transfer_amounts = list(1,2,5,10,15,25,50)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/killer/weedkiller
|
||||
name = "bottle of weed killer"
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/killer/weedkiller
|
||||
name = "jug of weed killer"
|
||||
desc = "Contains a herbicide."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle19"
|
||||
icon_state = "plastic_jug_wk"
|
||||
list_reagents = list("atrazine" = 80)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/killer/weedkiller/New()
|
||||
..()
|
||||
reagents.add_reagent("atrazine", 50)
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/killer/pestkiller
|
||||
name = "bottle of pest spray"
|
||||
/obj/item/reagent_containers/glass/bottle/nutrient/killer/pestkiller
|
||||
name = "jug of pest spray"
|
||||
desc = "Contains a pesticide."
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bottle20"
|
||||
|
||||
/obj/item/reagent_containers/glass/bottle/killer/pestkiller/New()
|
||||
..()
|
||||
reagents.add_reagent("pestkiller", 50)
|
||||
icon_state = "plastic_jug_pk"
|
||||
list_reagents = list("pestkiller" = 80)
|
||||
|
||||
@@ -744,6 +744,10 @@
|
||||
to_chat(user, "<span class='notice'>[reagent_source] is empty.</span>")
|
||||
return 1
|
||||
|
||||
if(reagent_source.has_lid && !reagent_source.is_drainable()) //if theres a LID then cannot transfer reagents.
|
||||
to_chat(user, "<span class='warning'>You need to open [O] first!</span>")
|
||||
return TRUE
|
||||
|
||||
var/list/trays = list(src)//makes the list just this in cases of syringes and compost etc
|
||||
var/target = myseed ? myseed.plantname : src
|
||||
var/visi_msg = ""
|
||||
|
||||
@@ -1,93 +1,37 @@
|
||||
///Adminfu
|
||||
//Help act:Heal/revie GP //p is for help
|
||||
//Disarm:Stun
|
||||
//Grab:Neck
|
||||
//Harm:Gib
|
||||
#define HEAL_COMBO "GP"
|
||||
|
||||
/datum/martial_art/adminfu
|
||||
name = "Way of the Dancing Admin"
|
||||
help_verb = /mob/living/carbon/human/proc/adminfu_help
|
||||
|
||||
/datum/martial_art/adminfu/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(findtext(streak,HEAL_COMBO))
|
||||
streak = ""
|
||||
healPalm(A,D)
|
||||
return 1
|
||||
return 0
|
||||
has_explaination_verb = TRUE
|
||||
combos = list(/datum/martial_combo/adminfu/healing_palm)
|
||||
|
||||
/datum/martial_art/adminfu/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
if(!D.stat)//do not kill what is dead...
|
||||
A.do_attack_animation(D)
|
||||
D.visible_message("<span class='warning'>[A] manifests a large glowing toolbox and shoves it in [D]'s chest!</span>", \
|
||||
"<spac class='userdanger'>[A] shoves a mystical toolbox in your chest!</span>")
|
||||
D.death()
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/martial_art/adminfu/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
A.do_attack_animation(D)
|
||||
D.Weaken(25)
|
||||
D.Stun(25)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/adminfu/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
var/obj/item/grab/G = D.grabbedby(A,1)
|
||||
if(G)
|
||||
G.state = GRAB_NECK
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/adminfu/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("P",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
|
||||
/datum/martial_art/adminfu/proc/healPalm(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
A.do_attack_animation(D)
|
||||
D.visible_message("<span class='warning'>[A] smacks [D] in the forehead!</span>")
|
||||
|
||||
//its the staff of healing code..hush
|
||||
if(istype(D,/mob))
|
||||
var/old_stat = D.stat
|
||||
if(isanimal(D) && D.stat == DEAD)
|
||||
var/mob/living/simple_animal/O = D
|
||||
var/mob/living/simple_animal/P = new O.type(O.loc)
|
||||
P.real_name = O.real_name
|
||||
P.name = O.name
|
||||
if(O.mind)
|
||||
O.mind.transfer_to(P)
|
||||
else
|
||||
P.key = O.key
|
||||
qdel(O)
|
||||
D = P
|
||||
else
|
||||
D.revive()
|
||||
D.suiciding = 0
|
||||
if(!D.ckey)
|
||||
for(var/mob/dead/observer/ghost in GLOB.player_list)
|
||||
if(D.real_name == ghost.real_name)
|
||||
ghost.reenter_corpse()
|
||||
break
|
||||
if(old_stat != DEAD)
|
||||
to_chat(D, "<span class='notice'>You feel great!</span>")
|
||||
else
|
||||
to_chat(D, "<span class='notice'>You rise with a start, you're alive!!!</span>")
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/proc/adminfu_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember the way of the dancing admin."
|
||||
set category = "Adminfu"
|
||||
|
||||
to_chat(usr, "<span class='notice'>Grab</span>: Automatic Neck Grab.")
|
||||
to_chat(usr, "<span class='notice'>Disarm</span>: Stun/weaken")
|
||||
to_chat(usr, "<span class='notice'>Harm</span>: Death.")
|
||||
to_chat(usr, "<span class='notice'>Healing Palm:</span>:Combo:Grab,Help intent. Heals or revives a crature.")
|
||||
|
||||
/datum/martial_art/adminfu/explaination_header(user)
|
||||
to_chat(user, "<span class='notice'>Grab</span>: Automatic Neck Grab.")
|
||||
to_chat(user, "<span class='notice'>Disarm</span>: Stun/weaken")
|
||||
to_chat(user, "<span class='notice'>Harm</span>: Death.")
|
||||
|
||||
/obj/item/adminfu_scroll
|
||||
name = "frayed scroll"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/datum/martial_combo/adminfu/healing_palm
|
||||
name = "Healing Palm"
|
||||
steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HELP)
|
||||
explaination_text = "Heals or revives a creature."
|
||||
combo_text_override = "Grab, switch hands, Help"
|
||||
|
||||
/datum/martial_combo/adminfu/healing_palm/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
user.do_attack_animation(target)
|
||||
target.visible_message("<span class='warning'>[user] smacks [target] in the forehead!</span>")
|
||||
|
||||
//its the staff of healing code..hush
|
||||
if(istype(target,/mob))
|
||||
var/old_stat = target.stat
|
||||
if(isanimal(target) && target.stat == DEAD)
|
||||
var/mob/living/simple_animal/O = target
|
||||
var/mob/living/simple_animal/P = new O.type(O.loc)
|
||||
P.real_name = O.real_name
|
||||
P.name = O.name
|
||||
if(O.mind)
|
||||
O.mind.transfer_to(P)
|
||||
else
|
||||
P.key = O.key
|
||||
qdel(O)
|
||||
target = P
|
||||
else
|
||||
target.revive()
|
||||
target.suiciding = 0
|
||||
if(!target.ckey)
|
||||
for(var/mob/dead/observer/ghost in GLOB.player_list)
|
||||
if(target.real_name == ghost.real_name)
|
||||
ghost.reenter_corpse()
|
||||
break
|
||||
if(old_stat != DEAD)
|
||||
to_chat(target, "<span class='notice'>You feel great!</span>")
|
||||
else
|
||||
to_chat(target, "<span class='notice'>You rise with a start, you're alive!!!</span>")
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_FAIL
|
||||
@@ -0,0 +1,18 @@
|
||||
/datum/martial_combo/cqc/consecutive
|
||||
name = "Consecutive CQC"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Mainly offensive move, huge damage and decent stamina damage."
|
||||
|
||||
/datum/martial_combo/cqc/consecutive/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat)
|
||||
target.visible_message("<span class='warning'>[user] strikes [target]'s abdomen, neck and back consecutively</span>", \
|
||||
"<span class='userdanger'>[user] strikes your abdomen, neck and back consecutively!</span>")
|
||||
playsound(get_turf(target), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
|
||||
var/obj/item/I = target.get_active_hand()
|
||||
if(I && target.drop_item())
|
||||
user.put_in_hands(I)
|
||||
target.adjustStaminaLoss(50)
|
||||
target.apply_damage(25, BRUTE)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_FAIL
|
||||
@@ -0,0 +1,24 @@
|
||||
/datum/martial_combo/cqc/kick
|
||||
name = "CQC Kick"
|
||||
steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Knocks opponent away. Knocks out stunned or knocked down opponents."
|
||||
|
||||
/datum/martial_combo/cqc/kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
. = MARTIAL_COMBO_FAIL
|
||||
if(!target.stat || !target.IsWeakened())
|
||||
target.visible_message("<span class='warning'>[user] kicks [target] back!</span>", \
|
||||
"<span class='userdanger'>[user] kicks you back!</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(target, user.dir)
|
||||
target.throw_at(throw_target, 1, 14, user)
|
||||
target.apply_damage(10, BRUTE)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL)
|
||||
. = MARTIAL_COMBO_DONE
|
||||
if(target.IsWeakened() && !target.stat)
|
||||
target.visible_message("<span class='warning'>[user] kicks [target]'s head, knocking [target.p_them()] out!</span>", \
|
||||
"<span class='userdanger'>[user] kicks your head, knocking you out!</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/genhit1.ogg', 50, 1, -1)
|
||||
target.SetSleeping(15)
|
||||
target.adjustBrainLoss(15)
|
||||
add_attack_logs(user, target, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL)
|
||||
. = MARTIAL_COMBO_DONE
|
||||
@@ -0,0 +1,11 @@
|
||||
/datum/martial_combo/cqc/pressure
|
||||
name = "Pressure"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_GRAB)
|
||||
explaination_text = "Decent stamina damage."
|
||||
|
||||
/datum/martial_combo/cqc/pressure/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
target.visible_message("<span class='warning'>[user] forces their arm on [target]'s neck!</span>")
|
||||
target.adjustStaminaLoss(60)
|
||||
playsound(get_turf(user), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE
|
||||
@@ -0,0 +1,22 @@
|
||||
/datum/martial_combo/cqc/restrain
|
||||
name = "Restrain"
|
||||
steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_GRAB)
|
||||
explaination_text = "Locks opponents into a restraining position, disarm to knock them out with a choke hold."
|
||||
combo_text_override = "Grab, switch hands, Grab"
|
||||
|
||||
/datum/martial_combo/cqc/restrain/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
var/datum/martial_art/cqc/CQC = MA
|
||||
if(!istype(CQC))
|
||||
return MARTIAL_COMBO_FAIL
|
||||
if(CQC.restraining)
|
||||
return MARTIAL_COMBO_FAIL
|
||||
if(!target.stat)
|
||||
target.visible_message("<span class='warning'>[user] locks [target] into a restraining position!</span>", \
|
||||
"<span class='userdanger'>[user] locks you into a restraining position!</span>")
|
||||
target.adjustStaminaLoss(20)
|
||||
target.Stun(5)
|
||||
CQC.restraining = TRUE
|
||||
addtimer(CALLBACK(CQC, /datum/martial_art/cqc/.proc/drop_restraining), 50, TIMER_UNIQUE)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_FAIL
|
||||
@@ -0,0 +1,16 @@
|
||||
/datum/martial_combo/cqc/slam
|
||||
name = "Slam"
|
||||
steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Slam opponent into the ground, knocking them down."
|
||||
combo_text_override = "Grab, switch hands, Harm"
|
||||
|
||||
/datum/martial_combo/cqc/slam/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.IsWeakened() && !target.resting && !target.lying)
|
||||
target.visible_message("<span class='warning'>[user] slams [target] into the ground!</span>", \
|
||||
"<span class='userdanger'>[user] slams you into the ground!</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/slam.ogg', 50, 1, -1)
|
||||
target.apply_damage(10, BRUTE)
|
||||
target.Weaken(6)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_FAIL
|
||||
@@ -0,0 +1,14 @@
|
||||
/datum/martial_combo/krav_maga/leg_sweep
|
||||
name = "Leg Sweep"
|
||||
explaination_text = "Trips the victim, rendering them prone and unable to move for a short time."
|
||||
|
||||
/datum/martial_combo/krav_maga/leg_sweep/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(target.stat || target.IsWeakened())
|
||||
return FALSE
|
||||
target.visible_message("<span class='warning'>[user] leg sweeps [target]!</span>", \
|
||||
"<span class='userdanger'>[user] leg sweeps you!</span>")
|
||||
playsound(get_turf(user), 'sound/effects/hit_kick.ogg', 50, 1, -1)
|
||||
target.apply_damage(5, BRUTE)
|
||||
target.Weaken(2)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Leg Sweep", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE_CLEAR_COMBOS
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/martial_combo/krav_maga/lung_punch
|
||||
name = "Lung Punch"
|
||||
explaination_text = "Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time."
|
||||
|
||||
/datum/martial_combo/krav_maga/lung_punch/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
target.visible_message("<span class='warning'>[user] pounds [target] on the chest!</span>", \
|
||||
"<span class='userdanger'>[user] slams your chest! You can't breathe!</span>")
|
||||
playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
target.AdjustLoseBreath(5)
|
||||
target.adjustOxyLoss(10)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Lung Punch", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE_CLEAR_COMBOS
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/martial_combo/krav_maga/neck_chop
|
||||
name = "Neck Chop"
|
||||
explaination_text = "Injures the neck, stopping the victim from speaking for a while."
|
||||
|
||||
/datum/martial_combo/krav_maga/neck_chop/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
target.visible_message("<span class='warning'>[user] karate chops [target]'s neck!</span>", \
|
||||
"<span class='userdanger'>[user] karate chops your neck, rendering you unable to speak for a short time!</span>")
|
||||
playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
target.apply_damage(5, BRUTE)
|
||||
target.AdjustSilence(10)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Neck Chop", ATKLOG_ALL)
|
||||
return MARTIAL_COMBO_DONE_CLEAR_COMBOS
|
||||
@@ -0,0 +1,36 @@
|
||||
/datum/martial_combo
|
||||
/// Name used to explain the combo
|
||||
var/name = "Code Fu"
|
||||
/// Which steps need to be performed
|
||||
var/list/steps
|
||||
/// What index to check
|
||||
var/current_step_index = 1
|
||||
/// Who is the target the combo is being executed on
|
||||
var/current_combo_target = null
|
||||
/// If you require to do the combo's on the same target
|
||||
var/combos_require_same_target = TRUE
|
||||
/// What does it do
|
||||
var/explaination_text = "Ability to break shit"
|
||||
/// How to do the combo. If null it'll auto generate it from the steps
|
||||
var/combo_text_override
|
||||
|
||||
/datum/martial_combo/proc/check_combo(step, mob/living/target)
|
||||
if(!combos_require_same_target || current_combo_target == null || current_combo_target == target)
|
||||
if(!length(steps) || step == steps[current_step_index])
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/martial_combo/proc/progress_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
current_combo_target = target
|
||||
if(current_step_index++ >= LAZYLEN(steps))
|
||||
return perform_combo(user, target, MA)
|
||||
return MARTIAL_COMBO_CONTINUE
|
||||
|
||||
/datum/martial_combo/proc/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
return MARTIAL_COMBO_FAIL // Override this
|
||||
|
||||
/datum/martial_combo/proc/give_explaination(user)
|
||||
var/final_combo_text = combo_text_override
|
||||
if(!final_combo_text)
|
||||
final_combo_text = english_list(steps, and_text = " ", comma_text = " ")
|
||||
to_chat(user, "<span class='notice'>[name]</span>: [final_combo_text]. [explaination_text]")
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/martial_combo/mimejutsu/mimechucks
|
||||
name = "Mimechucks"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Hits the opponent with invisible nunchucks."
|
||||
|
||||
/datum/martial_combo/mimejutsu/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat && !target.stunned && !target.IsWeakened())
|
||||
var/damage = rand(5, 8) + user.dna.species.punchdamagelow
|
||||
if(!damage)
|
||||
playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
target.visible_message("<span class='warning'>[user] swings invisible nunchcuks at [target]..and misses?</span>")
|
||||
return MARTIAL_COMBO_DONE
|
||||
|
||||
|
||||
var/obj/item/organ/external/affecting = target.get_organ(ran_zone(user.zone_selected))
|
||||
var/armor_block = target.run_armor_check(affecting, "melee")
|
||||
|
||||
target.visible_message("<span class='danger'>[user] has hit [target] with invisible nunchucks!</span>", \
|
||||
"<span class='userdanger'>[user] has hit [target] with a with invisible nunchuck!</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
target.apply_damage(damage, STAMINA, affecting, armor_block)
|
||||
add_attack_logs(user, target, "Melee attacked with [src] (mimechuck)")
|
||||
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,13 @@
|
||||
/datum/martial_combo/mimejutsu/silent_palm
|
||||
name = "Silent Palm"
|
||||
steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_DISARM)
|
||||
explaination_text = "Use mime energy to throw someone back."
|
||||
|
||||
/datum/martial_combo/mimejutsu/silent_palm/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat && !target.stunned && !target.IsWeakened())
|
||||
target.visible_message("<span class='danger'>[user] has barely touched [target] with [user.p_their()] palm!</span>", \
|
||||
"<span class='userdanger'>[user] hovers [user.p_their()] palm over your face!</span>")
|
||||
|
||||
var/atom/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(target, user)))
|
||||
target.throw_at(throw_target, 200, 4, user)
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,13 @@
|
||||
/datum/martial_combo/mimejutsu/smokebomb
|
||||
name = "Smokebomb"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM)
|
||||
explaination_text = "Drops a mime smokebomb."
|
||||
|
||||
/datum/martial_combo/mimejutsu/smokebomb/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
target.visible_message("<span class='danger'>[user] throws an invisible smoke bomb!!</span>")
|
||||
|
||||
var/datum/effect_system/smoke_spread/bad/smoke = new
|
||||
smoke.set_up(5, 0, target.loc)
|
||||
smoke.start()
|
||||
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,13 @@
|
||||
/datum/martial_combo/plasma_fist/plasma_fist
|
||||
name = "The Plasma Fist"
|
||||
steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Knocks the brain out of the opponent and gibs their body."
|
||||
|
||||
/datum/martial_combo/plasma_fist/plasma_fist/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
playsound(target.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
user.say("PLASMA FIST!")
|
||||
target.visible_message("<span class='danger'>[user] has hit [target] with THE PLASMA FIST TECHNIQUE!</span>", \
|
||||
"<span class='userdanger'>[user] has hit [target] with THE PLASMA FIST TECHNIQUE!</span>")
|
||||
target.gib()
|
||||
return MARTIAL_COMBO_DONE
|
||||
@@ -0,0 +1,13 @@
|
||||
/datum/martial_combo/plasma_fist/throwback
|
||||
name = "Throwback"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM)
|
||||
explaination_text = "Throws the target and an item at them."
|
||||
|
||||
/datum/martial_combo/plasma_fist/throwback/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
target.visible_message("<span class='danger'>[user] has hit [target] with Plasma Punch!</span>", \
|
||||
"<span class='userdanger'>[user] has hit [target] with Plasma Punch!</span>")
|
||||
playsound(target.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(target, user)))
|
||||
target.throw_at(throw_target, 200, 4, user)
|
||||
user.say("HYAH!")
|
||||
return MARTIAL_COMBO_DONE
|
||||
@@ -0,0 +1,20 @@
|
||||
/datum/martial_combo/plasma_fist/tornado_sweep
|
||||
name = "Tornado Sweep"
|
||||
steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM)
|
||||
explaination_text = "Repulses target and everyone back."
|
||||
|
||||
/datum/martial_combo/plasma_fist/tornado_sweep/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
user.say("TORNADO SWEEP!")
|
||||
INVOKE_ASYNC(src, .proc/do_tornado_effect, user)
|
||||
var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(1,user))
|
||||
turfs.Add(T)
|
||||
R.cast(turfs)
|
||||
return MARTIAL_COMBO_DONE
|
||||
|
||||
/datum/martial_combo/plasma_fist/tornado_sweep/proc/do_tornado_effect(mob/living/carbon/human/user)
|
||||
for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
|
||||
user.dir = i
|
||||
playsound(user.loc, 'sound/weapons/punch1.ogg', 15, 1, -1)
|
||||
sleep(1)
|
||||
@@ -0,0 +1,18 @@
|
||||
/datum/martial_combo/sleeping_carp/back_kick
|
||||
name = "Back Kick"
|
||||
steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_GRAB)
|
||||
explaination_text = "Opponent must be facing away. Knocks down."
|
||||
|
||||
/datum/martial_combo/sleeping_carp/back_kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(user.dir == target.dir && !target.stat && !target.IsWeakened())
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_KICK)
|
||||
target.visible_message("<span class='warning'>[user] kicks [target] in the back!</span>", \
|
||||
"<span class='userdanger'>[user] kicks you in the back, making you stumble and fall!</span>")
|
||||
step_to(target,get_step(target,target.dir),1)
|
||||
target.Weaken(4)
|
||||
playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Back Kick", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
user.say(pick("SURRPRIZU!","BACK STRIKE!","WOPAH!", "WATAAH", "ZOTA!", "Never turn your back to the enemy!"))
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,18 @@
|
||||
/datum/martial_combo/sleeping_carp/elbow_drop
|
||||
name = "Elbow Drop"
|
||||
steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition."
|
||||
|
||||
/datum/martial_combo/sleeping_carp/elbow_drop/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(target.IsWeakened() || target.resting || target.stat)
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
target.visible_message("<span class='warning'>[user] elbow drops [target]!</span>", \
|
||||
"<span class='userdanger'>[user] piledrives you with [user.p_their()] elbow!</span>")
|
||||
target.death() //FINISH HIM!
|
||||
target.apply_damage(50, BRUTE, "chest")
|
||||
playsound(get_turf(target), 'sound/weapons/punch1.ogg', 75, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Elbow Drop", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
user.say(pick("BANZAIII!", "KIYAAAA!", "OMAE WA MOU SHINDEIRU!", "YOU CAN'T SEE ME!", "MY TIME IS NOW!", "COWABUNGA"))
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/martial_combo/sleeping_carp/head_kick
|
||||
name = "Head Kick"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Decent damage, forces opponent to drop item in hand."
|
||||
|
||||
/datum/martial_combo/sleeping_carp/head_kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat && !target.IsWeakened())
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_KICK)
|
||||
target.visible_message("<span class='warning'>[user] kicks [target] in the head!</span>", \
|
||||
"<span class='userdanger'>[user] kicks you in the jaw!</span>")
|
||||
target.apply_damage(20, BRUTE, "head")
|
||||
target.drop_item()
|
||||
playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Head Kick", ATKLOG_ALL)
|
||||
if(prob(60))
|
||||
user.say(pick("OOHYOO!", "OOPYAH!", "HYOOAA!", "WOOAAA!", "SHURYUKICK!", "HIYAH!"))
|
||||
target.Stun(4)
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,20 @@
|
||||
/datum/martial_combo/sleeping_carp/stomach_knee
|
||||
name = "Stomach Knee"
|
||||
steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HARM)
|
||||
explaination_text = "Knocks the wind out of opponent and stuns."
|
||||
combo_text_override = "Grab, switch hands, Harm"
|
||||
|
||||
/datum/martial_combo/sleeping_carp/stomach_knee/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat && !target.IsWeakened())
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_KICK)
|
||||
target.visible_message("<span class='warning'>[user] knees [target] in the stomach!</span>", \
|
||||
"<span class='userdanger'>[user] winds you with a knee in the stomach!</span>")
|
||||
target.audible_message("<b>[target]</b> gags!")
|
||||
target.AdjustLoseBreath(3)
|
||||
target.Stun(2)
|
||||
playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Stomach Knee", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
user.say(pick("HWOP!", "KUH!", "YAKUUH!", "KYUH!", "KNEESTRIKE!"))
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -0,0 +1,20 @@
|
||||
/datum/martial_combo/sleeping_carp/wrist_wrench
|
||||
name = "Wrist Wrench"
|
||||
steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM)
|
||||
explaination_text = "Forces opponent to drop item in hand."
|
||||
|
||||
/datum/martial_combo/sleeping_carp/wrist_wrench/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA)
|
||||
if(!target.stat && !target.stunned && !target.IsWeakened())
|
||||
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
||||
target.visible_message("<span class='warning'>[user] grabs [target]'s wrist and wrenches it sideways!</span>", \
|
||||
"<span class='userdanger'>[user] grabs your wrist and violently wrenches it to the side!</span>")
|
||||
playsound(get_turf(user), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
add_attack_logs(user, target, "Melee attacked with martial-art [src] : Wrist Wrench", ATKLOG_ALL)
|
||||
if(prob(60))
|
||||
user.say(pick("WRISTY TWIRLY!", "WE FIGHT LIKE MEN!", "YOU DISHONOR YOURSELF!", "POHYAH!", "WHERE IS YOUR BATON NOW?", "SAY UNCLE!"))
|
||||
target.emote("scream")
|
||||
target.drop_item()
|
||||
target.apply_damage(5, BRUTE, pick("l_arm", "r_arm"))
|
||||
target.Stun(3)
|
||||
return MARTIAL_COMBO_DONE
|
||||
return MARTIAL_COMBO_DONE_BASIC_HIT
|
||||
@@ -1,152 +1,27 @@
|
||||
#define SLAM_COMBO "GH"
|
||||
#define KICK_COMBO "HH"
|
||||
#define RESTRAIN_COMBO "GG"
|
||||
#define PRESSURE_COMBO "DG"
|
||||
#define CONSECUTIVE_COMBO "DDH"
|
||||
|
||||
/datum/martial_art/cqc
|
||||
name = "CQC"
|
||||
help_verb = /mob/living/carbon/human/proc/CQC_help
|
||||
block_chance = 75
|
||||
var/just_a_cook = FALSE
|
||||
has_explaination_verb = TRUE
|
||||
combos = list(/datum/martial_combo/cqc/slam, /datum/martial_combo/cqc/kick, /datum/martial_combo/cqc/restrain, /datum/martial_combo/cqc/pressure, /datum/martial_combo/cqc/consecutive)
|
||||
var/restraining = FALSE //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
|
||||
var/static/list/areas_under_siege = typecacheof(list(/area/crew_quarters/kitchen,
|
||||
/area/crew_quarters/cafeteria,
|
||||
/area/crew_quarters/bar))
|
||||
|
||||
/datum/martial_art/cqc/under_siege
|
||||
name = "Close Quarters Cooking"
|
||||
just_a_cook = TRUE
|
||||
|
||||
/datum/martial_art/cqc/under_siege/can_use(mob/living/carbon/human/H)
|
||||
var/area/A = get_area(H)
|
||||
if(!(is_type_in_typecache(A, areas_under_siege)))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/martial_art/cqc/proc/drop_restraining()
|
||||
restraining = FALSE
|
||||
|
||||
/datum/martial_art/cqc/can_use(mob/living/carbon/human/H)
|
||||
var/area/A = get_area(H)
|
||||
if(just_a_cook && !(is_type_in_typecache(A, areas_under_siege)))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
if(findtext(streak, SLAM_COMBO))
|
||||
streak = ""
|
||||
Slam(A, D)
|
||||
return TRUE
|
||||
if(findtext(streak, KICK_COMBO))
|
||||
streak = ""
|
||||
Kick(A, D)
|
||||
return TRUE
|
||||
if(findtext(streak, RESTRAIN_COMBO))
|
||||
streak = ""
|
||||
Restrain(A, D)
|
||||
return TRUE
|
||||
if(findtext(streak, PRESSURE_COMBO))
|
||||
streak = ""
|
||||
Pressure(A, D)
|
||||
return TRUE
|
||||
if(findtext(streak, CONSECUTIVE_COMBO))
|
||||
streak = ""
|
||||
Consecutive(A, D)
|
||||
return FALSE
|
||||
|
||||
/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
if(!D.IsWeakened() && !D.resting && !D.lying)
|
||||
D.visible_message("<span class='warning'>[A] slams [D] into the ground!</span>", \
|
||||
"<span class='userdanger'>[A] slams you into the ground!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1)
|
||||
D.apply_damage(10, BRUTE)
|
||||
D.Weaken(6)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL)
|
||||
return TRUE
|
||||
streak = ""
|
||||
harm_act(A, D)
|
||||
streak = ""
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
var/success = FALSE
|
||||
if(!D.stat || !D.IsWeakened())
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] back!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you back!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(D, A.dir)
|
||||
D.throw_at(throw_target, 1, 14, A)
|
||||
D.apply_damage(10, BRUTE)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL)
|
||||
success = TRUE
|
||||
if(D.IsWeakened() && !D.stat)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D]'s head, knocking [D.p_them()] out!</span>", \
|
||||
"<span class='userdanger'>[A] kicks your head, knocking you out!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1)
|
||||
D.SetSleeping(15)
|
||||
D.adjustBrainLoss(15)
|
||||
add_attack_logs(A, D, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL)
|
||||
success = TRUE
|
||||
if(success)
|
||||
return TRUE
|
||||
streak = ""
|
||||
harm_act(A, D)
|
||||
streak = ""
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
D.visible_message("<span class='warning'>[A] forces their arm on [D]'s neck!</span>")
|
||||
D.adjustStaminaLoss(60)
|
||||
playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL)
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(restraining)
|
||||
return
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
if(!D.stat)
|
||||
D.visible_message("<span class='warning'>[A] locks [D] into a restraining position!</span>", \
|
||||
"<span class='userdanger'>[A] locks you into a restraining position!</span>")
|
||||
D.adjustStaminaLoss(20)
|
||||
D.Stun(5)
|
||||
restraining = TRUE
|
||||
addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL)
|
||||
return TRUE
|
||||
streak = ""
|
||||
harm_act(A, D)
|
||||
streak = ""
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
if(!D.stat)
|
||||
D.visible_message("<span class='warning'>[A] strikes [D]'s abdomen, neck and back consecutively</span>", \
|
||||
"<span class='userdanger'>[A] strikes your abdomen, neck and back consecutively!</span>")
|
||||
playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
|
||||
var/obj/item/I = D.get_active_hand()
|
||||
if(I && D.drop_item())
|
||||
A.put_in_hands(I)
|
||||
D.adjustStaminaLoss(50)
|
||||
D.apply_damage(25, BRUTE)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL)
|
||||
return TRUE
|
||||
streak = ""
|
||||
harm_act(A, D)
|
||||
streak = ""
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
add_to_streak("G", D)
|
||||
if(check_streak(A, D))
|
||||
return TRUE
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
var/obj/item/grab/G = D.grabbedby(A, 1)
|
||||
if(G)
|
||||
G.state = GRAB_AGGRESSIVE //Instant aggressive grab
|
||||
@@ -155,11 +30,7 @@
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
add_to_streak("H", D)
|
||||
if(check_streak(A, D))
|
||||
return TRUE
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL)
|
||||
A.do_attack_animation(D)
|
||||
var/picked_hit_type = pick("CQC'd", "neck chopped", "gut punched", "Big Bossed")
|
||||
@@ -185,12 +56,7 @@
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!can_use(A))
|
||||
return FALSE
|
||||
add_to_streak("D", D)
|
||||
var/obj/item/I = null
|
||||
if(check_streak(A, D))
|
||||
return TRUE
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
var/obj/item/grab/G = A.get_inactive_hand()
|
||||
if(restraining && istype(G) && G.affecting == D)
|
||||
D.visible_message("<span class='danger'>[A] puts [D] into a chokehold!</span>", \
|
||||
@@ -203,6 +69,8 @@
|
||||
else
|
||||
restraining = FALSE
|
||||
|
||||
var/obj/item/I = null
|
||||
|
||||
if(prob(65))
|
||||
if(!D.stat || !D.IsWeakened() || !restraining)
|
||||
I = D.get_active_hand()
|
||||
@@ -220,16 +88,8 @@
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Disarmed [I ? " grabbing \the [I]" : ""]", ATKLOG_ALL)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/proc/CQC_help()
|
||||
set name = "Remember The Basics"
|
||||
set desc = "You try to remember some of the basics of CQC."
|
||||
set category = "CQC"
|
||||
to_chat(usr, "<b><i>You try to remember some of the basics of CQC.</i></b>")
|
||||
/datum/martial_art/cqc/explaination_header(user)
|
||||
to_chat(user, "<b><i>You try to remember some of the basics of CQC.</i></b>")
|
||||
|
||||
to_chat(usr, "<span class='notice'>Slam</span>: Grab, switch hands, Harm. Slam opponent into the ground, knocking them down.")
|
||||
to_chat(usr, "<span class='notice'>CQC Kick</span>: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
|
||||
to_chat(usr, "<span class='notice'>Restrain</span>: Grab, switch hands, Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.")
|
||||
to_chat(usr, "<span class='notice'>Pressure</span>: Disarm Grab. Decent stamina damage.")
|
||||
to_chat(usr, "<span class='notice'>Consecutive CQC</span>: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
|
||||
|
||||
to_chat(usr, "<b><i>In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.</i></b>")
|
||||
/datum/martial_art/cqc/explaination_footer(user)
|
||||
to_chat(user, "<b><i>In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.</i></b>")
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
to_chat(owner, "<b><i>Your next attack will be a Neck Chop.</i></b>")
|
||||
owner.visible_message("<span class='danger'>[owner] assumes the Neck Chop stance!</span>")
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.martial_art.streak = "neck_chop"
|
||||
H.mind.martial_art.combos.Cut()
|
||||
H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/neck_chop)
|
||||
H.mind.martial_art.reset_combos()
|
||||
|
||||
/datum/action/leg_sweep
|
||||
name = "Leg Sweep - Trips the victim, rendering them prone and unable to move for a short time."
|
||||
@@ -28,7 +30,9 @@
|
||||
to_chat(owner, "<b><i>Your next attack will be a Leg Sweep.</i></b>")
|
||||
owner.visible_message("<span class='danger'>[owner] assumes the Leg Sweep stance!</span>")
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.martial_art.streak = "leg_sweep"
|
||||
H.mind.martial_art.combos.Cut()
|
||||
H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/leg_sweep)
|
||||
H.mind.martial_art.reset_combos()
|
||||
|
||||
/datum/action/lung_punch//referred to internally as 'quick choke'
|
||||
name = "Lung Punch - Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time."
|
||||
@@ -41,7 +45,9 @@
|
||||
to_chat(owner, "<b><i>Your next attack will be a Lung Punch.</i></b>")
|
||||
owner.visible_message("<span class='danger'>[owner] assumes the Lung Punch stance!</span>")
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.martial_art.streak = "quick_choke"//internal name for lung punch
|
||||
H.mind.martial_art.combos.Cut()
|
||||
H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/lung_punch)
|
||||
H.mind.martial_art.reset_combos()
|
||||
|
||||
/datum/martial_art/krav_maga/teach(var/mob/living/carbon/human/H,var/make_temporary=0)
|
||||
..()
|
||||
@@ -58,59 +64,8 @@
|
||||
legsweep.Remove(H)
|
||||
lungpunch.Remove(H)
|
||||
|
||||
/datum/martial_art/krav_maga/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
switch(streak)
|
||||
if("neck_chop")
|
||||
streak = ""
|
||||
neck_chop(A,D)
|
||||
return 1
|
||||
if("leg_sweep")
|
||||
streak = ""
|
||||
leg_sweep(A,D)
|
||||
return 1
|
||||
if("quick_choke")//is actually lung punch
|
||||
streak = ""
|
||||
quick_choke(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/krav_maga/proc/leg_sweep(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(D.stat || D.IsWeakened())
|
||||
return 0
|
||||
D.visible_message("<span class='warning'>[A] leg sweeps [D]!</span>", \
|
||||
"<span class='userdanger'>[A] leg sweeps you!</span>")
|
||||
playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1)
|
||||
D.apply_damage(5, BRUTE)
|
||||
D.Weaken(2)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Leg Sweep", ATKLOG_ALL)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/krav_maga/proc/quick_choke(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)//is actually lung punch
|
||||
D.visible_message("<span class='warning'>[A] pounds [D] on the chest!</span>", \
|
||||
"<span class='userdanger'>[A] slams your chest! You can't breathe!</span>")
|
||||
playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
D.AdjustLoseBreath(5)
|
||||
D.adjustOxyLoss(10)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Lung Punch", ATKLOG_ALL)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/krav_maga/proc/neck_chop(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
D.visible_message("<span class='warning'>[A] karate chops [D]'s neck!</span>", \
|
||||
"<span class='userdanger'>[A] karate chops your neck, rendering you unable to speak for a short time!</span>")
|
||||
playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
D.apply_damage(5, BRUTE)
|
||||
D.AdjustSilence(10)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Neck Chop", ATKLOG_ALL)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/krav_maga/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
..()
|
||||
|
||||
/datum/martial_art/krav_maga/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
add_attack_logs(A, D, "Melee attacked with [src]")
|
||||
var/picked_hit_type = pick("punches", "kicks")
|
||||
var/bonus_damage = 10
|
||||
@@ -126,11 +81,10 @@
|
||||
playsound(get_turf(D), 'sound/effects/hit_punch.ogg', 50, 1, -1)
|
||||
D.visible_message("<span class='danger'>[A] [picked_hit_type] [D]!</span>", \
|
||||
"<span class='userdanger'>[A] [picked_hit_type] you!</span>")
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/krav_maga/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
if(prob(60))
|
||||
if(D.hand)
|
||||
if(istype(D.l_hand, /obj/item))
|
||||
@@ -149,7 +103,7 @@
|
||||
D.visible_message("<span class='danger'>[A] attempted to disarm [D]!</span>", \
|
||||
"<span class='userdanger'>[A] attempted to disarm [D]!</span>")
|
||||
playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
//Krav Maga Gloves
|
||||
|
||||
|
||||
@@ -1,42 +1,87 @@
|
||||
#define HAS_COMBOS LAZYLEN(combos)
|
||||
#define COMBO_ALIVE_TIME 5 SECONDS // How long the combo stays alive when no new attack is done
|
||||
|
||||
/datum/martial_art
|
||||
var/name = "Martial Art"
|
||||
var/streak = ""
|
||||
var/max_streak_length = 6
|
||||
var/current_target = null
|
||||
var/temporary = 0
|
||||
var/temporary = FALSE
|
||||
var/datum/martial_art/base = null // The permanent style
|
||||
var/deflection_chance = 0 //Chance to deflect projectiles
|
||||
var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
|
||||
var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
|
||||
var/help_verb = null
|
||||
var/no_guns = FALSE //set to TRUE to prevent users of this style from using guns (sleeping carp, highlander). They can still pick them up, but not fire them.
|
||||
var/no_guns_message = "" //message to tell the style user if they try and use a gun while no_guns = TRUE (DISHONORABRU!)
|
||||
|
||||
/datum/martial_art/proc/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
return 0
|
||||
var/has_explaination_verb = FALSE // If the martial art has it's own explaination verb
|
||||
|
||||
/datum/martial_art/proc/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
return 0
|
||||
var/list/combos = list() // What combos can the user do? List of combo types
|
||||
var/list/datum/martial_art/current_combos = list() // What combos are currently (possibly) being performed
|
||||
var/last_hit = 0 // When the last hit happened
|
||||
|
||||
/datum/martial_art/proc/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
return 0
|
||||
/datum/martial_art/New()
|
||||
. = ..()
|
||||
reset_combos()
|
||||
|
||||
/datum/martial_art/proc/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
return 0
|
||||
/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
return act(MARTIAL_COMBO_STEP_DISARM, A, D)
|
||||
|
||||
/datum/martial_art/proc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
return act(MARTIAL_COMBO_STEP_HARM, A, D)
|
||||
|
||||
/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
return act(MARTIAL_COMBO_STEP_GRAB, A, D)
|
||||
|
||||
/datum/martial_art/proc/help_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
return act(MARTIAL_COMBO_STEP_HELP, A, D)
|
||||
|
||||
/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/proc/add_to_streak(var/element,var/mob/living/carbon/human/D)
|
||||
if(D != current_target)
|
||||
current_target = D
|
||||
streak = ""
|
||||
streak = streak+element
|
||||
if(length(streak) > max_streak_length)
|
||||
streak = copytext(streak,2)
|
||||
return
|
||||
/datum/martial_art/proc/act(step, mob/living/carbon/human/user, mob/living/carbon/human/target)
|
||||
if(!can_use(user))
|
||||
return MARTIAL_ARTS_CANNOT_USE
|
||||
if(last_hit + COMBO_ALIVE_TIME < world.time)
|
||||
reset_combos()
|
||||
last_hit = world.time
|
||||
|
||||
/datum/martial_art/proc/basic_hit(var/mob/living/carbon/human/A,var/mob/living/carbon/human/D)
|
||||
if(HAS_COMBOS)
|
||||
return check_combos(step, user, target)
|
||||
return FALSE
|
||||
|
||||
/datum/martial_art/proc/reset_combos()
|
||||
current_combos.Cut()
|
||||
for(var/combo_type in combos)
|
||||
current_combos.Add(new combo_type())
|
||||
|
||||
/datum/martial_art/proc/check_combos(step, mob/living/carbon/human/user, mob/living/carbon/human/target)
|
||||
. = FALSE
|
||||
for(var/thing in current_combos)
|
||||
var/datum/martial_combo/MC = thing
|
||||
if(!MC.check_combo(step, target))
|
||||
current_combos -= MC // It failed so remove it
|
||||
else
|
||||
switch(MC.progress_combo(user, target, src))
|
||||
if(MARTIAL_COMBO_FAIL)
|
||||
current_combos -= MC
|
||||
if(MARTIAL_COMBO_DONE_NO_CLEAR)
|
||||
. = TRUE
|
||||
current_combos -= MC
|
||||
if(MARTIAL_COMBO_DONE)
|
||||
reset_combos()
|
||||
return TRUE
|
||||
if(MARTIAL_COMBO_DONE_BASIC_HIT)
|
||||
basic_hit(user, target)
|
||||
reset_combos()
|
||||
return TRUE
|
||||
if(MARTIAL_COMBO_DONE_CLEAR_COMBOS)
|
||||
combos.Cut()
|
||||
reset_combos()
|
||||
return TRUE
|
||||
if(!LAZYLEN(current_combos))
|
||||
reset_combos()
|
||||
|
||||
/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
|
||||
var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)
|
||||
var/datum/unarmed_attack/attack = A.dna.species.unarmed
|
||||
@@ -54,7 +99,7 @@
|
||||
if(!damage)
|
||||
playsound(D.loc, attack.miss_sound, 25, 1, -1)
|
||||
D.visible_message("<span class='warning'>[A] has attempted to [atk_verb] [D]!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
var/obj/item/organ/external/affecting = D.get_organ(ran_zone(A.zone_selected))
|
||||
var/armor_block = D.run_armor_check(affecting, "melee")
|
||||
@@ -74,26 +119,60 @@
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
else if(D.lying)
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/proc/teach(var/mob/living/carbon/human/H,var/make_temporary=0)
|
||||
if(help_verb)
|
||||
H.verbs += help_verb
|
||||
/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
|
||||
if(!H.mind)
|
||||
return
|
||||
if(has_explaination_verb)
|
||||
H.verbs |= /mob/living/carbon/human/proc/martial_arts_help
|
||||
if(make_temporary)
|
||||
temporary = 1
|
||||
temporary = TRUE
|
||||
if(temporary)
|
||||
if(H.martial_art)
|
||||
base = H.martial_art.base
|
||||
if(H.mind.martial_art)
|
||||
base = H.mind.martial_art.base
|
||||
else
|
||||
base = src
|
||||
H.martial_art = src
|
||||
H.mind.martial_art = src
|
||||
|
||||
/datum/martial_art/proc/remove(var/mob/living/carbon/human/H)
|
||||
if(H.martial_art != src)
|
||||
if(!H.mind)
|
||||
return
|
||||
H.martial_art = base
|
||||
if(help_verb)
|
||||
H.verbs -= help_verb
|
||||
if(H.mind.martial_art != src)
|
||||
return
|
||||
H.mind.martial_art = base
|
||||
if(has_explaination_verb && !(base && base.has_explaination_verb))
|
||||
H.verbs -= /mob/living/carbon/human/proc/martial_arts_help
|
||||
|
||||
/mob/living/carbon/human/proc/martial_arts_help()
|
||||
set name = "Show Info"
|
||||
set desc = "Gives information about the martial arts you know."
|
||||
set category = "Martial Arts"
|
||||
var/mob/living/carbon/human/H = usr
|
||||
if(!istype(H))
|
||||
to_chat(usr, "<span class='warning'>You shouldn't have access to this verb. Report this as a bug to the github please.</span>")
|
||||
return
|
||||
H.mind.martial_art.give_explaination(H)
|
||||
|
||||
/datum/martial_art/proc/give_explaination(user = usr)
|
||||
explaination_header(user)
|
||||
explaination_combos(user)
|
||||
explaination_footer(user)
|
||||
|
||||
// Put after the header and before the footer in the explaination text
|
||||
/datum/martial_art/proc/explaination_combos(user)
|
||||
if(HAS_COMBOS)
|
||||
for(var/combo_type in combos)
|
||||
var/datum/martial_combo/MC = new combo_type()
|
||||
MC.give_explaination(user)
|
||||
|
||||
// Put on top of the explaination text
|
||||
/datum/martial_art/proc/explaination_header(user)
|
||||
return
|
||||
|
||||
// Put below the combos in the explaination text
|
||||
/datum/martial_art/proc/explaination_footer(user)
|
||||
return
|
||||
|
||||
//ITEMS
|
||||
|
||||
@@ -283,3 +362,6 @@
|
||||
if(wielded)
|
||||
return ..()
|
||||
return 0
|
||||
|
||||
#undef HAS_COMBOS
|
||||
#undef COMBO_ALIVE_TIME
|
||||
|
||||
@@ -1,90 +1,16 @@
|
||||
#define MIMECHUCKS_COMBO "DH"
|
||||
#define MIMESMOKE_COMBO "DD"
|
||||
#define MIMEPALM_COMBO "GD"
|
||||
|
||||
/datum/martial_art/mimejutsu
|
||||
name = "Mimejutsu"
|
||||
help_verb = /mob/living/carbon/human/proc/mimejutsu_help
|
||||
|
||||
/datum/martial_art/mimejutsu/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(findtext(streak,MIMECHUCKS_COMBO))
|
||||
streak = ""
|
||||
mimeChuck(A,D)
|
||||
return 1
|
||||
if(findtext(streak,MIMESMOKE_COMBO))
|
||||
streak = ""
|
||||
mimeSmoke(A,D)
|
||||
return 1
|
||||
if(findtext(streak,MIMEPALM_COMBO))
|
||||
streak = ""
|
||||
mimePalm(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/mimejutsu/proc/mimeChuck(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.stunned && !D.IsWeakened())
|
||||
var/damage = rand(5, 8) + A.dna.species.punchdamagelow
|
||||
if(!damage)
|
||||
playsound(D.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
D.visible_message("<span class='warning'>[A] swings invisible nunchcuks at [D]..and misses?</span>")
|
||||
return 0
|
||||
|
||||
|
||||
var/obj/item/organ/external/affecting = D.get_organ(ran_zone(A.zone_selected))
|
||||
var/armor_block = D.run_armor_check(affecting, "melee")
|
||||
|
||||
D.visible_message("<span class='danger'>[A] has hit [D] with invisible nunchucks!</span>", \
|
||||
"<span class='userdanger'>[A] has hit [D] with a with invisible nunchuck!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
D.apply_damage(damage, STAMINA, affecting, armor_block)
|
||||
add_attack_logs(A, D, "Melee attacked with [src] (mimechuck)")
|
||||
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/mimejutsu/proc/mimeSmoke(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
|
||||
D.visible_message("<span class='danger'>[A] throws an invisible smoke bomb!!</span>")
|
||||
|
||||
var/datum/effect_system/smoke_spread/bad/smoke = new
|
||||
smoke.set_up(5, 0, D.loc)
|
||||
smoke.start()
|
||||
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/mimejutsu/proc/mimePalm(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.stunned && !D.IsWeakened())
|
||||
D.visible_message("<span class='danger'>[A] has barely touched [D] with [A.p_their()] palm!</span>", \
|
||||
"<span class='userdanger'>[A] hovers [A.p_their()] palm over your face!</span>")
|
||||
|
||||
var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A)))
|
||||
D.throw_at(throw_target, 200, 4,A)
|
||||
return basic_hit(A,D)
|
||||
|
||||
|
||||
/datum/martial_art/mimejutsu/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
|
||||
return ..()
|
||||
has_explaination_verb = TRUE
|
||||
combos = list(/datum/martial_combo/mimejutsu/mimechucks, /datum/martial_combo/mimejutsu/smokebomb, /datum/martial_combo/mimejutsu/silent_palm)
|
||||
|
||||
/datum/martial_art/mimejutsu/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/mimejutsu/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
A.do_attack_animation(D)
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/item/mimejutsu_scroll
|
||||
name = "Mimejutsu 'scroll'"
|
||||
@@ -106,13 +32,5 @@
|
||||
name = "beret with staple"
|
||||
icon_state = "beret"
|
||||
|
||||
/mob/living/carbon/human/proc/mimejutsu_help()
|
||||
set name = "Recall Ancient Mimeing"
|
||||
set desc = "Remember the martial techniques of Mimejutsu."
|
||||
set category = "Mimejutsu"
|
||||
|
||||
to_chat(usr, "<b><i>You make a invisible box around yourself and recall the teachings of Mimejutsu...</i></b>")
|
||||
|
||||
to_chat(usr, "<span class='notice'>Mimechucks</span>: Disarm Harm. Hits the opponent with invisible nunchucks.")
|
||||
to_chat(usr, "<span class='notice'>Smokebomb</span>: Disarm Disarm. Drops a mime smokebomb.")
|
||||
to_chat(usr, "<span class='notice'>Silent Palm</span>: Grab Disarm. Using mime energy throw someone back.")
|
||||
/datum/martial_art/mimejutsu/explaination_header(user)
|
||||
to_chat(user, "<b><i>You make a invisible box around yourself and recall the teachings of Mimejutsu...</i></b>")
|
||||
|
||||
@@ -1,86 +1,22 @@
|
||||
#define TORNADO_COMBO "HHD"
|
||||
#define THROWBACK_COMBO "DHD"
|
||||
#define PLASMA_COMBO "HDDDH"
|
||||
|
||||
/datum/martial_art/plasma_fist
|
||||
name = "Plasma Fist"
|
||||
help_verb = /mob/living/carbon/human/proc/plasma_fist_help
|
||||
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(findtext(streak,TORNADO_COMBO))
|
||||
streak = ""
|
||||
Tornado(A,D)
|
||||
return 1
|
||||
if(findtext(streak,THROWBACK_COMBO))
|
||||
streak = ""
|
||||
Throwback(A,D)
|
||||
return 1
|
||||
if(findtext(streak,PLASMA_COMBO))
|
||||
streak = ""
|
||||
Plasma(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Tornado(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
A.say("TORNADO SWEEP!")
|
||||
spawn(0)
|
||||
for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
|
||||
A.dir = i
|
||||
playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1)
|
||||
sleep(1)
|
||||
var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(1,A))
|
||||
turfs.Add(T)
|
||||
R.cast(turfs)
|
||||
return
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Throwback(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
D.visible_message("<span class='danger'>[A] has hit [D] with Plasma Punch!</span>", \
|
||||
"<span class='userdanger'>[A] has hit [D] with Plasma Punch!</span>")
|
||||
playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A)))
|
||||
D.throw_at(throw_target, 200, 4,A)
|
||||
A.say("HYAH!")
|
||||
return
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Plasma(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
A.say("PLASMA FIST!")
|
||||
D.visible_message("<span class='danger'>[A] has hit [D] with THE PLASMA FIST TECHNIQUE!</span>", \
|
||||
"<span class='userdanger'>[A] has hit [D] with THE PLASMA FIST TECHNIQUE!</span>")
|
||||
D.gib()
|
||||
return
|
||||
combos = list(/datum/martial_combo/plasma_fist/tornado_sweep, /datum/martial_combo/plasma_fist/throwback, /datum/martial_combo/plasma_fist/plasma_fist)
|
||||
has_explaination_verb = TRUE
|
||||
|
||||
/datum/martial_art/plasma_fist/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/plasma_fist/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/plasma_fist/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/proc/plasma_fist_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember the martial techniques of the Plasma Fist."
|
||||
set category = "Plasma Fist"
|
||||
|
||||
to_chat(usr, "<b><i>You clench your fists and have a flashback of knowledge...</i></b>")
|
||||
to_chat(usr, "<span class='notice'>Tornado Sweep</span>: Harm Harm Disarm. Repulses target and everyone back.")
|
||||
to_chat(usr, "<span class='notice'>Throwback</span>: Disarm Harm Disarm. Throws the target and an item at them.")
|
||||
to_chat(usr, "<span class='notice'>The Plasma Fist</span>: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.")
|
||||
/datum/martial_art/plasma_fist/explaination_header(user)
|
||||
to_chat(user, "<b><i>You clench your fists and have a flashback of knowledge...</i></b>")
|
||||
|
||||
@@ -1,126 +1,21 @@
|
||||
//Used by the gang of the same name. Uses combos. Basic attacks bypass armor and never miss
|
||||
#define WRIST_WRENCH_COMBO "DD"
|
||||
#define BACK_KICK_COMBO "HG"
|
||||
#define STOMACH_KNEE_COMBO "GH"
|
||||
#define HEAD_KICK_COMBO "DHH"
|
||||
#define ELBOW_DROP_COMBO "HDHDH"
|
||||
/datum/martial_art/the_sleeping_carp
|
||||
name = "The Sleeping Carp"
|
||||
deflection_chance = 100
|
||||
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
|
||||
no_guns = TRUE
|
||||
no_guns_message = "Use of ranged weaponry would bring dishonor to the clan."
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(findtext(streak,WRIST_WRENCH_COMBO))
|
||||
streak = ""
|
||||
wristWrench(A,D)
|
||||
return 1
|
||||
if(findtext(streak,BACK_KICK_COMBO))
|
||||
streak = ""
|
||||
backKick(A,D)
|
||||
return 1
|
||||
if(findtext(streak,STOMACH_KNEE_COMBO))
|
||||
streak = ""
|
||||
kneeStomach(A,D)
|
||||
return 1
|
||||
if(findtext(streak,HEAD_KICK_COMBO))
|
||||
streak = ""
|
||||
headKick(A,D)
|
||||
return 1
|
||||
if(findtext(streak,ELBOW_DROP_COMBO))
|
||||
streak = ""
|
||||
elbowDrop(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/wristWrench(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.stunned && !D.IsWeakened())
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
D.visible_message("<span class='warning'>[A] grabs [D]'s wrist and wrenches it sideways!</span>", \
|
||||
"<span class='userdanger'>[A] grabs your wrist and violently wrenches it to the side!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Wrist Wrench", ATKLOG_ALL)
|
||||
if(prob(60))
|
||||
A.say(pick("WRISTY TWIRLY!", "WE FIGHT LIKE MEN!", "YOU DISHONOR YOURSELF!", "POHYAH!", "WHERE IS YOUR BATON NOW?", "SAY UNCLE!"))
|
||||
D.emote("scream")
|
||||
D.drop_item()
|
||||
D.apply_damage(5, BRUTE, pick("l_arm", "r_arm"))
|
||||
D.Stun(3)
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/backKick(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(A.dir == D.dir && !D.stat && !D.IsWeakened())
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] in the back!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you in the back, making you stumble and fall!</span>")
|
||||
step_to(D,get_step(D,D.dir),1)
|
||||
D.Weaken(4)
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Back Kick", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
A.say(pick("SURRPRIZU!","BACK STRIKE!","WOPAH!", "WATAAH", "ZOTA!", "Never turn your back to the enemy!"))
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/kneeStomach(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.IsWeakened())
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
|
||||
D.visible_message("<span class='warning'>[A] knees [D] in the stomach!</span>", \
|
||||
"<span class='userdanger'>[A] winds you with a knee in the stomach!</span>")
|
||||
D.audible_message("<b>[D]</b> gags!")
|
||||
D.AdjustLoseBreath(3)
|
||||
D.Stun(2)
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Stomach Knee", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
A.say(pick("HWOP!", "KUH!", "YAKUUH!", "KYUH!", "KNEESTRIKE!"))
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/headKick(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.IsWeakened())
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] in the head!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you in the jaw!</span>")
|
||||
D.apply_damage(20, BRUTE, "head")
|
||||
D.drop_item()
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Head Kick", ATKLOG_ALL)
|
||||
if(prob(60))
|
||||
A.say(pick("OOHYOO!", "OOPYAH!", "HYOOAA!", "WOOAAA!", "SHURYUKICK!", "HIYAH!"))
|
||||
D.Stun(4)
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/elbowDrop(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
if(D.IsWeakened() || D.resting || D.stat)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
D.visible_message("<span class='warning'>[A] elbow drops [D]!</span>", \
|
||||
"<span class='userdanger'>[A] piledrives you with [A.p_their()] elbow!</span>")
|
||||
if(D.stat)
|
||||
D.death() //FINISH HIM!
|
||||
D.apply_damage(50, BRUTE, "chest")
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
|
||||
add_attack_logs(A, D, "Melee attacked with martial-art [src] : Elbow Drop", ATKLOG_ALL)
|
||||
if(prob(80))
|
||||
A.say(pick("BANZAIII!", "KIYAAAA!", "OMAE WA MOU SHINDEIRU!", "YOU CAN'T SEE ME!", "MY TIME IS NOW!", "COWABUNGA"))
|
||||
return 1
|
||||
return basic_hit(A,D)
|
||||
has_explaination_verb = TRUE
|
||||
combos = list(/datum/martial_combo/sleeping_carp/wrist_wrench, /datum/martial_combo/sleeping_carp/back_kick, /datum/martial_combo/sleeping_carp/stomach_knee, /datum/martial_combo/sleeping_carp/head_kick, /datum/martial_combo/sleeping_carp/elbow_drop)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
var/obj/item/grab/G = D.grabbedby(A,1)
|
||||
if(G)
|
||||
G.state = GRAB_AGGRESSIVE //Instant aggressive grab
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
MARTIAL_ARTS_ACT_CHECK
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
|
||||
D.visible_message("<span class='danger'>[A] [atk_verb] [D]!</span>", \
|
||||
@@ -132,24 +27,7 @@
|
||||
if(prob(D.getBruteLoss()) && !D.lying)
|
||||
D.visible_message("<span class='warning'>[D] stumbles and falls!</span>", "<span class='userdanger'>The blow sends you to the ground!</span>")
|
||||
D.Weaken(4)
|
||||
return 1
|
||||
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/proc/sleeping_carp_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember the martial techniques of the Sleeping Carp clan."
|
||||
set category = "Sleeping Carp"
|
||||
return TRUE
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/explaination_header(user)
|
||||
to_chat(usr, "<b><i>You retreat inward and recall the teachings of the Sleeping Carp...</i></b>")
|
||||
|
||||
to_chat(usr, "<span class='notice'>Wrist Wrench</span>: Disarm Disarm. Forces opponent to drop item in hand.")
|
||||
to_chat(usr, "<span class='notice'>Back Kick</span>: Harm Grab. Opponent must be facing away. Knocks down.")
|
||||
to_chat(usr, "<span class='notice'>Stomach Knee</span>: Grab Harm. Knocks the wind out of opponent and stuns.")
|
||||
to_chat(usr, "<span class='notice'>Head Kick</span>: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.")
|
||||
to_chat(usr, "<span class='notice'>Elbow Drop</span>: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.")
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
create_reagents(330)
|
||||
|
||||
martial_art = GLOB.default_martial_art
|
||||
|
||||
handcrafting = new()
|
||||
|
||||
// Set up DNA.
|
||||
@@ -298,8 +296,8 @@
|
||||
apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee"))
|
||||
|
||||
/mob/living/carbon/human/bullet_act()
|
||||
if(martial_art && martial_art.deflection_chance) //Some martial arts users can deflect projectiles!
|
||||
if(!prob(martial_art.deflection_chance))
|
||||
if(mind && mind.martial_art && mind.martial_art.deflection_chance) //Some martial arts users can deflect projectiles!
|
||||
if(!prob(mind.martial_art.deflection_chance))
|
||||
return ..()
|
||||
if(!src.lying && !(HULK in mutations)) //But only if they're not lying down, and hulks can't do it
|
||||
visible_message("<span class='danger'>[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
|
||||
@@ -1731,16 +1729,14 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
|
||||
if(G.trigger_guard == TRIGGER_GUARD_NORMAL)
|
||||
if(HULK in mutations)
|
||||
to_chat(src, "<span class='warning'>Your meaty finger is much too large for the trigger guard!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
if(NOGUNS in dna.species.species_traits)
|
||||
to_chat(src, "<span class='warning'>Your fingers don't fit in the trigger guard!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(martial_art && martial_art.no_guns) //great dishonor to famiry
|
||||
to_chat(src, "<span class='warning'>[martial_art.no_guns_message]</span>")
|
||||
return 0
|
||||
|
||||
return .
|
||||
if(mind && mind.martial_art && mind.martial_art.no_guns) //great dishonor to famiry
|
||||
to_chat(src, "<span class='warning'>[mind.martial_art.no_guns_message]</span>")
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/proc/change_icobase(var/new_icobase, var/new_deform, var/owner_sensitive)
|
||||
for(var/obj/item/organ/external/O in bodyparts)
|
||||
|
||||
@@ -214,7 +214,7 @@ emp_act
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/proc/check_block()
|
||||
if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
|
||||
if(mind && mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit) //todo: update this to utilize check_obscured_slots() //and make sure it's check_obscured_slots(TRUE) to stop aciding through visors etc
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new())
|
||||
/mob/living/carbon/human
|
||||
|
||||
hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD)
|
||||
@@ -42,8 +41,6 @@ GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new())
|
||||
|
||||
var/datum/personal_crafting/handcrafting
|
||||
|
||||
var/datum/martial_art/martial_art = null
|
||||
|
||||
var/special_voice = "" // For changing our voice. Used by a symptom.
|
||||
|
||||
var/hand_blood_color
|
||||
|
||||
@@ -696,13 +696,14 @@
|
||||
|
||||
if(alcohol_strength >= slur_start) //slurring
|
||||
Slur(drunk)
|
||||
if(alcohol_strength >= brawl_start) //the drunken martial art
|
||||
if(!istype(martial_art, /datum/martial_art/drunk_brawling))
|
||||
var/datum/martial_art/drunk_brawling/F = new
|
||||
F.teach(src, 1)
|
||||
if(alcohol_strength < brawl_start) //removing the art
|
||||
if(istype(martial_art, /datum/martial_art/drunk_brawling))
|
||||
martial_art.remove(src)
|
||||
if(mind)
|
||||
if(alcohol_strength >= brawl_start) //the drunken martial art
|
||||
if(!istype(mind.martial_art, /datum/martial_art/drunk_brawling))
|
||||
var/datum/martial_art/drunk_brawling/F = new
|
||||
F.teach(src, TRUE)
|
||||
else if(alcohol_strength < brawl_start) //removing the art
|
||||
if(istype(mind.martial_art, /datum/martial_art/drunk_brawling))
|
||||
mind.martial_art.remove(src)
|
||||
if(alcohol_strength >= confused_start && prob(33)) //confused walking
|
||||
if(!confused)
|
||||
Confused(1)
|
||||
|
||||
@@ -516,7 +516,7 @@
|
||||
playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
target.visible_message("<span class='danger'>[user] attempted to disarm [target]!</span>")
|
||||
|
||||
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) //Handles any species-specific attackhand events.
|
||||
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) //Handles any species-specific attackhand events.
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
@@ -528,6 +528,9 @@
|
||||
to_chat(M, "<span class='warning'>You can't use your hand.</span>")
|
||||
return
|
||||
|
||||
if(M.mind)
|
||||
attacker_style = M.mind.martial_art
|
||||
|
||||
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
|
||||
add_attack_logs(M, H, "Melee attacked with fists (miss/block)")
|
||||
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>")
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
var/list/default_genes = list(REGEN, BREATHLESS, COLDRES)
|
||||
var/list/default_spells = list()
|
||||
var/activated = FALSE //for wishgranters to not give an option if someone already has it.
|
||||
|
||||
|
||||
/datum/superheroes/proc/create(var/mob/living/carbon/human/H)
|
||||
assign_genes(H)
|
||||
assign_spells(H)
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
/datum/superheroes/griffin
|
||||
name = "The Griffin"
|
||||
default_spells = list(/obj/effect/proc_holder/spell/targeted/recruit)
|
||||
default_spells = list(/obj/effect/proc_holder/spell/targeted/click/recruit)
|
||||
class = "Supervillain"
|
||||
desc = "You are The Griffin, the ultimate supervillain. You thrive on chaos and have no respect for the supposed authority \
|
||||
of the command staff of this station. Along with your gang of dim-witted yet trusty henchmen, you will be able to execute \
|
||||
@@ -145,100 +145,102 @@
|
||||
|
||||
|
||||
//The Griffin's special recruit abilitiy
|
||||
/obj/effect/proc_holder/spell/targeted/recruit
|
||||
/obj/effect/proc_holder/spell/targeted/click/recruit
|
||||
name = "Recruit Greyshirt"
|
||||
desc = "Allows you to recruit a conscious, non-braindead, non-catatonic human to be part of the Greyshirts, your personal henchmen. This works on Civilians only and you can recruit a maximum of 3!."
|
||||
charge_max = 450
|
||||
clothes_req = 0
|
||||
clothes_req = FALSE
|
||||
range = 1 //Adjacent to user
|
||||
action_icon_state = "spell_greytide"
|
||||
var/recruiting = 0
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets,mob/living/user = usr)
|
||||
for(var/mob/living/carbon/human/target in targets)
|
||||
var/obj/item/organ/external/head/head_organ = target.get_organ("head")
|
||||
if(SSticker.mode.greyshirts.len >= 3)
|
||||
click_radius = -1
|
||||
selection_activated_message = "<span class='notice'>You start preparing a mindblowing monologue. <B>Left-click to cast at a target!</B></span>"
|
||||
selection_deactivated_message = "<span class='notice'>You decide to save your brilliance for another day.</span>"
|
||||
allowed_type = /mob/living/carbon/human
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/recruit/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE)
|
||||
if(SSticker.mode.greyshirts.len >= 3)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='warning'>You have already recruited the maximum number of henchmen.</span>")
|
||||
if(!in_range(user, target))
|
||||
to_chat(user, "<span class='warning'>You need to be closer to enthrall [target].</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!target.ckey)
|
||||
to_chat(user, "<span class='warning'>The target has no mind.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.stat)
|
||||
to_chat(user, "<span class='warning'>The target must be conscious.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(!ishuman(target))
|
||||
to_chat(user, "<span class='warning'>You can only recruit humans.</span>")
|
||||
charge_counter = charge_max
|
||||
return
|
||||
if(target.mind.assigned_role != "Civilian")
|
||||
to_chat(user, "<span class='warning'>You can only recruit Civilians.</span>")
|
||||
return
|
||||
if(recruiting)
|
||||
return FALSE
|
||||
if(recruiting)
|
||||
if(show_message)
|
||||
to_chat(user, "<span class='danger'>You are already recruiting!</span>")
|
||||
charge_counter = charge_max
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/recruit/valid_target(mob/living/carbon/human/target, user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
return target.ckey && !target.stat
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/click/recruit/cast(list/targets,mob/living/user = usr)
|
||||
var/mob/living/carbon/human/target = targets[1]
|
||||
if(target.mind.assigned_role != "Civilian")
|
||||
to_chat(user, "<span class='warning'>You can only recruit Civilians.</span>")
|
||||
revert_cast(user)
|
||||
return
|
||||
recruiting = TRUE
|
||||
to_chat(user, "<span class='danger'>This target is valid. You begin the recruiting process.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[user] focuses in concentration. Your head begins to ache.</span>")
|
||||
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You begin by introducing yourself and explaining what you're about.</span>")
|
||||
user.visible_message("<span class='danger'>[user] introduces [user.p_them()]self and explains [user.p_their()] plans.</span>")
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You begin the recruitment of [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] leans over towards [target], whispering excitedly as [user.p_they()] give[user.p_s()] a speech.</span>")
|
||||
to_chat(target, "<span class='danger'>You feel yourself agreeing with [user], and a surge of loyalty begins building.</span>")
|
||||
target.Weaken(12)
|
||||
sleep(20)
|
||||
if(ismindshielded(target))
|
||||
to_chat(user, "<span class='notice'>[target.p_they(TRUE)] are enslaved by Nanotrasen. You feel [target.p_their()] interest in your cause wane and disappear.</span>")
|
||||
user.visible_message("<span class='danger'>[user] stops talking for a moment, then moves back away from [target].</span>")
|
||||
to_chat(target, "<span class='danger'>Your mindshield implant activates, protecting you from conversion.</span>")
|
||||
return
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You begin filling out the application form with [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] pulls out a pen and paper and begins filling an application form with [target].</span>")
|
||||
to_chat(target, "<span class='danger'>You are being convinced by [user] to fill out an application form to become a henchman.</span>")//Ow the edge
|
||||
|
||||
if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant
|
||||
to_chat(user, "<span class='danger'>The enrollment process has been interrupted - you have lost the attention of [target].</span>")
|
||||
to_chat(target, "<span class='warning'>You move away and are no longer under the charm of [user]. The application form is null and void.</span>")
|
||||
recruiting = FALSE
|
||||
return
|
||||
recruiting = 1
|
||||
to_chat(user, "<span class='danger'>This target is valid. You begin the recruiting process.</span>")
|
||||
to_chat(target, "<span class='userdanger'>[user] focuses in concentration. Your head begins to ache.</span>")
|
||||
|
||||
for(var/progress = 0, progress <= 3, progress++)
|
||||
switch(progress)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You begin by introducing yourself and explaining what you're about.</span>")
|
||||
user.visible_message("<span class='danger'>[user] introduces [user.p_them()]self and explains [user.p_their()] plans.</span>")
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You begin the recruitment of [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] leans over towards [target], whispering excitedly as [user.p_they()] give[user.p_s()] a speech.</span>")
|
||||
to_chat(target, "<span class='danger'>You feel yourself agreeing with [user], and a surge of loyalty begins building.</span>")
|
||||
target.Weaken(12)
|
||||
sleep(20)
|
||||
if(ismindshielded(target))
|
||||
to_chat(user, "<span class='notice'>[target.p_they(TRUE)] are enslaved by Nanotrasen. You feel [target.p_their()] interest in your cause wane and disappear.</span>")
|
||||
user.visible_message("<span class='danger'>[user] stops talking for a moment, then moves back away from [target].</span>")
|
||||
to_chat(target, "<span class='danger'>Your mindshield implant activates, protecting you from conversion.</span>")
|
||||
return
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You begin filling out the application form with [target].</span>")
|
||||
user.visible_message("<span class='danger'>[user] pulls out a pen and paper and begins filling an application form with [target].</span>")
|
||||
to_chat(target, "<span class='danger'>You are being convinced by [user] to fill out an application form to become a henchman.</span>")//Ow the edge
|
||||
|
||||
if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant
|
||||
to_chat(user, "<span class='danger'>The enrollment process has been interrupted - you have lost the attention of [target].</span>")
|
||||
to_chat(target, "<span class='warning'>You move away and are no longer under the charm of [user]. The application form is null and void.</span>")
|
||||
recruiting = 0
|
||||
return
|
||||
|
||||
recruiting = 0
|
||||
to_chat(user, "<span class='notice'>You have recruited <b>[target]</b> as your henchman!</span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.</b></span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You must follow the orders of [user], and help [user.p_them()] succeed in [user.p_their()] dastardly schemes.</span>")
|
||||
to_chat(target, "<span class='deadsay'>You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.</span>")
|
||||
SSticker.mode.greyshirts += target.mind
|
||||
target.set_species(/datum/species/human)
|
||||
recruiting = FALSE
|
||||
to_chat(user, "<span class='notice'>You have recruited <b>[target]</b> as your henchman!</span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.</b></span>")
|
||||
to_chat(target, "<span class='deadsay'><b>You must follow the orders of [user], and help [user.p_them()] succeed in [user.p_their()] dastardly schemes.</span>")
|
||||
to_chat(target, "<span class='deadsay'>You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.</span>")
|
||||
SSticker.mode.greyshirts += target.mind
|
||||
target.set_species(/datum/species/human)
|
||||
var/obj/item/organ/external/head/head_organ = target.get_organ("head")
|
||||
if(head_organ)
|
||||
head_organ.h_style = "Bald"
|
||||
head_organ.f_style = "Shaved"
|
||||
target.s_tone = 35
|
||||
// No `update_dna=0` here because the character is being over-written
|
||||
target.change_eye_color(1,1,1)
|
||||
for(var/obj/item/W in target.get_all_slots())
|
||||
target.unEquip(W)
|
||||
target.rename_character(target.real_name, "Generic Henchman ([rand(1, 1000)])")
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey/greytide(target), slot_w_uniform)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/shoes/black/greytide(target), slot_shoes)
|
||||
target.equip_to_slot_or_del(new /obj/item/storage/toolbox/mechanical/greytide(target), slot_l_hand)
|
||||
target.equip_to_slot_or_del(new /obj/item/radio/headset(target), slot_l_ear)
|
||||
var/obj/item/card/id/syndicate/W = new(target)
|
||||
W.icon_state = "lifetimeid"
|
||||
W.access = list(ACCESS_MAINT_TUNNELS)
|
||||
W.assignment = "Greyshirt"
|
||||
W.rank = "Greyshirt"
|
||||
W.flags |= NODROP
|
||||
W.SetOwnerInfo(target)
|
||||
W.UpdateName()
|
||||
target.equip_to_slot_or_del(W, slot_wear_id)
|
||||
target.regenerate_icons()
|
||||
target.s_tone = 35
|
||||
// No `update_dna=0` here because the character is being over-written
|
||||
target.change_eye_color(1,1,1)
|
||||
for(var/obj/item/W in target.get_all_slots())
|
||||
target.unEquip(W)
|
||||
target.rename_character(target.real_name, "Generic Henchman ([rand(1, 1000)])")
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey/greytide(target), slot_w_uniform)
|
||||
target.equip_to_slot_or_del(new /obj/item/clothing/shoes/black/greytide(target), slot_shoes)
|
||||
target.equip_to_slot_or_del(new /obj/item/storage/toolbox/mechanical/greytide(target), slot_l_hand)
|
||||
target.equip_to_slot_or_del(new /obj/item/radio/headset(target), slot_l_ear)
|
||||
var/obj/item/card/id/syndicate/W = new(target)
|
||||
W.icon_state = "lifetimeid"
|
||||
W.access = list(ACCESS_MAINT_TUNNELS)
|
||||
W.assignment = "Greyshirt"
|
||||
W.rank = "Greyshirt"
|
||||
W.flags |= NODROP
|
||||
W.SetOwnerInfo(target)
|
||||
W.UpdateName()
|
||||
target.equip_to_slot_or_del(W, slot_wear_id)
|
||||
target.regenerate_icons()
|
||||
|
||||
@@ -122,8 +122,8 @@ Difficulty: Very Hard
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L)
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.martial_art && prob(H.martial_art.deflection_chance))
|
||||
. = TRUE
|
||||
if(H.mind && H.mind.martial_art && prob(H.mind.martial_art.deflection_chance))
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
|
||||
ranged_cooldown = world.time + 40
|
||||
|
||||
@@ -42,27 +42,6 @@
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner
|
||||
user.DoVentSmash()
|
||||
|
||||
// ---------- PRINCESS ACTIONS
|
||||
|
||||
/datum/action/innate/terrorspider/evolvequeen
|
||||
name = "Evolve Queen"
|
||||
icon_icon = 'icons/mob/terrorspider.dmi'
|
||||
button_icon_state = "terror_queen"
|
||||
|
||||
/datum/action/innate/terrorspider/evolvequeen/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/princess/user = owner
|
||||
if(!istype(user))
|
||||
to_chat(user, "<span class='warning'>ERROR: attempt to use evolve queen ability on a non-princess</span>")
|
||||
return
|
||||
var/feedings_left = user.feedings_to_evolve - user.fed
|
||||
if(feedings_left > 0)
|
||||
to_chat(user, "<span class='warning'>You must wrap [feedings_left] more humanoid prey before you can do this!</span>")
|
||||
return
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in GLOB.ts_spiderlist)
|
||||
if(Q.spider_awaymission == user.spider_awaymission)
|
||||
to_chat(user, "<span class='warning'>The presence of another Queen in the area is preventing you from maturing.")
|
||||
return
|
||||
user.evolve_to_queen()
|
||||
|
||||
// ---------- QUEEN ACTIONS
|
||||
|
||||
@@ -93,19 +72,11 @@
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner
|
||||
user.LayQueenEggs()
|
||||
|
||||
/datum/action/innate/terrorspider/queen/queenfakelings
|
||||
name = "Fake Spiderlings"
|
||||
icon_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "spiderling"
|
||||
|
||||
/datum/action/innate/terrorspider/queen/queenfakelings/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner
|
||||
user.QueenFakeLings()
|
||||
|
||||
// ---------- EMPRESS
|
||||
|
||||
/datum/action/innate/terrorspider/queen/empress/empresserase
|
||||
name = "Erase Brood"
|
||||
name = "Empress Erase Brood"
|
||||
icon_icon = 'icons/effects/blood.dmi'
|
||||
button_icon_state = "mgibbl1"
|
||||
|
||||
@@ -113,6 +84,16 @@
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner
|
||||
user.EraseBrood()
|
||||
|
||||
/datum/action/innate/terrorspider/queen/empress/empresslings
|
||||
name = "Empresss Spiderlings"
|
||||
icon_icon = 'icons/effects/effects.dmi'
|
||||
button_icon_state = "spiderling"
|
||||
|
||||
/datum/action/innate/terrorspider/queen/empress/empresslings/Activate()
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner
|
||||
user.EmpressLings()
|
||||
|
||||
|
||||
// ---------- WEB
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/Web()
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
else if(volume < 90)
|
||||
// bitten thrice, die quickly, severe muscle cramps make movement very difficult. Even calling for help probably won't save you.
|
||||
// total damage: 4, human health 150 until crit, = 37.5 ticks, = 75s = 1m15s until death
|
||||
update_flags |= M.adjustToxLoss(4, FALSE) // a bit worse than coiine
|
||||
update_flags |= M.adjustToxLoss(4, FALSE)
|
||||
update_flags |= M.EyeBlurry(6, FALSE)
|
||||
M.Confused(6)
|
||||
else
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
ventcrawler = 1
|
||||
idle_ventcrawl_chance = 0
|
||||
ai_playercontrol_allowtype = 0
|
||||
rapid = 3
|
||||
canlay = 1000
|
||||
spider_tier = TS_TIER_5
|
||||
projectiletype = /obj/item/projectile/terrorqueenspit/empress
|
||||
@@ -35,6 +34,8 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/New()
|
||||
..()
|
||||
empresslings_action = new()
|
||||
empresslings_action.Grant(src)
|
||||
empresserase_action = new()
|
||||
empresserase_action.Grant(src)
|
||||
|
||||
@@ -44,7 +45,6 @@
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/NestMode()
|
||||
..()
|
||||
queeneggs_action.button.name = "Empress Eggs"
|
||||
queenfakelings_action.button.name = "Empress Lings"
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/LayQueenEggs()
|
||||
var/eggtype = input("What kind of eggs?") as null|anything in list(TS_DESC_QUEEN, TS_DESC_MOTHER, TS_DESC_PRINCE, TS_DESC_PRINCESS, TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE, TS_DESC_WHITE, TS_DESC_BROWN)
|
||||
@@ -70,7 +70,7 @@
|
||||
if(TS_DESC_PRINCE)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings)
|
||||
if(TS_DESC_PRINCESS)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/princess, numlings)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings)
|
||||
if(TS_DESC_MOTHER)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings)
|
||||
if(TS_DESC_QUEEN)
|
||||
@@ -78,7 +78,7 @@
|
||||
else
|
||||
to_chat(src, "<span class='danger'>Unrecognized egg type.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/QueenFakeLings()
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EmpressLings()
|
||||
var/numlings = input("How many?") as null|anything in list(10, 20, 30, 40, 50)
|
||||
var/sbpc = input("%chance to be stillborn?") as null|anything in list(0, 25, 50, 75, 100)
|
||||
for(var/i=0, i<numlings, i++)
|
||||
@@ -96,7 +96,8 @@
|
||||
S.amount_grown = 250
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EraseBrood()
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(T.spider_tier < spider_tier)
|
||||
T.degenerate = 1
|
||||
to_chat(T, "<span class='userdanger'>Through the hivemind, the raw power of [src] floods into your body, burning it from the inside out!</span>")
|
||||
@@ -106,8 +107,6 @@
|
||||
qdel(T)
|
||||
to_chat(src, "<span class='userdanger'>All Terror Spiders, except yourself, will die off shortly.</span>")
|
||||
|
||||
|
||||
/obj/item/projectile/terrorqueenspit/empress
|
||||
damage_type = BURN
|
||||
damage = 30
|
||||
bonus_tox = 0
|
||||
damage = 90
|
||||
|
||||
|
||||
@@ -39,11 +39,10 @@
|
||||
to_chat(src, "<span class='warning'>You must wrap more humanoid prey before you can do this!</span>")
|
||||
return
|
||||
var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN)
|
||||
var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown)
|
||||
if(num_brown < 2)
|
||||
var/list/spider_array = CountSpidersDetailed(FALSE)
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2)
|
||||
eggtypes += TS_DESC_BROWN
|
||||
var/num_black = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
if(num_black < 2)
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2)
|
||||
eggtypes += TS_DESC_BLACK
|
||||
var/eggtype = pick(eggtypes)
|
||||
if(client)
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/DoHiveSense()
|
||||
var/hsline = ""
|
||||
to_chat(src, "Your Brood: ")
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(T.spider_awaymission != spider_awaymission)
|
||||
continue
|
||||
hsline = "* [T] in [get_area(T)], "
|
||||
@@ -20,21 +21,55 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpiders()
|
||||
var/numspiders = 0
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission)
|
||||
numspiders += 1
|
||||
return numspiders
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersType(specific_type)
|
||||
var/numspiders = 0
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission)
|
||||
if(T.type == specific_type)
|
||||
numspiders += 1
|
||||
for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list)
|
||||
if(E.spiderling_type == specific_type && E.z == z)
|
||||
numspiders += E.spiderling_number
|
||||
for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list)
|
||||
if(!L.stillborn && L.grow_as == specific_type && L.z == z)
|
||||
numspiders += 1
|
||||
return numspiders
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersDetailed(check_mine = FALSE, list/mytypes = list())
|
||||
var/list/spider_totals = list("all" = 0)
|
||||
var/check_list = length(mytypes) > 0
|
||||
for(var/thistype in mytypes)
|
||||
spider_totals[thistype] = 0
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(T.stat == DEAD || T.spider_placed || spider_awaymission != T.spider_awaymission)
|
||||
continue
|
||||
if(check_mine && T.spider_myqueen != src)
|
||||
continue
|
||||
if(check_list && !(T.type in mytypes))
|
||||
continue
|
||||
if(T == src)
|
||||
continue
|
||||
if(spider_totals[T.type])
|
||||
spider_totals[T.type]++
|
||||
else
|
||||
spider_totals[T.type] = 1
|
||||
spider_totals["all"]++
|
||||
for(var/thing in GLOB.ts_egg_list)
|
||||
var/obj/structure/spider/eggcluster/terror_eggcluster/E = thing
|
||||
if(check_mine && E.spider_myqueen != src)
|
||||
continue
|
||||
if(check_list && E.spiderling_type && !(E.spiderling_type in mytypes))
|
||||
continue
|
||||
if(spider_totals[E.spiderling_type])
|
||||
spider_totals[E.spiderling_type] += E.spiderling_number
|
||||
else
|
||||
spider_totals[E.spiderling_type] = E.spiderling_number
|
||||
spider_totals["all"] += E.spiderling_number
|
||||
for(var/thing in GLOB.ts_spiderling_list)
|
||||
var/obj/structure/spider/spiderling/terror_spiderling/L = thing
|
||||
if(L.stillborn)
|
||||
continue
|
||||
if(check_mine && L.spider_myqueen != src)
|
||||
continue
|
||||
if(check_list && L.grow_as && !(L.grow_as in mytypes))
|
||||
continue
|
||||
if(spider_totals[L.grow_as])
|
||||
spider_totals[L.grow_as]++
|
||||
else
|
||||
spider_totals[L.grow_as] = 1
|
||||
spider_totals["all"]++
|
||||
return spider_totals
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
spider_opens_doors = 2
|
||||
web_type = /obj/structure/spider/terrorweb/purple
|
||||
ai_spins_webs = FALSE
|
||||
gender = MALE
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/prince/death(gibbed)
|
||||
if(can_die() && !hasdied && spider_uo71)
|
||||
|
||||
@@ -2,58 +2,76 @@
|
||||
// --------------------------------------------------------------------------------
|
||||
// ----------------- TERROR SPIDERS: T3 PRINCESS OF TERROR --------------------------
|
||||
// --------------------------------------------------------------------------------
|
||||
// -------------: ROLE: cutesy
|
||||
// -------------: AI: as green, but will evolve to queen when it can
|
||||
// -------------: SPECIAL: can evolve into a queen, if fed enough
|
||||
// -------------: TO FIGHT IT: kill it before it evolves
|
||||
// -------------: ROLE: mini-queen, maintains a smaller nest, but also more expendable
|
||||
// -------------: AI: maintains a small group of spiders. Small fraction of a queen's nest.
|
||||
// -------------: SPECIAL: lays eggs over time, like a queen
|
||||
// -------------: TO FIGHT IT: hunt it before it lays eggs
|
||||
// -------------: SPRITES FROM: FoS, https://www.paradisestation.org/forum/profile/335-fos
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/princess
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess
|
||||
name = "Princess of Terror spider"
|
||||
desc = "An enormous spider. It looks strangely cute and fluffy."
|
||||
spider_role_summary = "Future Queen"
|
||||
spider_role_summary = "Mini-Queen"
|
||||
ai_target_method = TS_DAMAGE_SIMPLE
|
||||
icon_state = "terror_princess1"
|
||||
icon_living = "terror_princess1"
|
||||
icon_dead = "terror_princess1_dead"
|
||||
maxHealth = 150
|
||||
health = 150
|
||||
regen_points_per_hp = 1 // always regens very fast
|
||||
force_threshold = 18 // outright immune to anything of force under 18, same as queen
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
idle_ventcrawl_chance = 5
|
||||
spider_tier = TS_TIER_3
|
||||
spider_opens_doors = 2
|
||||
web_type = /obj/structure/spider/terrorweb/queen
|
||||
var/feedings_to_evolve = 3
|
||||
var/datum/action/innate/terrorspider/ventsmash/ventsmash_action
|
||||
var/datum/action/innate/terrorspider/evolvequeen/evolvequeen_action
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/princess/New()
|
||||
..()
|
||||
ventsmash_action = new()
|
||||
ventsmash_action.Grant(src)
|
||||
evolvequeen_action = new()
|
||||
evolvequeen_action.Grant(src)
|
||||
// Unlike queens, no ranged attack.
|
||||
ranged = 0
|
||||
retreat_distance = 0
|
||||
minimum_distance = 0
|
||||
projectilesound = null
|
||||
projectiletype = null
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/princess/proc/evolve_to_queen()
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q = new(loc)
|
||||
if(mind)
|
||||
mind.transfer_to(Q)
|
||||
// Calling `transfer_to()` removes our new body (the Queen's) ability to see the med hud, so we have to re-add the queen here.
|
||||
var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
U.add_hud_to(Q)
|
||||
qdel(src)
|
||||
canlay = 0
|
||||
hasnested = TRUE
|
||||
spider_spawnfrequency = 300 // 30 seconds
|
||||
var/grant_prob = 25 // 25% chance every spider_spawnfrequency seconds to gain 1 egg
|
||||
var/spider_max_children = 8
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/princess/DoWrap()
|
||||
. = ..()
|
||||
if(fed == 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_queen_subtype_abilities()
|
||||
// Queens start in movement mode, where they can ventcrawl but not lay eggs. Then they move to NestMode() where they can wallsmash and egglay, but not ventcrawl.
|
||||
// Princesses are simpler, and can always lay eggs, always vent crawl, but never smash walls. Unlike queens, they don't have a "nesting" transformation.
|
||||
queeneggs_action = new()
|
||||
queeneggs_action.Grant(src)
|
||||
queensense_action = new()
|
||||
queensense_action.Grant(src)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ListAvailableEggTypes()
|
||||
var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN)
|
||||
|
||||
// Each princess can also have ONE black/purple/brown. If it dies, they can pick a new spider from the 3 advanced types to lay.
|
||||
var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/black, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/brown))
|
||||
if(spider_array["all"] < 1)
|
||||
valid_types |= TS_DESC_BLACK
|
||||
valid_types |= TS_DESC_PURPLE
|
||||
valid_types |= TS_DESC_BROWN
|
||||
|
||||
return valid_types
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_eggs()
|
||||
spider_lastspawn = world.time
|
||||
|
||||
if(!prob(grant_prob))
|
||||
return
|
||||
|
||||
var/list/spider_array = CountSpidersDetailed(TRUE)
|
||||
var/brood_count = spider_array["all"]
|
||||
|
||||
// Color shifts depending on how much of their brood capacity they have used.
|
||||
if(brood_count == 0)
|
||||
icon_state = "terror_princess1"
|
||||
icon_living = "terror_princess1"
|
||||
icon_dead = "terror_princess1_dead"
|
||||
desc = "An enormous spider. It looks strangely cute and fluffy, with soft pink fur covering most of its body."
|
||||
else if(fed == 1)
|
||||
else if(brood_count < (spider_max_children /2))
|
||||
icon_state = "terror_princess2"
|
||||
icon_living = "terror_princess2"
|
||||
icon_dead = "terror_princess2_dead"
|
||||
@@ -62,13 +80,45 @@
|
||||
icon_state = "terror_princess3"
|
||||
icon_living = "terror_princess3"
|
||||
icon_dead = "terror_princess3_dead"
|
||||
desc = "An enormous spider. Its entire body has turned an ominous blood red color, with actual blood dripping from its jaws. It stares around, hungrily."
|
||||
desc = "An enormous spider. Its entire body looks to be the color of dried blood."
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/princess/spider_special_action()
|
||||
if(cocoon_target)
|
||||
handle_cocoon_target()
|
||||
else if(fed >= feedings_to_evolve)
|
||||
evolve_to_queen()
|
||||
else if(world.time > (last_cocoon_object + freq_cocoon_object))
|
||||
seek_cocoon_target()
|
||||
if(!isturf(loc))
|
||||
to_chat(src, "<span class='danger'>You cannot generate eggs while hiding in [loc].</span>")
|
||||
return
|
||||
|
||||
if((brood_count + canlay) >= spider_max_children)
|
||||
return
|
||||
canlay++
|
||||
if(canlay == 1)
|
||||
to_chat(src, "<span class='notice'>You have an egg available to lay.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='notice'>You have [canlay] eggs available to lay.</span>")
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/NestMode()
|
||||
// Princesses don't nest. However, we still need to override this in case an AI princess calls it.
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/spider_special_action()
|
||||
// Princess AI routine. GREATLY simplified version of queen routine.
|
||||
if(!stat && !ckey)
|
||||
// Utilize normal queen AI for finding a nest site (neststep=0), and activating NestMode() (neststep=1)
|
||||
if(neststep != 2)
|
||||
return ..()
|
||||
// After that, simply lay an egg once per nestfrequency, until we have the max.
|
||||
if(world.time < (lastnestsetup + nestfrequency))
|
||||
return
|
||||
lastnestsetup = world.time
|
||||
if(ai_nest_is_full())
|
||||
return
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(pick(spider_types_standard), 1)
|
||||
// Yes, this means NPC princesses won't create T2 spiders.
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ai_nest_is_full()
|
||||
var/list/spider_array = CountSpidersDetailed(TRUE)
|
||||
if(spider_array["all"] >= spider_max_children)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// -------------: ROLE: gamma-level threat to the whole station, like a blob
|
||||
// -------------: AI: builds a nest, lays many eggs, attempts to take over the station
|
||||
// -------------: SPECIAL: spins webs, breaks lights, breaks cameras, webs objects, lays eggs, commands other spiders...
|
||||
// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs and/or decloner guns are a very good idea.
|
||||
// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs are a very good idea.
|
||||
// -------------: SPRITES FROM: IK3I
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen
|
||||
@@ -43,69 +43,94 @@
|
||||
var/neststep = 0
|
||||
var/hasnested = FALSE
|
||||
var/spider_max_per_nest = 35 // above this, AI queens become stable
|
||||
var/canlay = 4 // main counter for egg-laying ability! # = num uses, incremented at intervals
|
||||
var/canlay = 5 // main counter for egg-laying ability! # = num uses, incremented at intervals
|
||||
var/eggslaid = 0
|
||||
var/spider_can_fakelings = 3 // spawns defective spiderlings that don't grow up, used to freak out crew, atmosphere
|
||||
var/list/spider_types_standard = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green, /mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
var/datum/action/innate/terrorspider/queen/queennest/queennest_action
|
||||
var/datum/action/innate/terrorspider/queen/queensense/queensense_action
|
||||
var/datum/action/innate/terrorspider/queen/queeneggs/queeneggs_action
|
||||
var/datum/action/innate/terrorspider/queen/queenfakelings/queenfakelings_action
|
||||
var/datum/action/innate/terrorspider/ventsmash/ventsmash_action
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/New()
|
||||
..()
|
||||
queennest_action = new()
|
||||
queennest_action.Grant(src)
|
||||
ventsmash_action = new()
|
||||
ventsmash_action.Grant(src)
|
||||
grant_queen_subtype_abilities()
|
||||
spider_myqueen = src
|
||||
if(spider_awaymission)
|
||||
spider_growinstantly = 1
|
||||
spider_growinstantly = TRUE
|
||||
spider_spawnfrequency = 150
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_queen_subtype_abilities()
|
||||
queennest_action = new()
|
||||
queennest_action.Grant(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive.
|
||||
if(ckey && canlay < 12 && hasnested) // max 12 eggs worth stored at any one time, realistically that's tons.
|
||||
if(ckey && hasnested)
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
if(eggslaid >= 20)
|
||||
canlay += 3
|
||||
else if(eggslaid >= 10)
|
||||
canlay += 2
|
||||
else
|
||||
canlay++
|
||||
spider_lastspawn = world.time
|
||||
if(canlay == 1)
|
||||
to_chat(src, "<span class='notice'>You have an egg available to lay.</span>")
|
||||
else if(canlay == 12)
|
||||
to_chat(src, "<span class='notice'>You have [canlay] eggs available to lay. You won't grow any more eggs until you lay some of your existing ones.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='notice'>You have [canlay] eggs available to lay.</span>")
|
||||
grant_eggs()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_eggs()
|
||||
spider_lastspawn = world.time
|
||||
canlay += getSpiderLevel()
|
||||
if(canlay == 1)
|
||||
to_chat(src, "<span class='notice'>You have an egg available to lay.</span>")
|
||||
else if(canlay > 1)
|
||||
to_chat(src, "<span class='notice'>You have [canlay] eggs available to lay.</span>")
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/getSpiderLevel()
|
||||
return 1 + round(MinutesAlive() / 10)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MinutesAlive()
|
||||
return round((world.time - spider_creation_time) / 600)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/death(gibbed)
|
||||
if(can_die() && !hasdied)
|
||||
if(spider_uo71)
|
||||
UnlockBlastDoors("UO71_Caves")
|
||||
// When a queen dies, so do her player-controlled purple-type guardians. Intended as a motivator for purples to ensure they guard her.
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in GLOB.ts_spiderlist)
|
||||
if(ckey)
|
||||
P.visible_message("<span class='danger'>\The [src] writhes in pain!</span>")
|
||||
to_chat(P,"<span class='userdanger'>\The [src] has died. Without her hivemind link, purple terrors like yourself cannot survive more than a few minutes!</span>")
|
||||
P.degenerate = 1
|
||||
// When a queen (or subtype!) dies, so do all of her spiderlings, and half of all her fully grown offspring
|
||||
// This feature is intended to provide a way for crew to still win even if the queen has overwhelming numbers - by sniping the queen.
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(!T.spider_myqueen)
|
||||
continue
|
||||
if(T.spider_myqueen != src)
|
||||
continue
|
||||
if(prob(50) || T.spider_tier >= spider_tier)
|
||||
to_chat(T, "<span class='userdanger'>\The psychic backlash from the death of [src] crashes into your mind! Somehow... you find a way to keep going!</span>")
|
||||
continue
|
||||
T.visible_message("<span class='danger'>[T] writhes in pain!</span>")
|
||||
to_chat(T, "<span class='userdanger'>\The psychic backlash from the death of [src] overwhelms you! You feel the life start to drain out of you...</span>")
|
||||
T.degenerate = TRUE
|
||||
for(var/thing in GLOB.ts_spiderling_list)
|
||||
var/obj/structure/spider/spiderling/terror_spiderling/T = thing
|
||||
if(T.spider_myqueen && T.spider_myqueen == src)
|
||||
qdel(T)
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/Retaliate()
|
||||
..()
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
T.enemies |= enemies
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full()
|
||||
var/numspiders = CountSpiders()
|
||||
if(numspiders >= spider_max_per_nest)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/spider_special_action()
|
||||
if(!stat && !ckey)
|
||||
switch(neststep)
|
||||
@@ -152,11 +177,13 @@
|
||||
neststep = 2
|
||||
NestMode()
|
||||
if(2)
|
||||
// Create initial four purple nest guards.
|
||||
// Create initial T2 spiders.
|
||||
if(world.time > (lastnestsetup + nestfrequency))
|
||||
lastnestsetup = world.time
|
||||
spider_lastspawn = world.time
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 4)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 2)
|
||||
neststep = 3
|
||||
if(3)
|
||||
// Create spiders (random types) until nest is full.
|
||||
@@ -183,28 +210,26 @@
|
||||
neststep = 4
|
||||
else
|
||||
spider_lastspawn = world.time
|
||||
var/num_purple = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple)
|
||||
var/num_white = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/white)
|
||||
var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown)
|
||||
if(num_purple < 4)
|
||||
var/list/spider_array = CountSpidersDetailed(FALSE)
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 4)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2)
|
||||
else if(num_white < 2)
|
||||
else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/white] < 2)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2)
|
||||
else if(num_brown < 4)
|
||||
else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 4)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 4)
|
||||
else
|
||||
DoLayTerrorEggs(pick(spider_types_standard), 5)
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestPrompt()
|
||||
var/confirm = alert(src, "Are you sure you want to nest? You will be able to lay eggs, and smash walls, but not ventcrawl.","Nest?","Yes","No")
|
||||
if(confirm == "Yes")
|
||||
NestMode()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestMode()
|
||||
queeneggs_action = new()
|
||||
queeneggs_action.Grant(src)
|
||||
queenfakelings_action = new()
|
||||
queenfakelings_action.Grant(src)
|
||||
queensense_action = new()
|
||||
queensense_action.Grant(src)
|
||||
queennest_action.Remove(src)
|
||||
@@ -213,21 +238,8 @@
|
||||
ai_ventcrawls = FALSE
|
||||
environment_smash = ENVIRONMENT_SMASH_RWALLS
|
||||
DoQueenScreech(8, 100, 8, 100)
|
||||
MassFlicker()
|
||||
to_chat(src, "<span class='notice'>You have matured to your egglaying stage. You can now smash through walls, and lay eggs, but can no longer ventcrawl.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MassFlicker()
|
||||
var/list/target_lights = list()
|
||||
for(var/mob/living/carbon/human/H in GLOB.player_list)
|
||||
if(H.z != z)
|
||||
continue
|
||||
if(H.stat == DEAD)
|
||||
continue
|
||||
for(var/obj/machinery/light/L in orange(7, H))
|
||||
if(L.on && prob(25))
|
||||
target_lights += L
|
||||
for(var/obj/machinery/light/I in target_lights)
|
||||
I.flicker()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/LayQueenEggs()
|
||||
if(stat == DEAD)
|
||||
@@ -242,42 +254,21 @@
|
||||
else
|
||||
to_chat(src, "<span class='danger'>Too soon to attempt that again. Wait just a few more seconds...</span>")
|
||||
return
|
||||
var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE)
|
||||
if(canlay >= 4)
|
||||
eggtypes |= TS_DESC_BROWN
|
||||
if(canlay >= 12)
|
||||
eggtypes |= TS_DESC_MOTHER
|
||||
eggtypes |= TS_DESC_PRINCE
|
||||
var/num_purples = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple)
|
||||
if(num_purples >= 2)
|
||||
eggtypes -= TS_DESC_PURPLE
|
||||
var/num_blacks = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black)
|
||||
if(num_blacks >= 2)
|
||||
eggtypes -= TS_DESC_BLACK
|
||||
var/list/eggtypes = ListAvailableEggTypes()
|
||||
var/list/eggtypes_uncapped = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN)
|
||||
|
||||
var/eggtype = input("What kind of eggs?") as null|anything in eggtypes
|
||||
if(canlay < 1)
|
||||
// this was checked before input() but we have to check again to prevent them spam-clicking the popup.
|
||||
to_chat(src, "<span class='danger'>Too soon to lay another egg.</span>")
|
||||
return
|
||||
if(!(eggtype in eggtypes))
|
||||
to_chat(src, "<span class='danger'>Unrecognized egg type.</span>")
|
||||
return 0
|
||||
if(eggtype == TS_DESC_MOTHER || eggtype == TS_DESC_PRINCE)
|
||||
if(canlay < 12)
|
||||
to_chat(src, "<span class='danger'>Insufficient strength. It takes as much effort to lay one of those as it does to lay 12 normal eggs.</span>")
|
||||
else
|
||||
if(eggtype == TS_DESC_MOTHER)
|
||||
canlay -= 12
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, 1)
|
||||
else if(eggtype == TS_DESC_PRINCE)
|
||||
canlay -= 12
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, 1)
|
||||
return
|
||||
else if(eggtype == TS_DESC_BROWN)
|
||||
if(canlay < 4)
|
||||
to_chat(src, "<span class='danger'>Insufficient strength. It takes as much effort to lay one of those as it does to lay 4 normal eggs.</span>")
|
||||
else
|
||||
canlay -= 4
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 1)
|
||||
return
|
||||
|
||||
// Multiple of eggtypes_uncapped can be laid at once. Other types must be laid one at a time (to prevent exploits)
|
||||
var/numlings = 1
|
||||
if(eggtype != TS_DESC_PURPLE)
|
||||
if(eggtype in eggtypes_uncapped)
|
||||
if(canlay >= 5)
|
||||
numlings = input("How many in the batch?") as null|anything in list(1, 2, 3, 4, 5)
|
||||
else if(canlay >= 3)
|
||||
@@ -287,27 +278,55 @@
|
||||
if(eggtype == null || numlings == null)
|
||||
to_chat(src, "<span class='danger'>Cancelled.</span>")
|
||||
return
|
||||
// Actually lay the eggs.
|
||||
if(canlay < numlings)
|
||||
// We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once.
|
||||
to_chat(src, "<span class='warning'>Too soon to do this again!</span>")
|
||||
return
|
||||
canlay -= numlings
|
||||
eggslaid += numlings
|
||||
if(eggtype == TS_DESC_RED)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings)
|
||||
else if(eggtype == TS_DESC_GRAY)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings)
|
||||
else if(eggtype == TS_DESC_GREEN)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings)
|
||||
else if(eggtype == TS_DESC_BLACK)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings)
|
||||
else if(eggtype == TS_DESC_PURPLE)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings)
|
||||
else
|
||||
to_chat(src, "<span class='danger'>Unrecognized egg type.</span>")
|
||||
switch(eggtype)
|
||||
if(TS_DESC_RED)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings)
|
||||
if(TS_DESC_GRAY)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings)
|
||||
if(TS_DESC_GREEN)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings)
|
||||
if(TS_DESC_BLACK)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings)
|
||||
if(TS_DESC_PURPLE)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings)
|
||||
if(TS_DESC_BROWN)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, numlings)
|
||||
if(TS_DESC_MOTHER)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings)
|
||||
if(TS_DESC_PRINCE)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings)
|
||||
if(TS_DESC_PRINCESS)
|
||||
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings)
|
||||
else
|
||||
to_chat(src, "<span class='danger'>Unrecognized egg type.</span>")
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ListAvailableEggTypes()
|
||||
if(MinutesAlive() >= 20)
|
||||
var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/mother, /mob/living/simple_animal/hostile/poison/terror_spider/prince, /mob/living/simple_animal/hostile/poison/terror_spider/queen/princess))
|
||||
if(spider_array["all"] == 0)
|
||||
return list(TS_DESC_PRINCE, TS_DESC_PRINCESS) // Mother will be added to this list.... AFTER mothers are reworked.
|
||||
|
||||
var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN)
|
||||
var/list/spider_array = CountSpidersDetailed(FALSE, list(/mob/living/simple_animal/hostile/poison/terror_spider/brown, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/black))
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2)
|
||||
valid_types += TS_DESC_BROWN
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 2)
|
||||
valid_types += TS_DESC_PURPLE
|
||||
if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2)
|
||||
valid_types += TS_DESC_BLACK
|
||||
return valid_types
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/DoQueenScreech(light_range, light_chance, camera_range, camera_chance)
|
||||
visible_message("<span class='userdanger'>\The [src] emits a bone-chilling shriek!</span>")
|
||||
visible_message("<span class='userdanger'>[src] emits a bone-chilling shriek!</span>")
|
||||
for(var/obj/machinery/light/L in orange(light_range, src))
|
||||
if(L.on && prob(light_chance))
|
||||
L.break_light_tube()
|
||||
@@ -315,46 +334,33 @@
|
||||
if(C.status && prob(camera_chance))
|
||||
C.toggle_cam(src, 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/QueenFakeLings()
|
||||
if(eggslaid < 10)
|
||||
to_chat(src, "<span class='danger'>You must lay at least 10 eggs before doing this.</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/examine(mob/user)
|
||||
. = ..()
|
||||
if(!key || stat == DEAD)
|
||||
return
|
||||
if(spider_can_fakelings)
|
||||
spider_can_fakelings--
|
||||
var/numlings = 25
|
||||
for(var/i in 1 to numlings)
|
||||
var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src))
|
||||
S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red
|
||||
S.stillborn = 1
|
||||
S.spider_mymother = src
|
||||
if(!spider_can_fakelings)
|
||||
queenfakelings_action.Remove(src)
|
||||
else
|
||||
to_chat(src, "<span class='danger'>You have run out of uses of this ability.</span>")
|
||||
if(!isobserver(user) && !isterrorspider(user))
|
||||
return
|
||||
. += "<span class='notice'>[p_they(TRUE)] has laid [eggslaid] egg[eggslaid != 1 ? "s" : ""].</span>"
|
||||
. += "<span class='notice'>[p_they(TRUE)] has lived for [MinutesAlive()] minutes.</span>"
|
||||
|
||||
|
||||
/obj/item/projectile/terrorqueenspit
|
||||
name = "poisonous spit"
|
||||
damage = 0
|
||||
name = "acid spit"
|
||||
damage = 40
|
||||
icon_state = "toxin"
|
||||
damage_type = TOX
|
||||
var/bonus_tox = 30
|
||||
damage_type = BURN
|
||||
|
||||
/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target, blocked = 0, hit_zone)
|
||||
if(ismob(target) && blocked < 100)
|
||||
var/mob/living/L = target
|
||||
if(L.reagents)
|
||||
if(L.can_inject(null, FALSE, "chest", FALSE))
|
||||
L.Hallucinate(400)
|
||||
if(!isterrorspider(L))
|
||||
L.adjustToxLoss(bonus_tox)
|
||||
|
||||
/obj/structure/spider/terrorweb/queen
|
||||
name = "shimmering web"
|
||||
desc = "This web seems to shimmer all different colors in the light."
|
||||
name = "airtight web"
|
||||
desc = "This multi-layered web seems to be able to resist air pressure."
|
||||
|
||||
|
||||
/obj/structure/spider/terrorweb/queen/New()
|
||||
. = ..()
|
||||
air_update_turf(TRUE)
|
||||
|
||||
/obj/structure/spider/terrorweb/queen/CanAtmosPass(turf/T)
|
||||
return FALSE
|
||||
|
||||
/obj/structure/spider/terrorweb/queen/web_special_ability(mob/living/carbon/C)
|
||||
if(istype(C))
|
||||
var/inject_target = pick("chest","head")
|
||||
if(C.can_inject(null, FALSE, inject_target, FALSE))
|
||||
C.Hallucinate(400)
|
||||
C.adjustToxLoss(30)
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
C.enemies = enemies
|
||||
if(spider_growinstantly)
|
||||
C.amount_grown = 250
|
||||
C.spider_growinstantly = 1
|
||||
C.spider_growinstantly = TRUE
|
||||
spawn(10)
|
||||
stop_automated_movement = 0
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
name = "terror egg cluster"
|
||||
desc = "A cluster of tiny spider eggs. They pulse with a strong inner life, and appear to have sharp thorns on the sides."
|
||||
icon_state = "eggs"
|
||||
var/spider_growinstantly = 0
|
||||
var/spider_growinstantly = FALSE
|
||||
var/spider_myqueen = null
|
||||
var/spider_mymother = null
|
||||
var/spiderling_type = null
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
spider_steps_taken++
|
||||
CreatePath(entry_vent)
|
||||
step_to(src,entry_vent)
|
||||
if(spider_debug > 0)
|
||||
if(spider_debug)
|
||||
visible_message("<span class='notice'>[src] moves towards the vent [entry_vent].</span>")
|
||||
else
|
||||
path_to_vent = 0
|
||||
|
||||
@@ -17,6 +17,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
// Name / Description
|
||||
name = "terror spider"
|
||||
desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug."
|
||||
gender = FEMALE
|
||||
|
||||
// Icons
|
||||
icon = 'icons/mob/terrorspider.dmi'
|
||||
@@ -141,29 +142,33 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
var/mylocation = null
|
||||
var/chasecycles = 0
|
||||
var/web_infects = 0
|
||||
var/spider_creation_time = 0
|
||||
|
||||
var/datum/action/innate/terrorspider/web/web_action
|
||||
var/web_type = /obj/structure/spider/terrorweb
|
||||
var/datum/action/innate/terrorspider/wrap/wrap_action
|
||||
|
||||
// Breathing - require some oxygen, and no toxins, but take little damage from this requirement not being met (they can hold their breath)
|
||||
// Breathing - require some oxygen, and no toxins
|
||||
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
unsuitable_atmos_damage = 1
|
||||
|
||||
// Temperature - can freeze in space and cook in plasma, but it takes extreme temperatures to do this.
|
||||
minbodytemp = 100
|
||||
maxbodytemp = 500
|
||||
heat_damage_per_tick = 3
|
||||
// Temperature
|
||||
heat_damage_per_tick = 5 // Takes 250% normal damage from being in a hot environment ("kill it with fire!")
|
||||
|
||||
// DEBUG OPTIONS & COMMANDS
|
||||
var/spider_growinstantly = 0 // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI.
|
||||
var/spider_debug = 0
|
||||
var/spider_growinstantly = FALSE // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI.
|
||||
var/spider_debug = FALSE
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// --------------------- TERROR SPIDERS: SHARED ATTACK CODE -----------------------
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
|
||||
// Forces terrors to use the 'bite' graphic when attacking something. Same as code/modules/mob/living/carbon/alien/larva/larva_defense.dm#L34
|
||||
if(!no_effect && !visual_effect_icon)
|
||||
visual_effect_icon = ATTACK_EFFECT_BITE
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/AttackingTarget()
|
||||
if(isterrorspider(target))
|
||||
if(target in enemies)
|
||||
@@ -187,14 +192,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
if(F.welded)
|
||||
to_chat(src, "The fire door is welded shut.")
|
||||
else
|
||||
visible_message("<span class='danger'>\The [src] pries open the firedoor!</span>")
|
||||
visible_message("<span class='danger'>[src] pries open the firedoor!</span>")
|
||||
F.open()
|
||||
else
|
||||
to_chat(src, "Closing fire doors does not help.")
|
||||
else if(istype(target, /obj/machinery/door/airlock))
|
||||
var/obj/machinery/door/airlock/A = target
|
||||
if(A.density)
|
||||
try_open_airlock(A)
|
||||
try_open_airlock(A)
|
||||
else if(isliving(target) && (!client || a_intent == INTENT_HARM))
|
||||
var/mob/living/G = target
|
||||
if(issilicon(G))
|
||||
@@ -221,27 +225,23 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/examine(mob/user)
|
||||
. = ..()
|
||||
var/list/msgs = list()
|
||||
if(stat == DEAD)
|
||||
msgs += "<span class='notice'>It appears to be dead.</span>\n"
|
||||
else
|
||||
if(stat != DEAD)
|
||||
if(key)
|
||||
msgs += "<span class='warning'>Its eyes regard you with a curious intelligence.</span>"
|
||||
. += "<span class='warning'>[p_they(TRUE)] regards [p_their()] surroundings with a curious intelligence.</span>"
|
||||
if(health > (maxHealth*0.95))
|
||||
msgs += "<span class='notice'>It is in excellent health.</span>"
|
||||
. += "<span class='notice'>[p_they(TRUE)] is in excellent health.</span>"
|
||||
else if(health > (maxHealth*0.75))
|
||||
msgs += "<span class='notice'>It has a few injuries.</span>"
|
||||
. += "<span class='notice'>[p_they(TRUE)] has a few injuries.</span>"
|
||||
else if(health > (maxHealth*0.55))
|
||||
msgs += "<span class='warning'>It has many injuries.</span>"
|
||||
. += "<span class='warning'>[p_they(TRUE)] has many injuries.</span>"
|
||||
else if(health > (maxHealth*0.25))
|
||||
msgs += "<span class='warning'>It is barely clinging on to life!</span>"
|
||||
. += "<span class='warning'>[p_they(TRUE)] is barely clinging on to life!</span>"
|
||||
if(degenerate)
|
||||
msgs += "<span class='warning'>It appears to be dying.</span>"
|
||||
. += "<span class='warning'>[p_they(TRUE)] appears to be dying.</span>"
|
||||
else if(health < maxHealth && regen_points > regen_points_per_kill)
|
||||
msgs += "<span class='notice'>It appears to be regenerating quickly.</span>"
|
||||
. += "<span class='notice'>[p_they(TRUE)] appears to be regenerating quickly.</span>"
|
||||
if(killcount >= 1)
|
||||
msgs += "<span class='warning'>It has blood dribbling from its mouth.</span>"
|
||||
. += msgs.Join("<BR>")
|
||||
. += "<span class='warning'>[p_they(TRUE)] has blood dribbling from [p_their()] mouth.</span>"
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/New()
|
||||
..()
|
||||
@@ -254,9 +254,10 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
if(web_type)
|
||||
web_action = new()
|
||||
web_action.Grant(src)
|
||||
wrap_action = new()
|
||||
wrap_action.Grant(src)
|
||||
|
||||
if(regen_points_per_tick < regen_points_per_hp)
|
||||
// Only grant the Wrap action button to spiders who need to use it to regenerate their health
|
||||
wrap_action = new()
|
||||
wrap_action.Grant(src)
|
||||
name += " ([rand(1, 1000)])"
|
||||
real_name = name
|
||||
msg_terrorspiders("[src] has grown in [get_area(src)].")
|
||||
@@ -278,6 +279,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
addtimer(CALLBACK(src, .proc/announcetoghosts), 30)
|
||||
var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
U.add_hud_to(src)
|
||||
spider_creation_time = world.time
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts()
|
||||
if(spider_awaymission)
|
||||
@@ -285,8 +287,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
if(ckey)
|
||||
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
|
||||
notify_ghosts("[src] has appeared in [get_area(src)]. (already player-controlled)", source = src, alert_overlay = alert_overlay)
|
||||
notify_ghosts("[src] (player controlled) has appeared in [get_area(src)].")
|
||||
else if(ai_playercontrol_allowtype)
|
||||
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
|
||||
notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "<a href=?src=[UID()];activate=1>(Click to control)</a>", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK)
|
||||
@@ -342,7 +343,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/ObjBump(obj/O)
|
||||
if(istype(O, /obj/machinery/door/airlock))
|
||||
var/obj/machinery/door/airlock/L = O
|
||||
if(L.density)
|
||||
if(L.density) // must check density here, to avoid rapid bumping of an airlock that is in the process of opening, instantly forcing it closed
|
||||
return try_open_airlock(L)
|
||||
if(istype(O, /obj/machinery/door/firedoor))
|
||||
var/obj/machinery/door/firedoor/F = O
|
||||
@@ -352,7 +353,8 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext)
|
||||
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist)
|
||||
for(var/thing in GLOB.ts_spiderlist)
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(T.stat != DEAD)
|
||||
to_chat(T, "<span class='terrorspider'>TerrorSense: [msgtext]</span>")
|
||||
|
||||
@@ -365,21 +367,54 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/proc/try_open_airlock(obj/machinery/door/airlock/D)
|
||||
if(D.operating)
|
||||
return
|
||||
if(!D.density)
|
||||
to_chat(src, "<span class='warning'>Closing doors does not help us.</span>")
|
||||
else if(D.welded)
|
||||
to_chat(src, "<span class='warning'>The door is welded shut.</span>")
|
||||
if(D.welded)
|
||||
to_chat(src, "<span class='warning'>The door is welded.</span>")
|
||||
else if(D.locked)
|
||||
to_chat(src, "<span class='warning'>The door is bolted shut.</span>")
|
||||
to_chat(src, "<span class='warning'>The door is bolted.</span>")
|
||||
else if(D.allowed(src))
|
||||
D.open(1)
|
||||
return 1
|
||||
if(D.density)
|
||||
D.open(TRUE)
|
||||
else
|
||||
D.close(TRUE)
|
||||
return TRUE
|
||||
else if(D.arePowerSystemsOn() && (spider_opens_doors != 2))
|
||||
to_chat(src, "<span class='warning'>The door's motors resist your efforts to force it.</span>")
|
||||
else if(!spider_opens_doors)
|
||||
to_chat(src, "<span class='warning'>Your type of spider is not strong enough to force open doors.</span>")
|
||||
else
|
||||
visible_message("<span class='danger'>[src] pries open the door!</span>")
|
||||
visible_message("<span class='danger'>[src] forces the door!</span>")
|
||||
playsound(src.loc, "sparks", 100, 1)
|
||||
D.open(1)
|
||||
return 1
|
||||
if(D.density)
|
||||
D.open(TRUE)
|
||||
else
|
||||
D.close(TRUE)
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/Stat()
|
||||
..()
|
||||
// Determines what shows in the "Status" tab for player-controlled spiders. Used to help players understand spider health regeneration mechanics.
|
||||
// Uses <font color='#X'> because the status panel does NOT accept <span class='X'>.
|
||||
if(statpanel("Status") && ckey && stat == CONSCIOUS)
|
||||
if(degenerate)
|
||||
stat(null, "<font color='#eb4034'>Hivemind Connection Severed! Dying...</font>") // color=red
|
||||
return
|
||||
if(health != maxHealth)
|
||||
var/hp_points_per_second = 0
|
||||
var/ltext = "FAST"
|
||||
var/lcolor = "#fcba03" // orange
|
||||
var/secs_per_tick = (SSmobs.wait / 10) // This uses SSmobs.wait because it must use the same frequency as mobs are processed
|
||||
if(regen_points < (regen_points_per_hp * 2))
|
||||
// Slow regen speed: using regen_points as we get them. Figure out regen_points/sec, then convert that to hp/sec.
|
||||
var/regen_points_per_second = (regen_points_per_tick / secs_per_tick)
|
||||
hp_points_per_second = (regen_points_per_second / regen_points_per_hp)
|
||||
ltext = "SLOW (HUNGRY!)"
|
||||
lcolor = "#eb4034" // red
|
||||
else
|
||||
// Fast regen speed: healing at full 1 hp / tick rate. Just divide 1hp/tick by seconds/tick to get healing/sec.
|
||||
hp_points_per_second = 1 / secs_per_tick
|
||||
if(hp_points_per_second > 0)
|
||||
var/pc_of_max_per_second = round(((hp_points_per_second / maxHealth) * 100), 0.1)
|
||||
stat(null, "Regeneration: [ltext]: <font color='[lcolor]'>[num2text(pc_of_max_per_second)]% of health per second</font>")
|
||||
|
||||
|
||||
|
||||
@@ -478,6 +478,41 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
|
||||
return 0 //Unsupported slot
|
||||
//END HUMAN
|
||||
|
||||
/mob/proc/get_visible_mobs()
|
||||
var/list/seen_mobs = list()
|
||||
for(var/mob/M in view(src))
|
||||
seen_mobs += M
|
||||
|
||||
return seen_mobs
|
||||
|
||||
/**
|
||||
* Returns an assoc list which contains the mobs in range and their "visible" name.
|
||||
* Mobs out of view but in range will be listed as unknown. Else they will have their visible name
|
||||
*/
|
||||
/mob/proc/get_telepathic_targets()
|
||||
var/list/validtargets = new /list()
|
||||
var/turf/T = get_turf(src)
|
||||
var/list/mobs_in_view = get_visible_mobs()
|
||||
|
||||
for(var/mob/living/M in range(14, T))
|
||||
if(M && M.mind)
|
||||
if(M == src)
|
||||
continue
|
||||
var/mob_name
|
||||
if(M in mobs_in_view)
|
||||
mob_name = M.name
|
||||
else
|
||||
mob_name = "Unknown entity"
|
||||
var/i = 0
|
||||
var/result_name
|
||||
do
|
||||
result_name = mob_name
|
||||
if(i++)
|
||||
result_name += " ([i])" // Avoid dupes
|
||||
while(validtargets[result_name])
|
||||
validtargets[result_name] = M
|
||||
return validtargets
|
||||
|
||||
// If you're looking for `reset_perspective`, that's a synonym for this proc.
|
||||
/mob/proc/reset_perspective(atom/A)
|
||||
if(client)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user