Merge remote-tracking branch 'Paradise/master' into pr/dearmochi/14365

This commit is contained in:
AffectedArc07
2020-09-18 20:01:56 +01:00
95 changed files with 2141 additions and 1700 deletions
+16
View File
@@ -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 .
+13 -1
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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)
+13 -32
View File
@@ -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
+12 -6
View File
@@ -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)
.=..()
+9 -17
View File
@@ -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!
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+10 -14
View File
@@ -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
+13 -39
View File
@@ -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"
+3 -3
View File
@@ -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
+30 -29
View File
@@ -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
+5 -5
View File
@@ -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))
+2 -2
View File
@@ -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)
+8 -8
View File
@@ -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."
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
)
+2 -2
View File
@@ -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
+71 -66
View File
@@ -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)
+4
View File
@@ -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)
+1 -1
View File
@@ -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)"
+1 -1
View File
@@ -90,7 +90,7 @@
..()
if(climber)
climber.Weaken(2)
climber.visible_message("<span class='warning'>[climber.name] has been knocked off the table", "You've been knocked off the table", "You see [climber.name] get knocked off the table</span>")
climber.visible_message("<span class='warning'>[climber.name] has been knocked off the table", "You've been knocked off the table", "You hear [climber.name] get knocked off the table</span>")
else if(Adjacent(user) && user.pulling && user.pulling.pass_flags & PASSTABLE)
user.Move_Pulled(src)
if(user.pulling.loc == loc)
+11 -1
View File
@@ -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',
)
+4 -1
View File
@@ -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
+49 -4
View File
@@ -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
+12 -68
View File
@@ -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
+18 -158
View File
@@ -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>")
+13 -59
View File
@@ -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
+115 -33
View File
@@ -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
+8 -90
View File
@@ -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>")
+10 -74
View File
@@ -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>")
+7 -129
View File
@@ -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.")
+7 -11
View File
@@ -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
+8 -7
View File
@@ -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>")
+88 -86
View File
@@ -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)
+1 -1
View File
@@ -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
+6 -3
View File
@@ -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

-84
View File
@@ -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">&nbsp;</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;">&nbsp;{{:data.targetpressure}} kPa&nbsp;</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>
-46
View File
@@ -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:&nbsp;</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
View File
@@ -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"
+162 -28
View File
@@ -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;
+165 -87
View File
@@ -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"