mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 12:37:26 +01:00
Merge remote-tracking branch 'Paradise/master' into pr/dearmochi/14365
This commit is contained in:
@@ -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 .
|
||||
Vendored
+13
-1
@@ -1,5 +1,8 @@
|
||||
GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
|
||||
|
||||
/datum/repository/crew
|
||||
var/static/list/bold_jobs
|
||||
|
||||
/datum/repository/crew/New()
|
||||
cache_data = list()
|
||||
..()
|
||||
@@ -18,6 +21,13 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
|
||||
if(world.time < cache_entry.timestamp)
|
||||
return cache_entry.data
|
||||
|
||||
// Initialize the jobs here because in New(), GLOB.command_positions may not be inited yet
|
||||
if(!bold_jobs)
|
||||
bold_jobs = list()
|
||||
bold_jobs += GLOB.command_positions
|
||||
bold_jobs += get_all_centcom_jobs()
|
||||
bold_jobs += list("Nanotrasen Representative", "Blueshield", "Magistrate")
|
||||
|
||||
for(var/thing in GLOB.human_list)
|
||||
var/mob/living/carbon/human/H = thing
|
||||
var/obj/item/clothing/under/C = H.w_uniform
|
||||
@@ -32,11 +42,13 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new())
|
||||
crewmemberData["name"] = H.get_authentification_name(if_no_id="Unknown")
|
||||
crewmemberData["rank"] = H.get_authentification_rank(if_no_id="Unknown", if_no_job="No Job")
|
||||
crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job")
|
||||
crewmemberData["is_command"] = (crewmemberData["assignment"] in bold_jobs)
|
||||
|
||||
if(C.sensor_mode >= SUIT_SENSOR_BINARY)
|
||||
crewmemberData["dead"] = H.stat > UNCONSCIOUS
|
||||
crewmemberData["dead"] = H.stat == DEAD
|
||||
|
||||
if(C.sensor_mode >= SUIT_SENSOR_VITAL)
|
||||
crewmemberData["stat"] = H.stat
|
||||
crewmemberData["oxy"] = round(H.getOxyLoss(), 1)
|
||||
crewmemberData["tox"] = round(H.getToxLoss(), 1)
|
||||
crewmemberData["fire"] = round(H.getFireLoss(), 1)
|
||||
|
||||
+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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
|
||||
/// The maximum `target_pressure` you can set on the pump. Equates to about 1013.25 kPa.
|
||||
#define MAX_TARGET_PRESSURE 10 * ONE_ATMOSPHERE
|
||||
/// The pump will be siphoning gas.
|
||||
#define DIRECTION_IN 0
|
||||
/// The pump will be pumping gas out.
|
||||
#define DIRECTION_OUT 1
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump
|
||||
name = "Portable Air Pump"
|
||||
|
||||
icon = 'icons/obj/atmos.dmi'
|
||||
icon_state = "psiphon:0"
|
||||
density = 1
|
||||
|
||||
var/on = 0
|
||||
var/direction_out = 0 //0 = siphoning, 1 = releasing
|
||||
var/target_pressure = 100
|
||||
|
||||
var/pressuremin = 0
|
||||
var/pressuremax = 10 * ONE_ATMOSPHERE
|
||||
|
||||
density = TRUE
|
||||
volume = 1000
|
||||
/// If the pump is turned on or off.
|
||||
var/on = FALSE
|
||||
/// The direction the pump is operating in. This should be either `DIRECTION_IN` or `DIRECTION_OUT`.
|
||||
var/direction = DIRECTION_IN
|
||||
/// The desired pressure the pump should be outputting, either into the atmosphere, or into a holding tank.
|
||||
var/target_pressure = 101.325
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/update_icon()
|
||||
src.overlays = 0
|
||||
overlays = 0
|
||||
|
||||
if(on)
|
||||
icon_state = "psiphon:1"
|
||||
@@ -39,7 +44,7 @@
|
||||
on = !on
|
||||
|
||||
if(prob(100/severity))
|
||||
direction_out = !direction_out
|
||||
direction = !direction
|
||||
|
||||
target_pressure = rand(0,1300)
|
||||
update_icon()
|
||||
@@ -54,7 +59,7 @@
|
||||
environment = holding.air_contents
|
||||
else
|
||||
environment = loc.return_air()
|
||||
if(direction_out)
|
||||
if(direction == DIRECTION_OUT)
|
||||
var/pressure_delta = target_pressure - environment.return_pressure()
|
||||
//Can not have a pressure delta that would cause environment pressure > tank pressure
|
||||
|
||||
@@ -87,9 +92,7 @@
|
||||
air_update_turf()
|
||||
|
||||
air_contents.merge(removed)
|
||||
//src.update_icon()
|
||||
|
||||
src.updateDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/return_air()
|
||||
@@ -102,72 +105,74 @@
|
||||
if(on)
|
||||
on = FALSE
|
||||
update_icon()
|
||||
else if(on && holding && direction_out)
|
||||
else if(on && holding && direction == DIRECTION_OUT)
|
||||
investigate_log("[key_name(user)] started a transfer into [holding].<br>", "atmos")
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob)
|
||||
src.add_hiddenprint(user)
|
||||
return src.attack_hand(user)
|
||||
/obj/machinery/portable_atmospherics/pump/attack_ai(mob/user)
|
||||
add_hiddenprint(user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob)
|
||||
return src.attack_hand(user)
|
||||
/obj/machinery/portable_atmospherics/pump/attack_ghost(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob)
|
||||
ui_interact(user)
|
||||
/obj/machinery/portable_atmospherics/pump/attack_hand(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state)
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/portable_atmospherics/pump/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)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
// the ui does not exist, so we'll create a new() one
|
||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
||||
ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state)
|
||||
// open the new ui window
|
||||
ui = new(user, src, ui_key, "PortablePump", "Portable Pump", 434, 377, master_ui, state)
|
||||
ui.open()
|
||||
// auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
ui.set_autoupdate(TRUE)
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state)
|
||||
var/data[0]
|
||||
data["portConnected"] = connected_port ? 1 : 0
|
||||
data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0)
|
||||
data["targetpressure"] = round(target_pressure)
|
||||
data["pump_dir"] = direction_out
|
||||
data["minpressure"] = round(pressuremin)
|
||||
data["maxpressure"] = round(pressuremax)
|
||||
data["on"] = on ? 1 : 0
|
||||
|
||||
data["hasHoldingTank"] = holding ? 1 : 0
|
||||
/obj/machinery/portable_atmospherics/pump/tgui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"on" = on,
|
||||
"direction" = direction,
|
||||
"port_connected" = connected_port ? TRUE : FALSE,
|
||||
"max_target_pressure" = MAX_TARGET_PRESSURE,
|
||||
"target_pressure" = round(target_pressure, 0.001),
|
||||
"tank_pressure" = air_contents.return_pressure() > 0 ? round(air_contents.return_pressure(), 0.001) : 0
|
||||
)
|
||||
if(holding)
|
||||
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0))
|
||||
data["has_holding_tank"] = TRUE
|
||||
data["holding_tank"] = list("name" = holding.name, "tank_pressure" = holding.air_contents.return_pressure() > 0 ? round(holding.air_contents.return_pressure(), 0.001) : 0)
|
||||
else
|
||||
data["has_holding_tank"] = FALSE
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/Topic(href, href_list)
|
||||
/obj/machinery/portable_atmospherics/pump/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["power"])
|
||||
on = !on
|
||||
if(on && direction_out)
|
||||
investigate_log("[key_name(usr)] started a transfer into [holding].<br>", "atmos")
|
||||
update_icon()
|
||||
switch(action)
|
||||
if("power")
|
||||
on = !on
|
||||
if(on && direction == DIRECTION_OUT)
|
||||
investigate_log("[key_name(usr)] started a transfer into [holding].<br>", "atmos")
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if(href_list["direction"])
|
||||
direction_out = !direction_out
|
||||
if(on && holding)
|
||||
investigate_log("[key_name(usr)] started a transfer into [holding].<br>", "atmos")
|
||||
if("set_direction")
|
||||
if(text2num(params["direction"]) == DIRECTION_IN)
|
||||
direction = DIRECTION_IN
|
||||
else
|
||||
direction = DIRECTION_OUT
|
||||
if(on && holding)
|
||||
investigate_log("[key_name(usr)] started a transfer into [holding].<br>", "atmos")
|
||||
return TRUE
|
||||
|
||||
if(href_list["remove_tank"])
|
||||
if(holding)
|
||||
on = FALSE
|
||||
holding.loc = loc
|
||||
holding = null
|
||||
update_icon()
|
||||
if("remove_tank")
|
||||
if(holding)
|
||||
on = FALSE
|
||||
holding.forceMove(get_turf(src))
|
||||
holding = null
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if(href_list["pressure_adj"])
|
||||
var/diff = text2num(href_list["pressure_adj"])
|
||||
target_pressure = clamp(target_pressure+diff, pressuremin, pressuremax)
|
||||
update_icon()
|
||||
if("set_pressure")
|
||||
target_pressure = clamp(text2num(params["pressure"]), 0, MAX_TARGET_PRESSURE)
|
||||
return TRUE
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
add_fingerprint(usr)
|
||||
|
||||
@@ -130,6 +130,10 @@
|
||||
// adding a template with the key "mapHeader" replaces the map header content
|
||||
ui.add_template("mapHeader", "sec_camera_map_header.tmpl")
|
||||
|
||||
// Send nanomaps
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.send(user)
|
||||
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
|
||||
@@ -447,7 +447,7 @@
|
||||
/obj/machinery/vending/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)
|
||||
var/estimated_height = 100 + (length(product_records) * 34)
|
||||
var/estimated_height = 100 + min(length(product_records) * 34, 500)
|
||||
if(length(prices) > 0)
|
||||
estimated_height += 100 // to account for the "current user" interface
|
||||
ui = new(user, src, ui_key, "Vending", name, 470, estimated_height, master_ui, state)
|
||||
|
||||
@@ -230,6 +230,9 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
|
||||
if(!(freq in internal_channels))
|
||||
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])
|
||||
|
||||
/mob/proc/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses)
|
||||
@@ -606,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 = new /obj/item/encryptionkey/heads/ai_integrated //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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -262,7 +262,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
var/list/common_dirs = list(
|
||||
"nano/assets/",
|
||||
"nano/codemirror/",
|
||||
"nano/images/",
|
||||
"nano/layouts/"
|
||||
)
|
||||
var/list/uncommon_dirs = list(
|
||||
@@ -376,3 +375,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
|
||||
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
|
||||
)
|
||||
|
||||
// Nanomaps
|
||||
/datum/asset/simple/nanomaps
|
||||
// It REALLY doesnt matter too much if these arent up to date
|
||||
// They are relatively big
|
||||
verify = FALSE
|
||||
assets = list(
|
||||
"Cyberiad_nanomap_z1.png" = 'icons/_nanomaps/Cyberiad_nanomap_z1.png',
|
||||
"Delta_nanomap_z1.png" = 'icons/_nanomaps/Delta_nanomap_z1.png',
|
||||
"MetaStation_nanomap_z1.png" = 'icons/_nanomaps/MetaStation_nanomap_z1.png',
|
||||
)
|
||||
|
||||
@@ -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) )
|
||||
@@ -939,9 +974,19 @@
|
||||
var/datum/asset/tgui_assets = get_asset_datum(/datum/asset/simple/tgui)
|
||||
tgui_assets.register()
|
||||
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.register()
|
||||
|
||||
// Clear the user's cache so they get resent.
|
||||
// This is not fully clearing their BYOND cache, just their assets sent from the server this round
|
||||
cache = list()
|
||||
|
||||
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
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define SMART_FRIDGE_LOCK_SHORTED -1
|
||||
|
||||
/**
|
||||
* # Smart Fridge
|
||||
*
|
||||
@@ -23,8 +21,6 @@
|
||||
var/seconds_electrified = 0
|
||||
/// Whether the fridge should randomly shoot held items at a nearby living target or not.
|
||||
var/shoot_inventory = FALSE
|
||||
/// Whether the fridge is locked. Used for the secure variant of the fridge.
|
||||
var/locked = FALSE
|
||||
/// Whether the fridge requires ID scanning. Used for the secure variant of the fridge.
|
||||
var/scan_id = TRUE
|
||||
/// Whether the fridge is considered secure. Used for wiring and display.
|
||||
@@ -139,7 +135,7 @@
|
||||
|
||||
/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user)
|
||||
if(exchange_parts(user, O))
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
return
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
to_chat(user, "<span class='notice'>\The [src] is unpowered and useless.</span>")
|
||||
@@ -147,7 +143,7 @@
|
||||
|
||||
if(load(O, user))
|
||||
user.visible_message("<span class='notice'>[user] has added \the [O] to \the [src].</span>", "<span class='notice'>You add \the [O] to \the [src].</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
update_icon()
|
||||
else if(istype(O, /obj/item/storage/bag))
|
||||
var/obj/item/storage/bag/P = O
|
||||
@@ -157,7 +153,7 @@
|
||||
items_loaded++
|
||||
if(items_loaded)
|
||||
user.visible_message("<span class='notice'>[user] loads \the [src] with \the [P].</span>", "<span class='notice'>You load \the [src] with \the [P].</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
update_icon()
|
||||
var/failed = length(P.contents)
|
||||
if(failed)
|
||||
@@ -176,7 +172,7 @@
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
wires.Interact(user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
return ..()
|
||||
|
||||
//Drag pill bottle to fridge to empty it into the fridge
|
||||
@@ -198,33 +194,28 @@
|
||||
items_loaded++
|
||||
if(items_loaded)
|
||||
user.visible_message("<span class='notice'>[user] empties \the [P] into \the [src].</span>", "<span class='notice'>You empty \the [P] into \the [src].</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
update_icon()
|
||||
var/failed = length(P.contents)
|
||||
if(failed)
|
||||
to_chat(user, "<span class='notice'>[failed] item\s [failed == 1 ? "is" : "are"] refused.</span>")
|
||||
|
||||
// UI
|
||||
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
|
||||
/obj/machinery/smartfridge/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)
|
||||
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, "smartfridge.tmpl", name, 400, 500)
|
||||
ui = new(user, src, ui_key, "Smartfridge", name, 500, 500)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/smartfridge/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/smartfridge/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["contents"] = null
|
||||
data["electrified"] = seconds_electrified > 0
|
||||
data["shoot_inventory"] = shoot_inventory
|
||||
data["locked"] = locked
|
||||
data["secure"] = is_secure
|
||||
data["can_dry"] = can_dry
|
||||
data["drying"] = drying
|
||||
|
||||
var/list/items[0]
|
||||
var/list/items = list()
|
||||
for(var/i in 1 to length(item_quants))
|
||||
var/K = item_quants[i]
|
||||
var/count = item_quants[K]
|
||||
@@ -236,30 +227,31 @@
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/smartfridge/Topic(href, href_list)
|
||||
/obj/machinery/smartfridge/tgui_act(action, params)
|
||||
if(..())
|
||||
return FALSE
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
|
||||
var/mob/user = usr
|
||||
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
if(href_list["close"])
|
||||
user.unset_machine()
|
||||
ui.close()
|
||||
return FALSE
|
||||
switch(action)
|
||||
if("vend")
|
||||
if(is_secure && !emagged && scan_id && !allowed(usr)) //secure fridge check
|
||||
to_chat(usr, "<span class='warning'>Access denied.</span>")
|
||||
return FALSE
|
||||
|
||||
if(href_list["vend"])
|
||||
var/index = text2num(href_list["vend"])
|
||||
var/amount = text2num(href_list["amount"])
|
||||
if(isnull(index) || !ISINDEXSAFE(item_quants, index) || isnull(amount))
|
||||
return FALSE
|
||||
var/K = item_quants[index]
|
||||
var/count = item_quants[K]
|
||||
var/index = text2num(params["index"])
|
||||
var/amount = text2num(params["amount"])
|
||||
if(isnull(index) || !ISINDEXSAFE(item_quants, index) || isnull(amount))
|
||||
return FALSE
|
||||
var/K = item_quants[index]
|
||||
var/count = item_quants[K]
|
||||
if(count == 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible.
|
||||
return FALSE
|
||||
|
||||
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
|
||||
if(count > 0)
|
||||
item_quants[K] = max(count - amount, 0)
|
||||
|
||||
var/i = amount
|
||||
@@ -271,7 +263,6 @@
|
||||
adjust_item_drop_location(O)
|
||||
update_icon()
|
||||
break
|
||||
return TRUE
|
||||
else
|
||||
for(var/obj/O in contents)
|
||||
if(O.name == K)
|
||||
@@ -281,9 +272,7 @@
|
||||
i--
|
||||
if(i <= 0)
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Tries to load an item if it is accepted by [/obj/machinery/smartfridge/proc/accept_check].
|
||||
@@ -364,26 +353,12 @@
|
||||
|
||||
/obj/machinery/smartfridge/secure/emag_act(mob/user)
|
||||
emagged = TRUE
|
||||
locked = SMART_FRIDGE_LOCK_SHORTED
|
||||
to_chat(user, "<span class='notice'>You short out the product lock on \the [src].</span>")
|
||||
|
||||
/obj/machinery/smartfridge/secure/emp_act(severity)
|
||||
if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && prob(40 / severity))
|
||||
if(!emagged && prob(40 / severity))
|
||||
playsound(loc, 'sound/effects/sparks4.ogg', 60, TRUE)
|
||||
emagged = TRUE
|
||||
locked = SMART_FRIDGE_LOCK_SHORTED
|
||||
|
||||
/obj/machinery/smartfridge/secure/Topic(href, href_list)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return FALSE
|
||||
|
||||
if(href_list["vend"] && (usr.contents.Find(src) || Adjacent(usr)))
|
||||
if(!emagged && locked != SMART_FRIDGE_LOCK_SHORTED && scan_id && !allowed(usr))
|
||||
to_chat(usr, "<span class='warning'>Access denied.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* # Seed Storage
|
||||
@@ -672,23 +647,14 @@
|
||||
..()
|
||||
atmos_spawn_air(LINDA_SPAWN_HEAT)
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/Topic(href, href_list)
|
||||
if(..())
|
||||
return TRUE
|
||||
/obj/machinery/smartfridge/drying_rack/tgui_act(action, params)
|
||||
. = ..()
|
||||
|
||||
if(href_list["dryingOn"])
|
||||
drying = TRUE
|
||||
use_power = ACTIVE_POWER_USE
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
if(href_list["dryingOff"])
|
||||
drying = FALSE
|
||||
use_power = IDLE_POWER_USE
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
switch(action)
|
||||
if("drying")
|
||||
drying = !drying
|
||||
use_power = drying ? ACTIVE_POWER_USE : IDLE_POWER_USE
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/smartfridge/drying_rack/update_icon()
|
||||
..()
|
||||
@@ -741,15 +707,13 @@
|
||||
new dried(loc)
|
||||
item_quants[S.name]--
|
||||
qdel(S)
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
return TRUE
|
||||
for(var/obj/item/stack/sheet/wetleather/WL in contents)
|
||||
var/obj/item/stack/sheet/leather/L = new(loc)
|
||||
L.amount = WL.amount
|
||||
item_quants[WL.name]--
|
||||
qdel(WL)
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
#undef SMART_FRIDGE_LOCK_SHORTED
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -131,7 +131,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
update_headlamp()
|
||||
|
||||
radio = new /obj/item/radio/borg(src)
|
||||
radio.recalculateChannels()
|
||||
common_radio = radio
|
||||
|
||||
init(alien, connect_to_AI, ai_to_sync_to)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 900, 800, state = state)
|
||||
ui.add_template("mapContent", "atmos_control_map_content.tmpl")
|
||||
ui.add_template("mapHeader", "atmos_control_map_header.tmpl")
|
||||
|
||||
// Send nanomaps
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.send(user)
|
||||
|
||||
ui.set_show_map(1)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
@@ -314,7 +314,7 @@
|
||||
/obj/item/paper/contract/infernal/magic/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0)
|
||||
if(!istype(user) || !user.mind)
|
||||
return -1
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null))
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null))
|
||||
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -128,6 +128,14 @@
|
||||
origin_tech = "materials=5;programming=4;biotech=5"
|
||||
var/stun_max_amount = 2
|
||||
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened
|
||||
name = "Hardened CNS Rebooter implant"
|
||||
emp_proof = TRUE
|
||||
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened/Initialize(mapload)
|
||||
. = ..()
|
||||
desc += " The implant has been hardened. It is invulnerable to EMPs."
|
||||
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/on_life()
|
||||
..()
|
||||
if(crit_fail)
|
||||
@@ -138,6 +146,7 @@
|
||||
owner.SetWeakened(stun_max_amount)
|
||||
|
||||
/obj/item/organ/internal/cyberimp/brain/anti_stun/emp_act(severity)
|
||||
..()
|
||||
if(crit_fail || emp_proof)
|
||||
return
|
||||
crit_fail = TRUE
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
/datum/tgui_module/crew_monitor/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)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
// The 557 may seem random, but its the perfectsize for margins on the nanomap
|
||||
ui = new(user, src, ui_key, "CrewMonitor", name, 1400, 557, master_ui, state)
|
||||
ui.autoupdate = TRUE
|
||||
ui = new(user, src, ui_key, "CrewMonitor", name, 800, 600, master_ui, state)
|
||||
|
||||
// Send nanomaps
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.send(user)
|
||||
|
||||
ui.open()
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 714 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 929 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 661 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.9 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.0 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.4 MiB |
@@ -1,84 +0,0 @@
|
||||
<h3>Pump Status</h3>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Tank Pressure:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(data.tankPressure)}} kPa
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Port Status:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:data.portConnected ? '<span class="good">Connected</span>' : '<span class="average">Disconnected</span>'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Holding Tank Status</h3>
|
||||
{{if data.hasHoldingTank}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Tank Label:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
<div style="float: left; width: 180px;">{{:data.holdingTank.name}}</div> {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Tank Pressure:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="item"><span class="average"><i>No holding tank inserted.</i></span></div>
|
||||
<div class="item"> </div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
<h3>Power Regulator Status</h3>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Target Pressure:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}}
|
||||
<div style="clear: both; padding-top: 4px;">
|
||||
{{:helper.link('-', null, {'pressure_adj' : -1000}, (data.targetpressure > data.minpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}}
|
||||
<div style="float: left; width: 80px; text-align: center;"> {{:data.targetpressure}} kPa </div>
|
||||
{{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}}
|
||||
{{:helper.link('+', null, {'pressure_adj' : 1000}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Power Switch:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.link('On', 'unlock', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'lock', {'power' : 1}, data.on ? null : 'selected')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Pump Direction:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:helper.link('Out', 'arrow-right', {'direction' : 1}, data.pump_dir ? 'selected' : null)}}{{:helper.link('In', 'arrow-left', {'direction' : 1}, data.pump_dir ? null : 'selected')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<div class='item'>
|
||||
{{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}}
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<h2>Storage</h2>
|
||||
{{if data.secure}}
|
||||
<span class='notice'>
|
||||
{{:data.locked == -1 ? "Sec.re ACC_** //):securi_nt.diag=>##'or 1=1'%($..." : "Secure Access: Please have your identification ready."}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="item">
|
||||
{{if data.can_dry}}
|
||||
<div class="itemLabel" style="width: 70%">
|
||||
Drying:
|
||||
</div>
|
||||
<div class="itemContent" style="width: 30%;">
|
||||
{{:helper.link('On', 'power-off', {'dryingOn' : 1}, data.drying ? 'selected' : null)}}{{:helper.link('Off', 'close', {'dryingOff' : 1}, data.drying ? null : 'selected')}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='item'>
|
||||
{{if data.contents}}
|
||||
{{for data.contents}}
|
||||
<div class='item'>
|
||||
<span class='highlight'>{{:value.display_name}} ({{:value.quantity}} available)</span>
|
||||
<div class='line' style="float: left;">Vend: </div> {{:helper.link('x1', 'arrow-circle-down', { "vend" : value.vend, "amount" : 1 }, null, 'statusValue')}}
|
||||
{{if value.quantity >= 5}}
|
||||
{{:helper.link('x5', 'arrow-circle-down', { "vend" : value.vend, "amount" : 5 }, null, 'statusValue')}}
|
||||
{{/if}}
|
||||
{{if value.quantity >= 10}}
|
||||
{{:helper.link('x10', 'arrow-circle-down', { "vend" : value.vend, "amount" : 10 }, null, 'statusValue')}}
|
||||
{{/if}}
|
||||
{{if value.quantity >= 25}}
|
||||
{{:helper.link('x25', 'arrow-circle-down', { "vend" : value.vend, "amount" : 25 }, null, 'statusValue')}}
|
||||
{{/if}}
|
||||
{{if value.quantity > 1}}
|
||||
{{:helper.link('All', 'arrow-circle-down', { "vend" : value.vend, "amount" : value.quantity }, null, 'statusValue')}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/for}}
|
||||
{{else}}
|
||||
<span class='average'>No products loaded.</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
+23
-1
@@ -50,6 +50,7 @@
|
||||
#include "code\__DEFINES\lighting.dm"
|
||||
#include "code\__DEFINES\logs.dm"
|
||||
#include "code\__DEFINES\machines.dm"
|
||||
#include "code\__DEFINES\martial_arts.dm"
|
||||
#include "code\__DEFINES\math.dm"
|
||||
#include "code\__DEFINES\MC.dm"
|
||||
#include "code\__DEFINES\mecha.dm"
|
||||
@@ -1664,7 +1665,28 @@
|
||||
#include "code\modules\martial_arts\mimejutsu.dm"
|
||||
#include "code\modules\martial_arts\plasma_fist.dm"
|
||||
#include "code\modules\martial_arts\sleeping_carp.dm"
|
||||
#include "code\modules\martial_arts\wrestleing.dm"
|
||||
#include "code\modules\martial_arts\wrestling.dm"
|
||||
#include "code\modules\martial_arts\combos\martial_combo.dm"
|
||||
#include "code\modules\martial_arts\combos\adminfu\healing_palm.dm"
|
||||
#include "code\modules\martial_arts\combos\cqc\consecutive.dm"
|
||||
#include "code\modules\martial_arts\combos\cqc\kick.dm"
|
||||
#include "code\modules\martial_arts\combos\cqc\pressure.dm"
|
||||
#include "code\modules\martial_arts\combos\cqc\restrain.dm"
|
||||
#include "code\modules\martial_arts\combos\cqc\slam.dm"
|
||||
#include "code\modules\martial_arts\combos\krav_maga\leg_sweep.dm"
|
||||
#include "code\modules\martial_arts\combos\krav_maga\lung_punch.dm"
|
||||
#include "code\modules\martial_arts\combos\krav_maga\neck_chop.dm"
|
||||
#include "code\modules\martial_arts\combos\mimejutsu\mimechucks.dm"
|
||||
#include "code\modules\martial_arts\combos\mimejutsu\silent_palm.dm"
|
||||
#include "code\modules\martial_arts\combos\mimejutsu\smokebomb.dm"
|
||||
#include "code\modules\martial_arts\combos\plasma_fist\plasma_fist.dm"
|
||||
#include "code\modules\martial_arts\combos\plasma_fist\throwback.dm"
|
||||
#include "code\modules\martial_arts\combos\plasma_fist\tornado_sweep.dm"
|
||||
#include "code\modules\martial_arts\combos\sleeping_carp\back_kick.dm"
|
||||
#include "code\modules\martial_arts\combos\sleeping_carp\elbow_drop.dm"
|
||||
#include "code\modules\martial_arts\combos\sleeping_carp\head_kick.dm"
|
||||
#include "code\modules\martial_arts\combos\sleeping_carp\stomach_knee.dm"
|
||||
#include "code\modules\martial_arts\combos\sleeping_carp\wrist_wrench.dm"
|
||||
#include "code\modules\mining\abandonedcrates.dm"
|
||||
#include "code\modules\mining\fulton.dm"
|
||||
#include "code\modules\mining\machine_processing.dm"
|
||||
|
||||
@@ -1,45 +1,179 @@
|
||||
import { Component } from 'inferno';
|
||||
import { Box, Icon, Tooltip } from '.';
|
||||
import { useBackend } from "../backend";
|
||||
import { LabeledList } from './LabeledList';
|
||||
import { Slider } from './Slider';
|
||||
|
||||
export const NanoMap = (props, context) => {
|
||||
const { config } = useBackend(context);
|
||||
const { onClick } = props;
|
||||
return (
|
||||
<Box className="NanoMap__container">
|
||||
<Box
|
||||
as="img"
|
||||
src={config.map+"_nanomap_z1.png"}
|
||||
style={{
|
||||
width: '512px',
|
||||
height: '512px',
|
||||
}}
|
||||
onClick={onClick} />
|
||||
</Box>
|
||||
);
|
||||
const pauseEvent = e => {
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = false;
|
||||
return false;
|
||||
};
|
||||
|
||||
const NanoMapMarker = props => {
|
||||
export class NanoMap extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Auto center based on window size
|
||||
const Xcenter = (window.innerWidth / 2) - 256;
|
||||
const Ycenter = (window.innerHeight / 2) - 256;
|
||||
|
||||
this.state = {
|
||||
offsetX: 128,
|
||||
offsetY: 48,
|
||||
transform: 'none',
|
||||
dragging: false,
|
||||
originX: null,
|
||||
originY: null,
|
||||
zoom: 1,
|
||||
};
|
||||
|
||||
// Dragging
|
||||
this.handleDragStart = e => {
|
||||
this.ref = e.target;
|
||||
this.setState({
|
||||
dragging: false,
|
||||
originX: e.screenX,
|
||||
originY: e.screenY,
|
||||
});
|
||||
document.addEventListener('mousemove', this.handleDragMove);
|
||||
document.addEventListener('mouseup', this.handleDragEnd);
|
||||
pauseEvent(e);
|
||||
};
|
||||
|
||||
this.handleDragMove = e => {
|
||||
this.setState(prevState => {
|
||||
const state = { ...prevState };
|
||||
const newOffsetX = e.screenX - state.originX;
|
||||
const newOffsetY = e.screenY - state.originY;
|
||||
if (prevState.dragging) {
|
||||
state.offsetX += newOffsetX;
|
||||
state.offsetY += newOffsetY;
|
||||
state.originX = e.screenX;
|
||||
state.originY = e.screenY;
|
||||
} else {
|
||||
state.dragging = true;
|
||||
}
|
||||
return state;
|
||||
});
|
||||
pauseEvent(e);
|
||||
};
|
||||
|
||||
this.handleDragEnd = e => {
|
||||
this.setState({
|
||||
dragging: false,
|
||||
originX: null,
|
||||
originY: null,
|
||||
});
|
||||
document.removeEventListener('mousemove', this.handleDragMove);
|
||||
document.removeEventListener('mouseup', this.handleDragEnd);
|
||||
pauseEvent(e);
|
||||
};
|
||||
|
||||
this.handleZoom = (_e, value) => {
|
||||
this.setState(state => {
|
||||
const newZoom = Math.min(Math.max(value, 1), 8);
|
||||
let zoomDiff = (newZoom - state.zoom) * 1.5;
|
||||
state.zoom = newZoom;
|
||||
state.offsetX = state.offsetX - 262 * zoomDiff;
|
||||
state.offsetY = state.offsetY - 256 * zoomDiff;
|
||||
if (props.onZoom) {
|
||||
props.onZoom(state.zoom);
|
||||
}
|
||||
return state;
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { config } = useBackend(this.context);
|
||||
const { dragging, offsetX, offsetY, zoom = 1 } = this.state;
|
||||
const { children } = this.props;
|
||||
|
||||
const mapUrl = config.map + "_nanomap_z1.png?" + Date.now();
|
||||
const mapSize = (510 * zoom) + 'px';
|
||||
const newStyle = {
|
||||
width: mapSize,
|
||||
height: mapSize,
|
||||
"margin-top": offsetY + "px",
|
||||
"margin-left": offsetX + "px",
|
||||
"overflow": "hidden",
|
||||
"position": "relative",
|
||||
"background-image": "url(" + mapUrl + ")",
|
||||
"background-size": "cover",
|
||||
"background-repeat": "no-repeat",
|
||||
"text-align": "center",
|
||||
"cursor": dragging ? "move" : "auto",
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className="NanoMap__container">
|
||||
<Box
|
||||
style={newStyle}
|
||||
textAlign="center"
|
||||
onMouseDown={this.handleDragStart}>
|
||||
<Box>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
<NanoMapZoomer zoom={zoom} onZoom={this.handleZoom} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const NanoMapMarker = (props, context) => {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
zoom = 1,
|
||||
icon,
|
||||
tooltip,
|
||||
color,
|
||||
} = props;
|
||||
const rx = ((x * 2 * zoom) - zoom) - 3;
|
||||
const ry = ((y * 2 * zoom) - zoom) - 3;
|
||||
return (
|
||||
<Box
|
||||
position="absolute"
|
||||
className="NanoMap__marker"
|
||||
top={(((255 - y) * 2) + 2) + 'px'}
|
||||
left={((x * 2) + 2) + 'px'}>
|
||||
<Icon
|
||||
name={icon}
|
||||
color={color}
|
||||
size={0.5}
|
||||
/>
|
||||
<Tooltip content={tooltip} />
|
||||
</Box>
|
||||
<div>
|
||||
<Box
|
||||
position="absolute"
|
||||
className="NanoMap__marker"
|
||||
lineHeight="0"
|
||||
bottom={ry + "px"}
|
||||
left={rx + "px"}>
|
||||
<Icon
|
||||
name={icon}
|
||||
color={color}
|
||||
fontSize="6px"
|
||||
/>
|
||||
<Tooltip content={tooltip} />
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
NanoMap.Marker = NanoMapMarker;
|
||||
|
||||
const NanoMapZoomer = (props, context) => {
|
||||
return (
|
||||
<Box className="NanoMap__zoomer">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Zoom">
|
||||
<Slider
|
||||
minValue="1"
|
||||
maxValue="8"
|
||||
stepPixelSize="10"
|
||||
format={v => v + "x"}
|
||||
value={props.zoom}
|
||||
onDrag={(e, v) => props.onZoom(e, v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
NanoMap.Zoomer = NanoMapZoomer;
|
||||
|
||||
@@ -1,100 +1,178 @@
|
||||
import { sortBy } from 'common/collections';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, NanoMap, Table } from "../components";
|
||||
import { createSearch } from 'common/string';
|
||||
import { useBackend, useLocalState } from "../backend";
|
||||
import { Box, Button, Icon, Input, NanoMap, Table, Tabs } from "../components";
|
||||
import { TableCell } from '../components/Table';
|
||||
import { COLORS } from '../constants.js';
|
||||
import { Window } from "../layouts";
|
||||
|
||||
const getStatText = cm => {
|
||||
if (cm.dead) {
|
||||
return "Deceased";
|
||||
}
|
||||
if (parseInt(cm.stat, 10) === 1) { // Unconscious
|
||||
return "Unconscious";
|
||||
}
|
||||
return "Living";
|
||||
};
|
||||
|
||||
const getStatColor = cm => {
|
||||
if (cm.dead) {
|
||||
return "red";
|
||||
}
|
||||
if (parseInt(cm.stat, 10) === 1) { // Unconscious
|
||||
return "orange";
|
||||
}
|
||||
return "green";
|
||||
};
|
||||
|
||||
export const CrewMonitor = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const crew = sortBy(
|
||||
crewmember => crewmember.name,
|
||||
)(data.crewmembers || []);
|
||||
const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
|
||||
const decideTab = index => {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return <CrewMonitorDataView />;
|
||||
case 1:
|
||||
return <CrewMonitorMapView />;
|
||||
default:
|
||||
return "WE SHOULDN'T BE HERE!";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content className="Layout__content--noMargin">
|
||||
<Box m={1}>
|
||||
{crew.filter(x => x.sensor_type === 3).map(crewmember => (
|
||||
<NanoMap.Marker
|
||||
key={crewmember.ref}
|
||||
x={crewmember.x}
|
||||
y={crewmember.y}
|
||||
icon="circle"
|
||||
tooltip={crewmember.name}
|
||||
color={crewmember.dead ? 'red' : 'green'}
|
||||
/>
|
||||
))}
|
||||
<NanoMap />
|
||||
</Box>
|
||||
<Box className="NanoMap__contentOffset">
|
||||
<Box bold m={2}>
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell>
|
||||
Name
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Status
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Location
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{crew.map(crewmember => (
|
||||
<Table.Row key={crewmember.name}>
|
||||
<TableCell>
|
||||
{crewmember.name} ({crewmember.assignment})
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box inline
|
||||
color={crewmember.dead ? 'red' : 'green'}>
|
||||
{crewmember.dead ? 'Deceased' : 'Living'}
|
||||
</Box>
|
||||
{crewmember.sensor_type >= 2 ? (
|
||||
<Box inline>
|
||||
{'('}
|
||||
<Box inline
|
||||
color="red">
|
||||
{crewmember.brute}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color="orange">
|
||||
{crewmember.fire}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color="green">
|
||||
{crewmember.tox}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color="blue">
|
||||
{crewmember.oxy}
|
||||
</Box>
|
||||
{')'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{crewmember.sensor_type === 3 ? (
|
||||
data.isAI ? (
|
||||
<Button fluid
|
||||
content={crewmember.area+" ("
|
||||
+crewmember.x+", "+crewmember.y+")"}
|
||||
onClick={() => act('track', {
|
||||
track: crewmember.ref,
|
||||
})} />
|
||||
) : (
|
||||
crewmember.area+" ("+crewmember.x+", "+crewmember.y+")"
|
||||
)
|
||||
) : "Not Available"}
|
||||
</TableCell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Box>
|
||||
<Window.Content>
|
||||
<Box fillPositionedParent>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
key="DataView"
|
||||
selected={0 === tabIndex}
|
||||
onClick={() => setTabIndex(0)}>
|
||||
<Icon name="table" /> Data View
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="MapView"
|
||||
selected={1 === tabIndex}
|
||||
onClick={() => setTabIndex(1)}>
|
||||
<Icon name="map-marked-alt" /> Map View
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
{decideTab(tabIndex)}
|
||||
</Box>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const CrewMonitorDataView = (_properties, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const crew = sortBy(
|
||||
cm => cm.name,
|
||||
)(data.crewmembers || []);
|
||||
const [
|
||||
search,
|
||||
setSearch,
|
||||
] = useLocalState(context, 'search', '');
|
||||
const searcher = createSearch(search, cm => {
|
||||
return cm.name + "|" + cm.assignment + "|" + cm.area;
|
||||
});
|
||||
return (
|
||||
<Box>
|
||||
<Input
|
||||
placeholder="Search by name, assignment or location.."
|
||||
width="100%"
|
||||
onInput={(_e, value) => setSearch(value)}
|
||||
/>
|
||||
<Table m="0.5rem">
|
||||
<Table.Row header>
|
||||
<Table.Cell>
|
||||
Name
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Status
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
Location
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{crew.filter(searcher).map(cm => (
|
||||
<Table.Row key={cm.name} bold={!!cm.is_command}>
|
||||
<TableCell>
|
||||
{cm.name} ({cm.assignment})
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box inline
|
||||
color={getStatColor(cm)}>
|
||||
{getStatText(cm)}
|
||||
</Box>
|
||||
{cm.sensor_type >= 2 ? (
|
||||
<Box inline>
|
||||
{'('}
|
||||
<Box inline
|
||||
color={COLORS.damageType.oxy}>
|
||||
{cm.oxy}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color={COLORS.damageType.toxin}>
|
||||
{cm.tox}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color={COLORS.damageType.burn}>
|
||||
{cm.fire}
|
||||
</Box>
|
||||
{'|'}
|
||||
<Box inline
|
||||
color={COLORS.damageType.brute}>
|
||||
{cm.brute}
|
||||
</Box>
|
||||
{')'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{cm.sensor_type === 3 ? (
|
||||
data.isAI ? (
|
||||
<Button fluid
|
||||
icon="location-arrow"
|
||||
content={
|
||||
cm.area + " (" + cm.x + ", " + cm.y + ")"
|
||||
}
|
||||
onClick={() => act('track', {
|
||||
track: cm.ref,
|
||||
})} />
|
||||
) : (
|
||||
cm.area + " (" + cm.x + ", " + cm.y + ")"
|
||||
)
|
||||
) : "Not Available"}
|
||||
</TableCell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const CrewMonitorMapView = (_properties, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const [zoom, setZoom] = useLocalState(context, 'zoom', 1);
|
||||
return (
|
||||
<Box height="526px" mb="0.5rem" overflow="hidden">
|
||||
<NanoMap onZoom={v => setZoom(v)}>
|
||||
{data.crewmembers.filter(x => x.sensor_type === 3).map(cm => (
|
||||
<NanoMap.Marker
|
||||
key={cm.ref}
|
||||
x={cm.x}
|
||||
y={cm.y}
|
||||
zoom={zoom}
|
||||
icon="circle"
|
||||
tooltip={cm.name + " (" + cm.assignment + ")"}
|
||||
color={getStatColor(cm)}
|
||||
/>
|
||||
))}
|
||||
</NanoMap>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
import { useBackend } from "../backend";
|
||||
import { Button, Section, LabeledList, Slider, Box, ProgressBar, Flex } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const PortablePump = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { has_holding_tank } = data;
|
||||
|
||||
return (
|
||||
<Window resizable>
|
||||
<Window.Content>
|
||||
<PumpSettings />
|
||||
<PressureSettings />
|
||||
{has_holding_tank ? (
|
||||
<HoldingTank />
|
||||
) : (
|
||||
<Section title="Holding Tank">
|
||||
<Box color="average" bold={1}>
|
||||
No Holding Tank Inserted.
|
||||
</Box>
|
||||
</Section>
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const PumpSettings = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
on,
|
||||
direction,
|
||||
port_connected,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Pump Settings">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Power">
|
||||
<Button
|
||||
icon={on ? "power-off" : "power-off"}
|
||||
content={on ? "On" : "Off"}
|
||||
color={on ? null : "red"}
|
||||
selected={on}
|
||||
onClick={() => act('power')} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Pump Direction">
|
||||
<Box
|
||||
mt={0.5}
|
||||
mb={1}>
|
||||
<Button
|
||||
icon="sign-in-alt"
|
||||
content="In"
|
||||
selected={!direction}
|
||||
width={3.75}
|
||||
onClick={() => act('set_direction', {
|
||||
direction: 0,
|
||||
})} />
|
||||
<Button
|
||||
icon="sign-out-alt"
|
||||
content="Out"
|
||||
selected={direction}
|
||||
onClick={() => act('set_direction', {
|
||||
direction: 1,
|
||||
})} />
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Port status">
|
||||
<Box
|
||||
color={port_connected ? "green" : "average"}
|
||||
bold={1}>
|
||||
{port_connected ? "Connected" : "Disconnected"}
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const PressureSettings = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
tank_pressure,
|
||||
target_pressure,
|
||||
max_target_pressure,
|
||||
} = data;
|
||||
|
||||
const average_pressure = max_target_pressure * 0.70;
|
||||
const bad_pressure = max_target_pressure * 0.25;
|
||||
|
||||
return (
|
||||
<Section title="Pressure Settings">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Stored pressure">
|
||||
<ProgressBar
|
||||
value={tank_pressure}
|
||||
minValue={0}
|
||||
maxValue={max_target_pressure}
|
||||
ranges={{
|
||||
good: [average_pressure, Infinity],
|
||||
average: [bad_pressure, average_pressure],
|
||||
bad: [-Infinity, bad_pressure],
|
||||
}}>
|
||||
{tank_pressure} kPa
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Flex mt={2}>
|
||||
<Flex.Item
|
||||
mt={0.4}
|
||||
grow={1}
|
||||
color="label">
|
||||
Target pressure:
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
icon="undo"
|
||||
mr={0.5}
|
||||
width={2.2}
|
||||
textAlign="center"
|
||||
onClick={() => act('set_pressure', {
|
||||
pressure: 101.325,
|
||||
})} />
|
||||
<Button
|
||||
icon="fast-backward"
|
||||
mr={0.5}
|
||||
width={2.2}
|
||||
textAlign="center"
|
||||
onClick={() => act('set_pressure', {
|
||||
pressure: 0,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Slider
|
||||
animated
|
||||
unit="kPa"
|
||||
width={17.3}
|
||||
stepPixelSize={0.22}
|
||||
minValue={0}
|
||||
maxValue={max_target_pressure}
|
||||
value={target_pressure}
|
||||
onChange={(e, value) => act('set_pressure', {
|
||||
pressure: value,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Button
|
||||
icon="fast-forward"
|
||||
ml={0.5}
|
||||
width={2.2}
|
||||
textAlign="center"
|
||||
onClick={() => act('set_pressure', {
|
||||
pressure: max_target_pressure,
|
||||
})} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const HoldingTank = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
holding_tank,
|
||||
max_target_pressure,
|
||||
} = data;
|
||||
|
||||
const average_pressure = max_target_pressure * 0.70;
|
||||
const bad_pressure = max_target_pressure * 0.25;
|
||||
|
||||
return (
|
||||
<Section
|
||||
title="Holding Tank"
|
||||
buttons={
|
||||
<Button
|
||||
onClick={() => act('remove_tank')}
|
||||
icon="eject">
|
||||
Eject
|
||||
</Button>
|
||||
}>
|
||||
<Flex>
|
||||
<Flex.Item
|
||||
color="label"
|
||||
mr={7.2}
|
||||
mb={2.2}>
|
||||
Tank Label:
|
||||
</Flex.Item>
|
||||
<Flex.Item mb={1} color="silver">
|
||||
{holding_tank.name}
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Flex.Item
|
||||
color="label"
|
||||
mt={0.5}
|
||||
mr={3.8}>
|
||||
Tank Pressure:
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1}>
|
||||
<ProgressBar
|
||||
value={holding_tank.tank_pressure}
|
||||
minValue={0}
|
||||
maxValue={max_target_pressure}
|
||||
ranges={{
|
||||
good: [average_pressure, Infinity],
|
||||
average: [bad_pressure, average_pressure],
|
||||
bad: [-Infinity, bad_pressure],
|
||||
}}>
|
||||
{holding_tank.tank_pressure} kPa
|
||||
</ProgressBar>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Section, Button, NumberInput, Flex, NoticeBox } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const Smartfridge = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
secure, // secure fridge notice
|
||||
can_dry, // dry section
|
||||
drying, // drying rack on/off.
|
||||
contents,
|
||||
} = data;
|
||||
return (
|
||||
<Window>
|
||||
<Window.Content>
|
||||
{!!secure && (
|
||||
<Section title="Secure">
|
||||
<NoticeBox>
|
||||
Secure Access: Please have your identification ready.
|
||||
</NoticeBox>
|
||||
</Section>
|
||||
)}
|
||||
{!!can_dry && (
|
||||
<Section title="Drying rack">
|
||||
<Button
|
||||
icon={drying ? 'power-off' : 'times'}
|
||||
content={drying ? 'On' : 'Off'}
|
||||
selected={drying}
|
||||
onClick={() => act('drying')} />
|
||||
</Section>
|
||||
)}
|
||||
<Section title="Contents">
|
||||
{!contents && (
|
||||
<Box color="average"> No products loaded. </Box>
|
||||
)}
|
||||
{!!contents && contents.map(item => {
|
||||
return (
|
||||
<Flex direction="row" key={item}>
|
||||
<Flex.Item width="45%">
|
||||
{item.display_name}
|
||||
</Flex.Item>
|
||||
<Flex.Item width="25%">
|
||||
({item.quantity} in stock)
|
||||
</Flex.Item>
|
||||
<Flex.Item width="30%">
|
||||
<Button
|
||||
icon="arrow-down"
|
||||
tooltip="Dispense one."
|
||||
content="1"
|
||||
onClick={() => act('vend',
|
||||
{ index: item.vend, amount: 1 })} />
|
||||
<NumberInput
|
||||
width="40px"
|
||||
minValue={0}
|
||||
value={0}
|
||||
maxValue={item.quantity}
|
||||
step={1}
|
||||
stepPixelSize={3}
|
||||
onDrag={(e, value) => act('vend',
|
||||
{ index: item.vend, amount: value })} />
|
||||
<Button
|
||||
icon="arrow-down"
|
||||
content="All"
|
||||
tooltip="Dispense all. "
|
||||
onClick={() => act('vend',
|
||||
{ index: item.vend, amount: item.quantity })} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,20 +1,23 @@
|
||||
$color-background: rgba(0, 0, 0, 0.33) !default;
|
||||
|
||||
.NanoMap__contentOffset {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 524px;
|
||||
right: 0;
|
||||
background-color: $color-background;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.NanoMap__container {
|
||||
position: absolute;
|
||||
overflow: hiddden;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.NanoMap__marker {
|
||||
z-index: 10;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.NanoMap__zoomer {
|
||||
z-index: 20;
|
||||
background-color: $color-background;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
padding: 0.5rem;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,14 +1,14 @@
|
||||
#!/bin/bash
|
||||
# Generate maps
|
||||
tools/github-actions/nanomap-renderer minimap "./_maps/map_files/cyberiad/cyberiad.dmm"
|
||||
tools/github-actions/nanomap-renderer minimap "./_maps/map_files/Delta/delta.dmm"
|
||||
tools/github-actions/nanomap-renderer minimap "./_maps/map_files/MetaStation/MetaStation.v41A.II.dmm"
|
||||
tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/cyberiad/cyberiad.dmm"
|
||||
tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/Delta/delta.dmm"
|
||||
tools/github-actions/nanomap-renderer minimap -w 2040 -h 2040 "./_maps/map_files/MetaStation/MetaStation.v41A.II.dmm"
|
||||
# Move and rename files so the game understands them
|
||||
cd "data/nanomaps"
|
||||
mv "cyberiad_nanomap_z1.png" "Cyberiad_nanomap_z1.png"
|
||||
mv "delta_nanomap_z1.png" "Delta_nanomap_z1.png"
|
||||
mv "MetaStation.v41A.II_nanomap_z1.png" "MetaStation_nanomap_z1.png"
|
||||
cd "../../"
|
||||
cp "data/nanomaps/Cyberiad_nanomap_z1.png" "nano/images"
|
||||
cp "data/nanomaps/Delta_nanomap_z1.png" "nano/images"
|
||||
cp "data/nanomaps/MetaStation_nanomap_z1.png" "nano/images"
|
||||
cp "data/nanomaps/Cyberiad_nanomap_z1.png" "icons/_nanomaps"
|
||||
cp "data/nanomaps/Delta_nanomap_z1.png" "icons/_nanomaps"
|
||||
cp "data/nanomaps/MetaStation_nanomap_z1.png" "icons/_nanomaps"
|
||||
|
||||
Reference in New Issue
Block a user