mirror of
https://github.com/goonstation/goonstation-2016.git
synced 2026-08-26 23:46:12 +01:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,874 @@
|
||||
/datum/abilityHolder
|
||||
var/help_mode = 0
|
||||
var/list/abilities = list()
|
||||
var/list/suspended = list()
|
||||
var/locked = 0
|
||||
|
||||
var/topBarRendered = 0
|
||||
var/rendered = 1
|
||||
var/datum/targetable/shiftPower = null
|
||||
var/datum/targetable/ctrlPower = null
|
||||
var/datum/targetable/altPower = null
|
||||
|
||||
var/usesPoints = 1
|
||||
var/pointName = ""
|
||||
var/notEnoughPointsMessage = "<span style=\"color:red\">You do not have enough points to use that ability.</span>"
|
||||
var/points = 0 //starting points
|
||||
var/regenRate = 1 //starting regen
|
||||
var/bonus = 0
|
||||
var/lastBonus = 0
|
||||
var/tabName = "Spells"
|
||||
|
||||
var/mob/owner = null
|
||||
|
||||
New(var/mob/M)
|
||||
owner = M
|
||||
|
||||
proc/updateButtons()
|
||||
if (src.topBarRendered && src.rendered)
|
||||
if(!src.owner || !src.owner.client)
|
||||
return
|
||||
|
||||
for(var/obj/screen/ability/A in src.owner.client.screen)
|
||||
src.owner.client.screen -= A
|
||||
|
||||
var/pos_x = 1
|
||||
var/pos_y = 0
|
||||
|
||||
for(var/datum/targetable/B in src.abilities)
|
||||
if (!istype(B.object, /obj/screen/ability/topBar))
|
||||
continue
|
||||
var/obj/screen/ability/topBar/button = B.object
|
||||
button.update_on_hud(pos_x,pos_y)
|
||||
if (!B.special_screen_loc)
|
||||
pos_x++
|
||||
if(pos_x > 15)
|
||||
pos_x = 1
|
||||
pos_y++
|
||||
return
|
||||
|
||||
if(src.rendered)
|
||||
if(!src.owner || !src.owner.client)
|
||||
return
|
||||
|
||||
for(var/datum/targetable/B in src.abilities)
|
||||
if(istype(B.object, /obj/screen/ability) && !istype(B.object, /obj/screen/ability/topBar))
|
||||
B.object.updateIcon()
|
||||
return
|
||||
|
||||
proc/deepCopy()
|
||||
var/datum/abilityHolder/copy = new src.type
|
||||
for (var/datum/targetable/T in src.suspended)
|
||||
if (!T.copiable)
|
||||
continue
|
||||
copy.addAbility(T.type)
|
||||
copy.suspendAllAbilities()
|
||||
for (var/datum/targetable/T in src.abilities)
|
||||
if (!T.copiable)
|
||||
continue
|
||||
copy.addAbility(T.type)
|
||||
return copy
|
||||
|
||||
proc/addBonus(var/value)
|
||||
bonus += value
|
||||
|
||||
proc/generatePoints()
|
||||
lastBonus = bonus
|
||||
points += bonus
|
||||
points += regenRate
|
||||
bonus = 0
|
||||
|
||||
proc/transferOwnership(var/newbody)
|
||||
owner = newbody
|
||||
|
||||
proc/Stat()
|
||||
if (usesPoints && pointName != "" && rendered)
|
||||
stat(null, " ")
|
||||
stat("[src.pointName]:", src.points)
|
||||
if (src.regenRate || src.lastBonus)
|
||||
stat("Generation Rate:", "[src.regenRate] + [src.lastBonus]")
|
||||
|
||||
proc/StatAbilities()
|
||||
if (topBarRendered || !rendered)
|
||||
return
|
||||
statpanel(src.tabName)
|
||||
onAbilityStat()
|
||||
for (var/datum/targetable/spell in src.abilities)
|
||||
spell.Stat()
|
||||
|
||||
proc/onAbilityStat()
|
||||
return
|
||||
|
||||
proc/deductPoints(cost)
|
||||
if (!usesPoints || cost == 0)
|
||||
return
|
||||
|
||||
points -= cost
|
||||
|
||||
proc/suspendAllAbilities()
|
||||
src.suspended = src.abilities.Copy()
|
||||
src.abilities.len = 0
|
||||
src.updateButtons()
|
||||
|
||||
proc/resumeAllAbilities()
|
||||
if (src.suspended && src.suspended.len)
|
||||
src.abilities = src.suspended
|
||||
src.suspended = list()
|
||||
src.updateButtons()
|
||||
|
||||
proc/addAbility(var/abilityType)
|
||||
if (istext(abilityType))
|
||||
abilityType = text2path(abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return
|
||||
if (src.abilities.Find(abilityType))
|
||||
return
|
||||
var/datum/targetable/A = new abilityType
|
||||
A.holder = src
|
||||
src.abilities += A
|
||||
A.onAttach(src)
|
||||
src.updateButtons()
|
||||
return A
|
||||
|
||||
proc/removeAbility(var/abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return
|
||||
for (var/datum/targetable/A in src.abilities)
|
||||
if (A.type == abilityType)
|
||||
src.abilities -= A
|
||||
if (A == src.altPower)
|
||||
src.altPower = null
|
||||
if (A == src.ctrlPower)
|
||||
src.ctrlPower = null
|
||||
if (A == src.shiftPower)
|
||||
src.shiftPower = null
|
||||
qdel(A)
|
||||
return
|
||||
src.updateButtons()
|
||||
|
||||
proc/removeAbilityInstance(var/datum/targetable/A)
|
||||
if (!istype(A))
|
||||
return
|
||||
if (A in src.abilities)
|
||||
src.abilities -= A
|
||||
qdel(A)
|
||||
return
|
||||
src.updateButtons()
|
||||
|
||||
proc/getAbility(var/abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return null
|
||||
for (var/datum/targetable/A in src.abilities)
|
||||
if (A.type == abilityType)
|
||||
return A
|
||||
return null
|
||||
|
||||
proc/pointCheck(cost)
|
||||
if (!usesPoints)
|
||||
return 1
|
||||
if (src.points < 0) // Just-in-case fallback.
|
||||
logTheThing("debug", usr, null, "'s ability holder ([src.type]) was set to an invalid value (points less than 0), resetting.")
|
||||
src.points = 0
|
||||
if (cost > points)
|
||||
boutput(owner, notEnoughPointsMessage)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
proc/click(atom/target, params)
|
||||
if (!owner)
|
||||
return 0
|
||||
if (params["alt"])
|
||||
if (altPower)
|
||||
altPower.handleCast(target)
|
||||
return 1
|
||||
//else
|
||||
// boutput(owner, "<span style=\"color:red\">Nothing is bound to alt.</span>")
|
||||
return 0
|
||||
else if (params["ctrl"])
|
||||
if (ctrlPower)
|
||||
ctrlPower.handleCast(target)
|
||||
return 1
|
||||
//else
|
||||
// boutput(owner, "<span style=\"color:red\">Nothing is bound to ctrl.</span>")
|
||||
return 0
|
||||
else if (params["shift"])
|
||||
if (shiftPower)
|
||||
shiftPower.handleCast(target)
|
||||
return 1
|
||||
//else
|
||||
// boutput(owner, "<span style=\"color:red\">Nothing is bound to shift.</span>")
|
||||
return 0
|
||||
|
||||
proc/actionKey(var/num)
|
||||
//Please make sure you return 1 if one of the holders/abilities handled the key.
|
||||
for (var/datum/targetable/T in src.abilities)
|
||||
if(T.waiting_for_hotkey)
|
||||
unbind_action_number(num)
|
||||
T.waiting_for_hotkey = 0
|
||||
T.action_key_number = num
|
||||
boutput(owner, "<span style=\"color:blue\">Bound [T.name] to [num].</span>")
|
||||
updateButtons()
|
||||
return 1
|
||||
|
||||
updateButtons()
|
||||
|
||||
for (var/datum/targetable/T in src.abilities)
|
||||
if (T.action_key_number < 0)
|
||||
continue
|
||||
if(T.action_key_number == num)
|
||||
if((T.ignore_sticky_cooldown && !T.cooldowncheck()) || T.cooldowncheck())
|
||||
if (!T.targeted)
|
||||
T.handleCast()
|
||||
return
|
||||
else
|
||||
if(usr.targeting_spell == T)
|
||||
usr.targeting_spell = null
|
||||
else
|
||||
usr.targeting_spell = T
|
||||
usr.update_cursor()
|
||||
T.holder.updateButtons()
|
||||
return 1
|
||||
else
|
||||
boutput(owner, "<span style=\"color:red\">That ability is on cooldown for [round((T.last_cast - world.time) / 10)] seconds!</span>")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
proc/cancel_action_binding()
|
||||
for (var/datum/targetable/T in src.abilities)
|
||||
T.waiting_for_hotkey = 0
|
||||
updateButtons()
|
||||
|
||||
proc/unbind_action_number(var/num)
|
||||
for (var/datum/targetable/T in src.abilities)
|
||||
if(T.action_key_number == num)
|
||||
T.action_key_number = -1
|
||||
boutput(owner, "<span style=\"color:red\">Unbound [T.name] from [num].</span>")
|
||||
updateButtons()
|
||||
return 0
|
||||
|
||||
/obj/screen/ability
|
||||
var/datum/targetable/owner
|
||||
var/static/image/binding = image('icons/mob/spell_buttons.dmi',"binding")
|
||||
//*screams*
|
||||
var/static/image/one = image('icons/mob/spell_buttons.dmi',"1")
|
||||
var/static/image/two = image('icons/mob/spell_buttons.dmi',"2")
|
||||
var/static/image/three = image('icons/mob/spell_buttons.dmi',"3")
|
||||
var/static/image/four = image('icons/mob/spell_buttons.dmi',"4")
|
||||
var/static/image/five = image('icons/mob/spell_buttons.dmi',"5")
|
||||
var/static/image/six = image('icons/mob/spell_buttons.dmi',"6")
|
||||
var/static/image/seven = image('icons/mob/spell_buttons.dmi',"7")
|
||||
var/static/image/eight = image('icons/mob/spell_buttons.dmi',"8")
|
||||
var/static/image/nine = image('icons/mob/spell_buttons.dmi',"9")
|
||||
var/static/image/zero = image('icons/mob/spell_buttons.dmi',"0")
|
||||
|
||||
proc/updateIcon()
|
||||
src.overlays.Cut()
|
||||
if (owner.waiting_for_hotkey)
|
||||
src.overlays += src.binding
|
||||
if(owner.action_key_number > -1)
|
||||
set_number_overlay(owner.action_key_number)
|
||||
return
|
||||
|
||||
proc/set_number_overlay(var/num)
|
||||
switch(num)
|
||||
if(1)
|
||||
src.overlays += src.one
|
||||
if(2)
|
||||
src.overlays += src.two
|
||||
if(3)
|
||||
src.overlays += src.three
|
||||
if(4)
|
||||
src.overlays += src.four
|
||||
if(5)
|
||||
src.overlays += src.five
|
||||
if(6)
|
||||
src.overlays += src.six
|
||||
if(7)
|
||||
src.overlays += src.seven
|
||||
if(8)
|
||||
src.overlays += src.eight
|
||||
if(9)
|
||||
src.overlays += src.nine
|
||||
if(0)
|
||||
src.overlays += src.zero
|
||||
return
|
||||
|
||||
// Switch to targeted only if multiple mobs are in range. All screen abilities customize their clicked(),
|
||||
// and you have to call this proc there if you want to use it. You also need to set 'target_selection_check = 1'
|
||||
// for every spell that should function in this manner.
|
||||
// See /obj/screen/ability/wrestler/clicked() for a practical example (Convair880).
|
||||
proc/do_target_selection_check()
|
||||
var/datum/targetable/spell = owner
|
||||
var/use_targeted = 0
|
||||
|
||||
if (!spell || !istype(spell))
|
||||
return 0
|
||||
if (!spell.holder)
|
||||
return 0
|
||||
|
||||
if (spell.target_selection_check == 1)
|
||||
var/list/mob/targets = spell.target_reference_lookup()
|
||||
if (targets.len <= 0)
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">There's nobody in range.</span>")
|
||||
use_targeted = 2 // Abort parent proc.
|
||||
else if (targets.len == 1) // Only one guy nearby, but we need the mob reference for handleCast() then.
|
||||
use_targeted = 0
|
||||
spawn
|
||||
spell.handleCast(targets[1])
|
||||
use_targeted = 2 // Abort parent proc.
|
||||
else
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\"><b>Multiple targets detected, switching to manual aiming.</b></span>")
|
||||
use_targeted = 1
|
||||
|
||||
return use_targeted
|
||||
|
||||
//WIRE TOOLTIPS
|
||||
MouseEntered(location, control, params)
|
||||
var/theme
|
||||
if (istype(owner, /datum/targetable/wraithAbility) || istype(owner, /datum/targetable/revenantAbility))
|
||||
theme = "wraith"
|
||||
|
||||
usr.client.tooltip.show(src, params, title = src.name, content = (src.desc ? src.desc : null), theme = theme)
|
||||
|
||||
MouseExited()
|
||||
usr.client.tooltip.hide()
|
||||
|
||||
/obj/screen/ability/topBar
|
||||
var/static/image/ctrl_highlight = image('icons/mob/spell_buttons.dmi',"ctrl")
|
||||
var/static/image/shift_highlight = image('icons/mob/spell_buttons.dmi',"shift")
|
||||
var/static/image/alt_highlight = image('icons/mob/spell_buttons.dmi',"alt")
|
||||
var/static/image/cooldown = image('icons/mob/spell_buttons.dmi',"cooldown")
|
||||
var/static/image/darkener = image('icons/mob/spell_buttons.dmi',"darkener")
|
||||
|
||||
var/obj/screen/pseudo_overlay/cd_tens
|
||||
var/obj/screen/pseudo_overlay/cd_secs
|
||||
var/tens_offset_x = 0
|
||||
var/tens_offset_y = 0
|
||||
var/secs_offset_x = 0
|
||||
var/secs_offset_y = 0
|
||||
|
||||
New()
|
||||
..()
|
||||
var/obj/screen/pseudo_overlay/T = new /obj/screen/pseudo_overlay(src)
|
||||
var/obj/screen/pseudo_overlay/S = new /obj/screen/pseudo_overlay(src)
|
||||
T.icon = 'icons/effects/particles_characters.dmi'
|
||||
S.icon = 'icons/effects/particles_characters.dmi'
|
||||
T.x_offset = tens_offset_x
|
||||
T.y_offset = tens_offset_y
|
||||
S.x_offset = secs_offset_x
|
||||
S.y_offset = secs_offset_y
|
||||
cd_tens = T
|
||||
cd_secs = S
|
||||
darkener.alpha = 100
|
||||
spawn(0)
|
||||
T.color = owner.cd_text_color
|
||||
S.color = owner.cd_text_color
|
||||
|
||||
updateIcon()
|
||||
var/mob/M = get_controlling_mob()
|
||||
if (!istype(M) || !M.client)
|
||||
return null
|
||||
|
||||
src.overlays = list()
|
||||
if (owner.holder)
|
||||
if (src == owner.holder.shiftPower)
|
||||
src.overlays += src.shift_highlight
|
||||
if (src == owner.holder.ctrlPower)
|
||||
src.overlays += src.ctrl_highlight
|
||||
if (src == owner.holder.altPower)
|
||||
src.overlays += src.alt_highlight
|
||||
if (owner.waiting_for_hotkey)
|
||||
src.overlays += src.binding
|
||||
|
||||
if(owner.action_key_number > -1)
|
||||
set_number_overlay(owner.action_key_number)
|
||||
|
||||
return
|
||||
|
||||
proc/get_controlling_mob()
|
||||
var/mob/M = owner.holder.owner
|
||||
if (!istype(M) || !M.client)
|
||||
return null
|
||||
return M
|
||||
|
||||
proc/update_on_hud(var/pos_x = 0,var/pos_y = 0)
|
||||
|
||||
updateIcon()
|
||||
|
||||
var/mob/M = get_controlling_mob()
|
||||
if (!istype(M) || !M.client)
|
||||
return null
|
||||
if (owner.special_screen_loc)
|
||||
src.screen_loc = owner.special_screen_loc
|
||||
else
|
||||
src.screen_loc = "NORTH-[pos_y],[pos_x]"
|
||||
|
||||
var/name = initial(owner.name)
|
||||
if (owner.holder)
|
||||
if (owner.holder.usesPoints)
|
||||
name += "; Cost: [owner.pointCost] [owner.holder.pointName]"
|
||||
name += "; Cooldown: [owner.cooldown / 10] seconds"
|
||||
src.name = name
|
||||
|
||||
|
||||
|
||||
M.client.screen += src
|
||||
M.client.screen -= src.cd_tens
|
||||
M.client.screen -= src.cd_secs
|
||||
|
||||
var/on_cooldown = round((owner.last_cast - world.time) / 10)
|
||||
if (on_cooldown > 0)
|
||||
on_cooldown = min(on_cooldown,99)
|
||||
src.overlays += src.darkener
|
||||
src.overlays += src.cooldown
|
||||
if (on_cooldown >= 10)
|
||||
src.cd_tens.icon_state = "[get_digit_from_number(on_cooldown,2)]"
|
||||
src.cd_tens.screen_loc = "NORTH-[pos_y]:[src.tens_offset_y],[pos_x]:[src.tens_offset_x]"
|
||||
M.client.screen += src.cd_tens
|
||||
src.cd_secs.icon_state = "[get_digit_from_number(on_cooldown,1)]"
|
||||
src.cd_secs.screen_loc = "NORTH-[pos_y]:[src.secs_offset_y],[pos_x]:[src.secs_offset_x]"
|
||||
M.client.screen += src.cd_secs
|
||||
|
||||
clicked(parameters)
|
||||
if (!owner.holder || !owner.holder.owner || usr != owner.holder.owner)
|
||||
boutput(usr, "<span style=\"color:red\">You do not own this ability.</span>")
|
||||
return
|
||||
var/datum/abilityHolder/holder = owner.holder
|
||||
var/mob/user = holder.owner
|
||||
|
||||
if(parameters["left"])
|
||||
if (owner.targeted && user.targeting_spell == owner)
|
||||
user.targeting_spell = null
|
||||
user.update_cursor()
|
||||
return
|
||||
|
||||
if (parameters["ctrl"])
|
||||
if (owner == holder.altPower || owner == holder.shiftPower)
|
||||
boutput(user, "<span style=\"color:red\">That ability is already bound to another key.</span>")
|
||||
return
|
||||
|
||||
if (owner == holder.ctrlPower)
|
||||
holder.ctrlPower = null
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] has been unbound from Ctrl-Click.</b></span>")
|
||||
holder.updateButtons()
|
||||
else
|
||||
holder.ctrlPower = owner
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] is now bound to Ctrl-Click.</b></span>")
|
||||
|
||||
else if (parameters["alt"])
|
||||
if (owner == holder.shiftPower || owner == holder.ctrlPower)
|
||||
boutput(user, "<span style=\"color:red\">That ability is already bound to another key.</span>")
|
||||
return
|
||||
|
||||
if (owner == holder.altPower)
|
||||
holder.altPower = null
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] has been unbound from Alt-Click.</b></span>")
|
||||
holder.updateButtons()
|
||||
else
|
||||
holder.altPower = owner
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] is now bound to Alt-Click.</b></span>")
|
||||
|
||||
else if (parameters["shift"])
|
||||
if (owner == holder.altPower || owner == holder.ctrlPower)
|
||||
boutput(user, "<span style=\"color:red\">That ability is already bound to another key.</span>")
|
||||
return
|
||||
|
||||
if (owner == holder.shiftPower)
|
||||
holder.shiftPower = null
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] has been unbound from Shift-Click.</b></span>")
|
||||
holder.updateButtons()
|
||||
else
|
||||
holder.shiftPower = owner
|
||||
boutput(user, "<span style=\"color:blue\"><b>[owner.name] is now bound to Shift-Click.</b></span>")
|
||||
|
||||
else
|
||||
if (holder.help_mode && owner.helpable)
|
||||
boutput(user, "<span style=\"color:blue\"><b>This is your [owner.name] ability.</b></span>")
|
||||
boutput(user, "<span style=\"color:blue\">[owner.desc]</span>")
|
||||
if (owner.holder.usesPoints)
|
||||
boutput(user, "<span style=\"color:blue\">Cost: <strong>[owner.pointCost]</strong></span>")
|
||||
if (owner.cooldown)
|
||||
boutput(user, "<span style=\"color:blue\">Cooldown: <strong>[owner.cooldown / 10] seconds</strong></span>")
|
||||
else
|
||||
if (!owner.cooldowncheck())
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability is on cooldown for [round((owner.last_cast - world.time) / 10)] seconds.</span>")
|
||||
return
|
||||
|
||||
if (!owner.targeted)
|
||||
owner.handleCast()
|
||||
return
|
||||
else
|
||||
user.targeting_spell = owner
|
||||
user.update_cursor()
|
||||
else if(parameters["middle"])
|
||||
if(owner.waiting_for_hotkey)
|
||||
holder.cancel_action_binding()
|
||||
else
|
||||
owner.waiting_for_hotkey = 1
|
||||
boutput(usr, "<span style=\"color:blue\">Please press a number to bind this ability to...</span>")
|
||||
|
||||
owner.holder.updateButtons()
|
||||
|
||||
MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
|
||||
if (!owner || !owner.holder || !owner.holder.topBarRendered)
|
||||
return
|
||||
if (!istype(O,/obj/screen/ability/topBar) || !owner.holder)
|
||||
return
|
||||
var/obj/screen/ability/source = O
|
||||
if (!istype(src.owner) || !istype(source.owner))
|
||||
boutput(src.owner, "<span style=\"color:red\">You may only switch the places of ability buttons.</span>")
|
||||
return
|
||||
|
||||
var/index_source = owner.holder.abilities.Find(source.owner)
|
||||
var/index_target = owner.holder.abilities.Find(src.owner)
|
||||
owner.holder.abilities.Swap(index_source,index_target)
|
||||
owner.holder.updateButtons()
|
||||
|
||||
/datum/targetable
|
||||
var
|
||||
name = null
|
||||
desc = null
|
||||
|
||||
max_range = 7
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
last_cast = 0
|
||||
cooldown = 100
|
||||
start_on_cooldown = 0
|
||||
datum/abilityHolder/holder
|
||||
obj/screen/ability/object
|
||||
pointCost = 0
|
||||
special_screen_loc = null
|
||||
helpable = 1
|
||||
cd_text_color = "#FFFFFF"
|
||||
copiable = 1
|
||||
target_nodamage_check = 0
|
||||
target_selection_check = 0 // See comment in /obj/screen/ability.
|
||||
dont_lock_holder = 0 // Bypass holder lock when we cast this spell.
|
||||
ignore_holder_lock = 0 // Can we cast this spell when the holder is locked?
|
||||
restricted_area_check = 0 // Are we prohibited from casting this spell in 1 (all of Z2) or 2 (only the VR)?
|
||||
can_target_ghosts = 0 // Can we target observers if we see them (ectogoggles)?
|
||||
check_range = 1 //Does this check for range at all?
|
||||
sticky = 0 //Targeting stays active after using spell if this is 1. click button again to disable the active spell.
|
||||
ignore_sticky_cooldown = 0 //if 1, Ability will stick to cursor even if ability goes on cooldown after first cast.
|
||||
|
||||
action_key_number = -1 //Number hotkey assigned to this ability. Only used if > 0
|
||||
waiting_for_hotkey = 0 //If 1, the next number hotkey pressed will be bound to this.
|
||||
|
||||
preferred_holder_type = /datum/abilityHolder
|
||||
|
||||
icon = 'icons/mob/spell_buttons.dmi'
|
||||
icon_state = "blob-template"
|
||||
|
||||
proc
|
||||
handleCast(atom/target)
|
||||
var/result = tryCast(target)
|
||||
if (result && result != 999)
|
||||
last_cast = 0 // reset cooldown
|
||||
else if (result != 999)
|
||||
doCooldown()
|
||||
afterCast()
|
||||
holder.updateButtons()
|
||||
|
||||
cast(atom/target)
|
||||
return
|
||||
|
||||
onAttach(var/datum/abilityHolder/H)
|
||||
if (src.start_on_cooldown)
|
||||
doCooldown()
|
||||
return
|
||||
|
||||
// Don't remove the holder.locked checks, as lots of people used lag and click-spamming
|
||||
// to execute one ability multiple times. The checks hopefully make it a bit more difficult.
|
||||
tryCast(atom/target)
|
||||
if (!holder || !holder.owner)
|
||||
logTheThing("debug", usr, null, "orphaned ability clicked: [name]. ([holder ? "no owner" : "no holder"])")
|
||||
return 1
|
||||
if (src.holder.locked == 1 && src.ignore_holder_lock != 1)
|
||||
boutput(holder.owner, "<span style=\"color:red\">You're already casting an ability.</span>")
|
||||
return 999
|
||||
if (src.dont_lock_holder != 1)
|
||||
src.holder.locked = 1
|
||||
if (!holder.pointCheck(pointCost))
|
||||
src.holder.locked = 0
|
||||
return 1000
|
||||
if (last_cast > world.time)
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability is on cooldown for [round((last_cast - world.time) / 10)] seconds.</span>")
|
||||
src.holder.locked = 0
|
||||
return 999
|
||||
if (src.restricted_area_check)
|
||||
var/turf/T = get_turf(holder.owner)
|
||||
if (!T || !isturf(T))
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability doesn't seem to work here.</span>")
|
||||
src.holder.locked = 0
|
||||
return 999
|
||||
switch (src.restricted_area_check)
|
||||
if (1)
|
||||
if (isrestrictedz(T.z))
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability doesn't seem to work here.</span>")
|
||||
src.holder.locked = 0
|
||||
return 999
|
||||
if (2)
|
||||
var/area/A = get_area(T)
|
||||
if (A && istype(A, /area/sim))
|
||||
boutput(holder.owner, "<span style=\"color:red\">You can't use this ability in virtual reality.</span>")
|
||||
src.holder.locked = 0
|
||||
return 999
|
||||
if (src.targeted && src.target_nodamage_check && (target && target != holder.owner && check_target_immunity(target) == 1))
|
||||
target.visible_message("<span style=\"color:red\"><B>[src.holder.owner]'s attack has no effect on [target] whatsoever!</B></span>")
|
||||
src.holder.locked = 0
|
||||
return 998
|
||||
if (!castcheck())
|
||||
src.holder.locked = 0
|
||||
return 998
|
||||
. = cast(target)
|
||||
src.holder.locked = 0
|
||||
if (!.)
|
||||
holder.deductPoints(pointCost)
|
||||
|
||||
updateObject()
|
||||
return
|
||||
|
||||
doCooldown()
|
||||
src.last_cast = world.time + src.cooldown
|
||||
|
||||
castcheck()
|
||||
return 1
|
||||
|
||||
cooldowncheck()
|
||||
if (src.last_cast > world.time)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
afterCast()
|
||||
return
|
||||
|
||||
Stat()
|
||||
updateObject(holder.owner)
|
||||
stat(null, object)
|
||||
|
||||
// Universal grab check you can use (Convair880).
|
||||
grab_check(var/mob/target, var/state = 1, var/dirty = 0)
|
||||
if (!holder || state < 1)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
var/obj/item/grab/G = null
|
||||
|
||||
if (dirty == 1)
|
||||
var/obj/item/grab/GD = M.equipped()
|
||||
|
||||
if (!GD || !istype(GD) || (!GD.affecting || !ismob(GD.affecting)))
|
||||
boutput(M, __red("You need to grab hold of the target with your active hand first!"))
|
||||
return 0
|
||||
|
||||
var/mob/living/L = GD.affecting
|
||||
if (L && ismob(L) && L != M)
|
||||
if (GD.state >= state)
|
||||
G = GD
|
||||
else
|
||||
boutput(M, __red("You need a tighter grip!"))
|
||||
else
|
||||
boutput(M, __red("You need to grab hold of the target with your active hand first!"))
|
||||
|
||||
return G
|
||||
|
||||
else
|
||||
if (!target || !ismob(target))
|
||||
return 0
|
||||
|
||||
if (src.targeted)
|
||||
for (var/obj/item/grab/G2 in M)
|
||||
if (G2.affecting)
|
||||
if (G2.affecting != target)
|
||||
continue
|
||||
if (G2.affecting == M)
|
||||
continue
|
||||
if (G2.state >= state)
|
||||
G = G2
|
||||
break
|
||||
else
|
||||
boutput(M, __red("You need a tighter grip!"))
|
||||
return 0
|
||||
if (isnull(G) || !istype(G))
|
||||
boutput(M, __red("You need to grab hold of [target] first!"))
|
||||
return 0
|
||||
else
|
||||
return G
|
||||
|
||||
return 0
|
||||
|
||||
// See comment in /obj/screen/ability (Convair880).
|
||||
target_reference_lookup()
|
||||
var/list/mob/targets = list()
|
||||
|
||||
if (!holder)
|
||||
return targets
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return targets
|
||||
|
||||
for (var/mob/living/L in oview(src.max_range, M))
|
||||
targets.Add(L)
|
||||
|
||||
return targets
|
||||
|
||||
/obj/screen/pseudo_overlay
|
||||
// this is hack as all get out
|
||||
// but since i cant directly alter the pixel offset of a screen overlay it'll have to do
|
||||
name = ""
|
||||
mouse_opacity = 0
|
||||
layer = 61
|
||||
var/x_offset = 0
|
||||
var/y_offset = 0
|
||||
|
||||
/datum/abilityHolder/composite
|
||||
var/list/holders = list()
|
||||
rendered = 0
|
||||
|
||||
proc/addHolder(holderType)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
if (H.type == holderType)
|
||||
return
|
||||
holders += new holderType(owner)
|
||||
updateButtons()
|
||||
|
||||
proc/addHolderInstance(var/datum/abilityHolder/N)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
if (H == N)
|
||||
return
|
||||
holders += N
|
||||
if (N.owner != owner)
|
||||
N.owner = owner
|
||||
updateButtons()
|
||||
|
||||
proc/removeHolder(holderType)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
if (H.type == holderType)
|
||||
holders -= H
|
||||
updateButtons()
|
||||
|
||||
proc/getHolder(holderType)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
if (H.type == holderType)
|
||||
return H
|
||||
|
||||
cancel_action_binding()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.cancel_action_binding()
|
||||
|
||||
unbind_action_number(var/num)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.unbind_action_number(num)
|
||||
return 0
|
||||
|
||||
actionKey(var/num)
|
||||
var/used = 0
|
||||
|
||||
//2 Steps avoid binding problems with more than 2 holders.
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
for (var/datum/targetable/T in H.abilities)
|
||||
if(T.waiting_for_hotkey)
|
||||
used = H.actionKey(num)
|
||||
break
|
||||
if(used) return used
|
||||
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
used = H.actionKey(num)
|
||||
if(used) return used
|
||||
return 0
|
||||
|
||||
updateButtons()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.updateButtons()
|
||||
|
||||
addBonus(var/value)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.addBonus(value)
|
||||
|
||||
generatePoints()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.generatePoints()
|
||||
|
||||
Stat()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.Stat()
|
||||
|
||||
StatAbilities()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.StatAbilities()
|
||||
|
||||
deductPoints(cost)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.deductPoints(cost)
|
||||
|
||||
suspendAllAbilities()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.suspendAllAbilities()
|
||||
|
||||
resumeAllAbilities()
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.resumeAllAbilities()
|
||||
|
||||
addAbility(var/abilityType)
|
||||
if (!holders.len)
|
||||
return
|
||||
if (istext(abilityType))
|
||||
abilityType = text2path(abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return
|
||||
var/datum/targetable/A = new abilityType
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
if (istype(H, A.preferred_holder_type))
|
||||
A.holder = H
|
||||
H.abilities += A
|
||||
A.onAttach(H)
|
||||
H.updateButtons()
|
||||
return
|
||||
var/datum/abilityHolder/X = holders[1]
|
||||
A.holder = X
|
||||
X.abilities += A
|
||||
X.updateButtons()
|
||||
A.onAttach(X)
|
||||
return A
|
||||
|
||||
removeAbility(var/abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.removeAbility(abilityType)
|
||||
src.updateButtons()
|
||||
|
||||
removeAbilityInstance(var/datum/targetable/A)
|
||||
if (!istype(A))
|
||||
return
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.removeAbilityInstance(A)
|
||||
src.updateButtons()
|
||||
|
||||
getAbility(var/abilityType)
|
||||
if (!ispath(abilityType))
|
||||
return null
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
var/R = H.getAbility(abilityType)
|
||||
if (R)
|
||||
return R
|
||||
return null
|
||||
|
||||
pointCheck(cost)
|
||||
return 1
|
||||
|
||||
deepCopy()
|
||||
var/datum/abilityHolder/composite/copy = new src.type
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
copy.holders += H.deepCopy()
|
||||
return copy
|
||||
|
||||
transferOwnership(var/newbody)
|
||||
for (var/datum/abilityHolder/H in holders)
|
||||
H.transferOwnership(newbody)
|
||||
owner = newbody
|
||||
@@ -0,0 +1,193 @@
|
||||
/mob/proc/make_changeling()
|
||||
var/datum/abilityHolder/changeling/O = src.get_ability_holder(/datum/abilityHolder/changeling)
|
||||
if (O)
|
||||
return
|
||||
|
||||
if (src.mind && !src.mind.is_changeling && (src.mind.special_role != "omnitraitor"))
|
||||
src << browse(grabResource("html/traitorTips/changelingTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
var/datum/abilityHolder/changeling/C = src.add_ability_holder(/datum/abilityHolder/changeling)
|
||||
C.addAbility(/datum/targetable/changeling/abomination)
|
||||
C.addAbility(/datum/targetable/changeling/absorb)
|
||||
C.addAbility(/datum/targetable/changeling/devour)
|
||||
C.addAbility(/datum/targetable/changeling/mimic_voice)
|
||||
C.addAbility(/datum/targetable/changeling/monkey)
|
||||
C.addAbility(/datum/targetable/changeling/regeneration)
|
||||
C.addAbility(/datum/targetable/changeling/scream)
|
||||
C.addAbility(/datum/targetable/changeling/spit)
|
||||
C.addAbility(/datum/targetable/changeling/stasis)
|
||||
C.addAbility(/datum/targetable/changeling/sting/neurotoxin)
|
||||
C.addAbility(/datum/targetable/changeling/sting/lsd)
|
||||
C.addAbility(/datum/targetable/changeling/sting/dna)
|
||||
C.addAbility(/datum/targetable/changeling/transform)
|
||||
|
||||
if (src.mind)
|
||||
src.mind.is_changeling = C
|
||||
|
||||
spawn (25) // Don't remove.
|
||||
if (src) src.assign_gimmick_skull()
|
||||
|
||||
return
|
||||
|
||||
/obj/screen/ability/changeling
|
||||
clicked(params)
|
||||
var/datum/targetable/changeling/spell = owner
|
||||
var/datum/abilityHolder/holder = owner.holder
|
||||
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
|
||||
if(params["shift"] && params["ctrl"])
|
||||
if(owner.waiting_for_hotkey)
|
||||
holder.cancel_action_binding()
|
||||
return
|
||||
else
|
||||
owner.waiting_for_hotkey = 1
|
||||
src.updateIcon()
|
||||
boutput(usr, "<span style=\"color:blue\">Please press a number to bind this ability to...</span>")
|
||||
return
|
||||
|
||||
if (!isturf(owner.holder.owner.loc) && !spell.can_use_in_container)
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">Using that in here will do just about no good for you.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
|
||||
/datum/abilityHolder/changeling
|
||||
usesPoints = 1
|
||||
regenRate = 0
|
||||
tabName = "Changeling"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">We are not strong enough to do this.</span>"
|
||||
var/list/absorbed_dna = list()
|
||||
var/in_fakedeath = 0
|
||||
var/absorbtions = 0
|
||||
|
||||
New(var/mob/living/M)
|
||||
..()
|
||||
var/datum/bioHolder/original = new/datum/bioHolder(M)
|
||||
original.CopyOther(M.bioHolder)
|
||||
absorbed_dna = list("[M.name]" = original)
|
||||
|
||||
proc/addDna(var/mob/living/M, var/headspider_override = 0)
|
||||
var/datum/abilityHolder/changeling/O = M.get_ability_holder(/datum/abilityHolder/changeling)
|
||||
if (O)
|
||||
boutput(owner, "<span style=\"color:blue\">[M] was a changeling! We have absorbed their entire genetic structure!</span>")
|
||||
logTheThing("combat", owner, M, "absorbs %target% as a changeling [log_loc(owner)].")
|
||||
|
||||
if (headspider_override != 1) // Headspiders shouldn't be free.
|
||||
src.points += 10 // 10 regular points for their body...
|
||||
if (O.points > 0) // ...and then grab their DNA stockpile too.
|
||||
src.points = max(0, src.points + O.points)
|
||||
|
||||
src.absorbtions++ // Same principle.
|
||||
for(var/D in O.absorbed_dna)
|
||||
src.absorbed_dna[D] = O.absorbed_dna[D]
|
||||
src.absorbtions++
|
||||
|
||||
O.absorbed_dna = list()
|
||||
O.points = 0
|
||||
else
|
||||
var/datum/bioHolder/original = new/datum/bioHolder(M)
|
||||
original.CopyOther(M.bioHolder)
|
||||
src.absorbed_dna[M.real_name] = original
|
||||
if (headspider_override != 1)
|
||||
src.points += 10
|
||||
src.absorbtions++
|
||||
|
||||
onAbilityStat()
|
||||
..()
|
||||
//On Changeling tab
|
||||
stat("Absorbed DNA:", absorbtions)
|
||||
stat("DNA Points:", points)
|
||||
|
||||
// ----------------------------------------
|
||||
// Generic abilities that critters may have
|
||||
// ----------------------------------------
|
||||
|
||||
/datum/targetable/changeling
|
||||
icon = 'icons/mob/spell_buttons.dmi'
|
||||
icon_state = "template" // No longer ToDo thanks to Sundance420.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
var/abomination_only = 0
|
||||
var/human_only = 0
|
||||
var/can_use_in_container = 0
|
||||
preferred_holder_type = /datum/abilityHolder/changeling
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/changeling/B = new /obj/screen/ability/changeling(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/changeling()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
|
||||
proc/incapacitationCheck()
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/changeling/H = holder
|
||||
if (istype(H) && H.in_fakedeath)
|
||||
return 1
|
||||
return M.stat || M.paralysis
|
||||
|
||||
castcheck()
|
||||
if (incapacitationCheck())
|
||||
boutput(holder.owner, __red("We cannot use our abilities while incapacitated."))
|
||||
return 0
|
||||
if (!human_only && !abomination_only)
|
||||
return 1
|
||||
var/mob/living/carbon/human/H = holder.owner
|
||||
if (istype(H))
|
||||
if (human_only && !istype(H.mutantrace, /datum/mutantrace/abomination) && !istype(H.mutantrace, /datum/mutantrace/monkey))
|
||||
return 1
|
||||
else if (abomination_only && istype(H.mutantrace, /datum/mutantrace/abomination))
|
||||
return 1
|
||||
else
|
||||
boutput(holder.owner, __red("You're not supposed to see this ability! Notify a coder."))
|
||||
boutput(holder.owner, __red("Also notify a coder if you see this message when you didn't actually click an ability."))
|
||||
return 0
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
|
||||
Stat()
|
||||
if (!human_only && !abomination_only)
|
||||
..()
|
||||
var/mob/living/carbon/human/H = holder.owner
|
||||
if (istype(H))
|
||||
if (human_only && !istype(H.mutantrace, /datum/mutantrace/abomination) && !istype(H.mutantrace, /datum/mutantrace/monkey))
|
||||
..()
|
||||
else if (abomination_only && istype(H.mutantrace, /datum/mutantrace/abomination))
|
||||
..()
|
||||
@@ -0,0 +1,88 @@
|
||||
/datum/targetable/changeling/abomination
|
||||
name = "Horror Form"
|
||||
desc = "Become something much more powerful."
|
||||
icon_state = "horror"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
can_use_in_container = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/mob/living/carbon/human/H = holder.owner
|
||||
if (H.mutantrace)
|
||||
if (istype(H.mutantrace, /datum/mutantrace/abomination))
|
||||
if (alert("Are we sure?","Exit Horror Form?","Yes","No") != "Yes")
|
||||
return 1
|
||||
H.revert_from_horror_form()
|
||||
else if (istype(H.mutantrace, /datum/mutantrace/monkey))
|
||||
boutput(H, "We cannot transform in this form.")
|
||||
return 1
|
||||
else
|
||||
boutput(H, "We cannot transform in this form.")
|
||||
return 1
|
||||
else
|
||||
if (holder.points < 15)
|
||||
boutput(holder.owner, __red("We're not strong enough to maintain the form."))
|
||||
return 1
|
||||
if (alert("Are we sure?","Enter Horror Form?","Yes","No") != "Yes")
|
||||
return 1
|
||||
H.set_mutantrace(/datum/mutantrace/abomination)
|
||||
H.stat = 0
|
||||
H.real_name = "Shambling Abomination"
|
||||
H.name = "Shambling Abomination"
|
||||
H.update_face()
|
||||
H.update_body()
|
||||
H.update_clothing()
|
||||
logTheThing("combat", H, null, "enters horror form as a changeling, [log_loc(H)].")
|
||||
return 0
|
||||
|
||||
/mob/proc/revert_from_horror_form()
|
||||
if(ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
qdel(H.mutantrace)
|
||||
H.set_mutantrace(null)
|
||||
var/datum/abilityHolder/changeling/C = H.get_ability_holder(/datum/abilityHolder/changeling)
|
||||
if(!C || C.points < 15)
|
||||
boutput(H, __red("You weren't strong enough to change back safely and blacked out!"))
|
||||
H.paralysis += 8
|
||||
else
|
||||
boutput(H, __red("You revert back to your original form. It leaves you weak."))
|
||||
H.weakened += 5
|
||||
if (C)
|
||||
C.points = max(C.points - 15, 0)
|
||||
var/D = pick(C.absorbed_dna)
|
||||
H.real_name = D
|
||||
H.name = D
|
||||
H.bioHolder.CopyOther(C.absorbed_dna[D])
|
||||
H.update_face()
|
||||
H.update_body()
|
||||
H.update_clothing()
|
||||
logTheThing("combat", H, null, "voluntarily leaves horror form as a changeling, [log_loc(H)].")
|
||||
return 0
|
||||
|
||||
/datum/targetable/changeling/scream
|
||||
name = "Horrific Scream"
|
||||
desc = "A terrorizing scream that causes everyone nearby to become flustered."
|
||||
icon_state = "scream"
|
||||
cooldown = 100
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
pointCost = 1
|
||||
abomination_only = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
holder.owner.visible_message(__red("<B>[holder.owner] screeches loudly! The very noise fills you with dread!</B>"))
|
||||
logTheThing("combat", holder.owner, null, "screeches as a changeling in horror form [log_loc(holder.owner)].")
|
||||
playsound(holder.owner.loc, 'sound/voice/creepyshriek.ogg', 80, 1) // cogwerks - using ISN's scary goddamn shriek here
|
||||
|
||||
for (var/mob/living/O in viewers(holder.owner, null))
|
||||
if (O == holder.owner)
|
||||
continue
|
||||
O.apply_sonic_stun(0, 0, 0, 10, 35, rand(0, 2))
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,194 @@
|
||||
/datum/action/bar/icon/abominationDevour
|
||||
duration = 50
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "abom_devour"
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "devour_over"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/changeling/devour/devour
|
||||
|
||||
New(Target, Devour)
|
||||
target = Target
|
||||
devour = Devour
|
||||
..()
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
var/obj/item/grab/G = ownerMob.equipped()
|
||||
|
||||
if (!istype(G) || G.affecting != target || G.state < 1)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onStart()
|
||||
..()
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
ownerMob.show_message("<span style=\"color:blue\">We must hold still for a moment...</span>", 1)
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
if(owner && ownerMob && target && get_dist(owner, target) <= 1 && devour)
|
||||
var/datum/abilityHolder/changeling/C = devour.holder
|
||||
if (istype(C))
|
||||
C.addDna(target)
|
||||
boutput(ownerMob, "<span style=\"color:blue\">We devour [target]!</span>")
|
||||
ownerMob.visible_message(text("<span style=\"color:red\"><B>[ownerMob] hungrily devours [target]!</B></span>"))
|
||||
playsound(ownerMob.loc, 'sound/misc/burp_alien.ogg', 50, 1)
|
||||
logTheThing("combat", ownerMob, target, "devours %target% as a changeling in horror form [log_loc(owner)].")
|
||||
|
||||
target.ghostize()
|
||||
qdel(target)
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
boutput(owner, "<span style=\"color:red\">Our feasting on [target] has been interrupted!</span>")
|
||||
|
||||
/datum/targetable/changeling/devour
|
||||
name = "Devour"
|
||||
desc = "Almost instantly devour a human for DNA."
|
||||
icon_state = "devour"
|
||||
abomination_only = 1
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
restricted_area_check = 2
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
var/mob/living/C = holder.owner
|
||||
|
||||
var/obj/item/grab/G = src.grab_check(null, 1, 1)
|
||||
if (!G || !istype(G))
|
||||
return 1
|
||||
var/mob/living/carbon/human/T = G.affecting
|
||||
|
||||
if (!istype(T))
|
||||
boutput(C, "<span style=\"color:red\">This creature is not compatible with our biology.</span>")
|
||||
return 1
|
||||
if (istype(T.mutantrace, /datum/mutantrace/monkey))
|
||||
boutput(C, "<span style=\"color:red\">Our hunger will not be satisfied by this lesser being.</span>")
|
||||
return 1
|
||||
if (T.bioHolder.HasEffect("husk"))
|
||||
boutput(usr, "<span style=\"color:red\">This creature has already been drained...</span>")
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/icon/abominationDevour(T, src), C)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/changelingAbsorb
|
||||
duration = 250
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "change_absorb"
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "devour_over"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/changeling/absorb/devour
|
||||
var/last_complete = 0
|
||||
|
||||
New(Target, Devour)
|
||||
target = Target
|
||||
devour = Devour
|
||||
..()
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour || !devour.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
var/obj/item/grab/G = ownerMob.equipped()
|
||||
|
||||
if (!istype(G) || G.affecting != target || G.state != 3)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/done = world.time - started
|
||||
var/complete = max(min((done / duration), 1), 0)
|
||||
if (complete >= 0.2 && last_complete < 0.2)
|
||||
boutput(ownerMob, "<span style=\"color:blue\">We extend a proboscis.</span>")
|
||||
ownerMob.visible_message(text("<span style=\"color:red\"><B>[ownerMob] extends a proboscis!</B></span>"))
|
||||
|
||||
if (complete > 0.6 && last_complete <= 0.6)
|
||||
boutput(ownerMob, "<span style=\"color:blue\">We stab [target] with the proboscis.</span>")
|
||||
ownerMob.visible_message(text("<span style=\"color:red\"><B>[ownerMob] stabs [target] with the proboscis!</B></span>"))
|
||||
boutput(target, "<span style=\"color:red\"><B>You feel a sharp stabbing pain!</B></span>")
|
||||
random_brute_damage(target, 40)
|
||||
|
||||
last_complete = complete
|
||||
|
||||
onStart()
|
||||
..()
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour || !devour.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
ownerMob.show_message("<span style=\"color:blue\">We must hold still...</span>", 1)
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/ownerMob = owner
|
||||
if(owner && ownerMob && target && get_dist(owner, target) <= 1 && devour)
|
||||
var/datum/abilityHolder/changeling/C = devour.holder
|
||||
if (istype(C))
|
||||
C.addDna(target)
|
||||
boutput(ownerMob, "<span style=\"color:blue\">We have absorbed [target]!</span>")
|
||||
ownerMob.visible_message(text("<span style=\"color:red\"><B>[ownerMob] sucks the fluids out of [target]!</B></span>"))
|
||||
logTheThing("combat", ownerMob, target, "absorbs %target% as a changeling [log_loc(owner)].")
|
||||
|
||||
target.death(0)
|
||||
target.real_name = "Unknown"
|
||||
target.bioHolder.AddEffect("husk")
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
boutput(owner, "<span style=\"color:red\">Our absorbtion of [target] has been interrupted!</span>")
|
||||
|
||||
/datum/targetable/changeling/absorb
|
||||
name = "Absorb DNA"
|
||||
desc = "Suck the DNA out of a target."
|
||||
icon_state = "absorb"
|
||||
human_only = 1
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
restricted_area_check = 2
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
var/mob/living/C = holder.owner
|
||||
|
||||
var/obj/item/grab/G = src.grab_check(null, 3, 1)
|
||||
if (!G || !istype(G))
|
||||
return 1
|
||||
var/mob/living/carbon/human/T = G.affecting
|
||||
|
||||
if (!istype(T))
|
||||
boutput(C, "<span style=\"color:red\">This creature is not compatible with our biology.</span>")
|
||||
return 1
|
||||
if (istype(T.mutantrace, /datum/mutantrace/monkey))
|
||||
boutput(C, "<span style=\"color:red\">Our hunger will not be satisfied by this lesser being.</span>")
|
||||
return 1
|
||||
if (T.bioHolder.HasEffect("husk"))
|
||||
boutput(usr, "<span style=\"color:red\">This creature has already been drained...</span>")
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/private/icon/changelingAbsorb(T, src), C)
|
||||
return 0
|
||||
@@ -0,0 +1,30 @@
|
||||
/datum/targetable/changeling/mimic_voice
|
||||
name = "Mimic Voice"
|
||||
desc = "Sound like someone else!"
|
||||
icon_state = "mimicvoice"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
human_only = 1
|
||||
can_use_in_container = 1
|
||||
var/last_mimiced_name = ""
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
var/mimic_name = input("Choose a name to mimic:","Mimic Target.",last_mimiced_name) as null|text
|
||||
|
||||
if (!mimic_name)
|
||||
return 1
|
||||
last_mimiced_name = mimic_name //A little qol, probably.
|
||||
|
||||
var/mimic_message = input("Choose something to say:","Mimic Message.","") as null|text
|
||||
|
||||
if (!mimic_message)
|
||||
return 1
|
||||
|
||||
logTheThing("say", holder.owner, mimic_name, "[mimic_message] (<b>Mimicing (%target%)</b>)")
|
||||
var/original_name = holder.owner.real_name
|
||||
holder.owner.real_name = copytext(html_encode(mimic_name), 1, 32)
|
||||
holder.owner.say(mimic_message)
|
||||
holder.owner.real_name = original_name
|
||||
return 0
|
||||
@@ -0,0 +1,162 @@
|
||||
/datum/targetable/changeling/stasis
|
||||
name = "Enter Regenerative Stasis"
|
||||
desc = "Enter a stasis, appearing to be completely dead for 45 seconds, while healing all injuries."
|
||||
icon_state = "stasis"
|
||||
human_only = 1
|
||||
cooldown = 450
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
can_use_in_container = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/datum/abilityHolder/changeling/H = holder
|
||||
if (!istype(H))
|
||||
boutput(holder.owner, __red("That ability is incompatible with our abilities. We should report this to a coder."))
|
||||
return 1
|
||||
|
||||
var/mob/living/carbon/human/C = holder.owner
|
||||
if (alert("Are we sure?","Enter Regenerative Stasis?","Yes","No") != "Yes")
|
||||
boutput(holder.owner, __blue("We change our mind."))
|
||||
return 1
|
||||
|
||||
if(!H.in_fakedeath)
|
||||
boutput(holder.owner, __blue("Repairing our wounds."))
|
||||
logTheThing("combat", holder.owner, null, "enters regenerative stasis as a changeling [log_loc(holder.owner)].")
|
||||
var/list/implants = list()
|
||||
for (var/obj/item/implant/I in holder.owner) //Still preserving implants
|
||||
implants += I
|
||||
|
||||
H.in_fakedeath = 1
|
||||
|
||||
C.lying = 1
|
||||
C.canmove = 0
|
||||
C.set_clothing_icon_dirty()
|
||||
|
||||
C.emote("deathgasp")
|
||||
|
||||
spawn(cooldown)
|
||||
if (C && C.stat != 2)
|
||||
C.HealDamage("All", 1000, 1000)
|
||||
C.take_toxin_damage(-INFINITY)
|
||||
C.take_oxygen_deprivation(-INFINITY)
|
||||
C.paralysis = 0
|
||||
C.stunned = 0
|
||||
C.weakened = 0
|
||||
C.radiation = 0
|
||||
C.health = 100
|
||||
C.updatehealth()
|
||||
C.reagents.clear_reagents()
|
||||
C.lying = 0
|
||||
C.canmove = 1
|
||||
boutput(C, "<span style=\"color:blue\">We have regenerated.</span>")
|
||||
logTheThing("combat", C, null, "[C] finishes regenerative statis as a changeling [log_loc(C)].")
|
||||
C.visible_message(__red("<B>[C] appears to wake from the dead, having healed all wounds.</span>"))
|
||||
for(var/obj/item/implant/I in implants)
|
||||
if (istype(I, /obj/item/implant/projectile))
|
||||
boutput(C, "<span style=\"color:red\">\an [I] falls out of your abdomen.</span>")
|
||||
I.on_remove(C)
|
||||
C.implant.Remove(I)
|
||||
I.set_loc(C.loc)
|
||||
continue
|
||||
|
||||
C.set_clothing_icon_dirty()
|
||||
H.in_fakedeath = 0
|
||||
return 0
|
||||
|
||||
/proc/changeling_super_heal_step(var/mob/living/carbon/human/healed)
|
||||
var/mob/living/carbon/human/C = healed
|
||||
var/list/implants = list()
|
||||
for (var/obj/item/implant/I in C) //Still preserving implants
|
||||
implants += I
|
||||
|
||||
C.reagents.remove_any(10)
|
||||
|
||||
if (!C.burning && C.stat != 2 && (C.health < 100 || !C.limbs.l_arm || !C.limbs.r_arm || !C.limbs.l_leg || !C.limbs.r_leg))
|
||||
if (C.health < 100)
|
||||
C.HealDamage("All", 10, 1)
|
||||
C.take_toxin_damage(-10)
|
||||
C.take_oxygen_deprivation(-10)
|
||||
if (C.blood_volume < 500)
|
||||
C.blood_volume += 10
|
||||
//changelings can get this somehow and it stops speed regen ever turning off otherwise
|
||||
boutput(C, "<span style=\"color:blue\">You feel your flesh knitting back together.</span>")
|
||||
for(var/obj/item/implant/I in implants)
|
||||
if (istype(I, /obj/item/implant/projectile))
|
||||
boutput(C, "<span style=\"color:red\">\an [I] falls out of your abdomen.</span>")
|
||||
I.on_remove(C)
|
||||
C.implant.Remove(I)
|
||||
I.set_loc(C.loc)
|
||||
continue
|
||||
|
||||
if (!C.limbs.l_arm || !C.limbs.r_arm || !C.limbs.l_leg || !C.limbs.r_leg)
|
||||
if(!C.limbs.l_arm && prob(25))
|
||||
if (isabomination(C))
|
||||
C.limbs.l_arm = new /obj/item/parts/human_parts/arm/left/abomination(C)
|
||||
else
|
||||
C.limbs.l_arm = new /obj/item/parts/human_parts/arm/left(C)
|
||||
C.limbs.l_arm.holder = C
|
||||
C.limbs.l_arm:original_holder = C
|
||||
C.limbs.l_arm:set_skin_tone()
|
||||
C.visible_message("<span style=\"color:red\"><B> [C]'s left arm grows back!</span>")
|
||||
C.set_body_icon_dirty()
|
||||
|
||||
if (!C.limbs.r_arm && prob(25))
|
||||
if (isabomination(C))
|
||||
C.limbs.r_arm = new /obj/item/parts/human_parts/arm/right/abomination(C)
|
||||
else
|
||||
C.limbs.r_arm = new /obj/item/parts/human_parts/arm/right(C)
|
||||
C.limbs.r_arm.holder = C
|
||||
C.limbs.r_arm:original_holder = C
|
||||
C.limbs.r_arm:set_skin_tone()
|
||||
C.visible_message("<span style=\"color:red\"><B> [C]'s right arm grows back!</span>")
|
||||
C.set_body_icon_dirty()
|
||||
|
||||
if (!C.limbs.l_leg && prob(25))
|
||||
C.limbs.l_leg = new /obj/item/parts/human_parts/leg/left(C)
|
||||
C.limbs.l_leg.holder = C
|
||||
C.limbs.l_leg:original_holder = C
|
||||
C.limbs.l_leg:set_skin_tone()
|
||||
C.visible_message("<span style=\"color:red\"><B> [C]'s left leg grows back!</span>")
|
||||
C.set_body_icon_dirty()
|
||||
|
||||
if (!C.limbs.r_leg && prob(25))
|
||||
C.limbs.r_leg = new /obj/item/parts/human_parts/leg/right(C)
|
||||
C.limbs.r_leg.holder = C
|
||||
C.limbs.r_leg:original_holder = C
|
||||
C.limbs.r_leg:set_skin_tone()
|
||||
C.visible_message("<span style=\"color:red\"><B> [C]'s right leg grows back!</span>")
|
||||
C.set_body_icon_dirty()
|
||||
|
||||
if (prob(25)) C.visible_message("<span style=\"color:red\"><B>[C]'s flesh is moving and sliding around oddly!</B></span>")
|
||||
|
||||
/datum/targetable/changeling/regeneration
|
||||
name = "Speed Regeneration"
|
||||
desc = "Regenerate your health quickly and rather loudly."
|
||||
icon_state = "speedregen"
|
||||
human_only = 1
|
||||
cooldown = 900
|
||||
pointCost = 10
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
can_use_in_container = 1
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (alert("Are we sure?","Speed Regenerate?","Yes","No") != "Yes")
|
||||
return 1
|
||||
var/mob/living/carbon/human/C = holder.owner
|
||||
if (!istype(C))
|
||||
boutput(holder.owner, __red("We have no idea what we are, but it's damn sure not compatible."))
|
||||
return 1
|
||||
boutput(holder.owner, __blue("Your skin begins reforming around your skeleton."))
|
||||
while(C.health < 100 || !C.limbs.l_arm || !C.limbs.r_arm || !C.limbs.l_leg || !C.limbs.r_leg)
|
||||
if(C.stat == 2)
|
||||
break
|
||||
sleep(30)
|
||||
changeling_super_heal_step(C)
|
||||
@@ -0,0 +1,61 @@
|
||||
/datum/targetable/changeling/spit
|
||||
name = "Toxic Spit"
|
||||
desc = "Spit homing acid at a target, melting their headgear (if any) or burning their face."
|
||||
icon_state = "acid"
|
||||
cooldown = 900
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
sticky = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("We cannot spit without a target."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
holder.owner.visible_message(__red("<b>[holder.owner] spits acid towards [target]!</b>"))
|
||||
logTheThing("combat", holder.owner, MT, "spits acid at %target% as a changeling [log_loc(holder.owner)].")
|
||||
spawn(0)
|
||||
var/obj/overlay/A = new /obj/overlay( holder.owner.loc )
|
||||
A.icon_state = "cbbolt"
|
||||
A.icon = 'icons/obj/projectiles.dmi'
|
||||
A.name = "acid"
|
||||
A.anchored = 0
|
||||
A.density = 0
|
||||
A.layer = EFFECTS_LAYER_UNDER_1
|
||||
A.flags += TABLEPASS
|
||||
A.reagents = new /datum/reagents(10)
|
||||
A.reagents.my_atom = A
|
||||
A.reagents.add_reagent("pacid", 10)
|
||||
|
||||
var/obj/overlay/B = new /obj/overlay( A.loc )
|
||||
B.icon_state = "cbbolt"
|
||||
B.icon = 'icons/obj/projectiles.dmi'
|
||||
B.name = "acid"
|
||||
B.anchored = 1
|
||||
B.density = 0
|
||||
B.layer = OBJ_LAYER
|
||||
|
||||
for(var/i=0, i<20, i++)
|
||||
B.loc = A.loc
|
||||
|
||||
step_to(A,MT,0)
|
||||
if (get_dist(A,MT) == 0)
|
||||
for(var/mob/O in AIviewers(MT, null))
|
||||
O.show_message(__red("<B>[MT.name] is hit by the acid spit!</B>"), 1)
|
||||
A.reagents.reaction(MT)
|
||||
MT.lastattacker = src
|
||||
MT.lastattackertime = world.time
|
||||
qdel(A)
|
||||
qdel(B)
|
||||
return
|
||||
sleep(5)
|
||||
qdel(A)
|
||||
qdel(B)
|
||||
@@ -0,0 +1,112 @@
|
||||
/datum/targetable/changeling/sting
|
||||
name = "Sting"
|
||||
desc = "Transfer some toxins into your target."
|
||||
var/stealthy = 1
|
||||
var/venom_id = "toxin"
|
||||
var/inject_amount = 50
|
||||
cooldown = 900
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
sticky = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("We cannot sting without a target."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("We cannot reach that target with our stinger."))
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
if (!MT.reagents)
|
||||
boutput(holder.owner, __red("That does not hold reagents, apparently."))
|
||||
if (!stealthy)
|
||||
holder.owner.visible_message(__red("<b>[holder.owner] stings [target]!</b>"))
|
||||
else
|
||||
holder.owner.show_message(__blue("We stealthily sting [target]."))
|
||||
MT.reagents.add_reagent(venom_id, inject_amount)
|
||||
logTheThing("combat", holder.owner, MT, "stings %target% with [name] as a changeling [log_loc(holder.owner)].")
|
||||
|
||||
neurotoxin
|
||||
name = "Neurotoxic Sting"
|
||||
desc = "Transfer some neurotoxin into your target."
|
||||
icon_state = "stingneuro"
|
||||
venom_id = "neurotoxin"
|
||||
|
||||
lsd
|
||||
name = "Hallucinogenic Sting"
|
||||
desc = "Transfer some LSD into your target."
|
||||
icon_state = "stinglsd"
|
||||
venom_id = "LSD"
|
||||
inject_amount = 30
|
||||
|
||||
dna
|
||||
name = "DNA Sting"
|
||||
desc = "Injects stable mutagen and the blood of the selected victim into your target."
|
||||
icon_state = "stingdna"
|
||||
venom_id = "dna_mutagen"
|
||||
inject_amount = 15
|
||||
pointCost = 4
|
||||
var/datum/targetable/changeling/dna_target_select/targeting = null
|
||||
|
||||
New()
|
||||
..()
|
||||
|
||||
onAttach(var/datum/abilityHolder/H)
|
||||
targeting = H.addAbility(/datum/targetable/changeling/dna_target_select)
|
||||
targeting.sting = src
|
||||
if (H.owner)
|
||||
object.suffix = "\[[holder.owner.name]\]"
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
MT.reagents.add_reagent("blood", 15, targeting.dna_sting_target)
|
||||
return 0
|
||||
|
||||
/datum/targetable/changeling/dna_target_select
|
||||
name = "Select DNA Sting target"
|
||||
desc = "Select target for DNA sting"
|
||||
icon_state = "stingdna"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
copiable = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
var/datum/bioHolder/dna_sting_target = null
|
||||
var/datum/targetable/changeling/sting = null
|
||||
sticky = 1
|
||||
|
||||
onAttach(var/datum/abilityHolder/G)
|
||||
var/datum/abilityHolder/changeling/H = G
|
||||
if (istype(H))
|
||||
dna_sting_target = H.absorbed_dna[H.absorbed_dna[1]]
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/datum/abilityHolder/changeling/H = holder
|
||||
if (!istype(H))
|
||||
boutput(holder.owner, __red("That ability is incompatible with our abilities. We should report this to a coder."))
|
||||
return 1
|
||||
|
||||
var/target_name = input("Select new DNA sting target!", "DNA Sting Target", null) as null|anything in H.absorbed_dna
|
||||
if (!target_name)
|
||||
boutput(holder.owner, __blue("We change our mind."))
|
||||
return 1
|
||||
|
||||
dna_sting_target = H.absorbed_dna[target_name]
|
||||
if (sting)
|
||||
sting.object.suffix = "\[[target_name]\]"
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,105 @@
|
||||
/datum/targetable/changeling/monkey
|
||||
name = "Lesser Form"
|
||||
desc = "Become something much less powerful."
|
||||
icon_state = "lesser"
|
||||
cooldown = 50
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
can_use_in_container = 1
|
||||
var/last_used_name = null
|
||||
|
||||
onAttach(var/datum/abilityHolder/H)
|
||||
..()
|
||||
last_used_name = H.owner.real_name
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/mob/living/carbon/human/H = holder.owner
|
||||
if (H.mutantrace)
|
||||
if (istype(H.mutantrace, /datum/mutantrace/monkey))
|
||||
if (alert("Are we sure?","Exit this lesser form?","Yes","No") != "Yes")
|
||||
return 1
|
||||
doCooldown()
|
||||
|
||||
H.transforming = 1
|
||||
H.canmove = 0
|
||||
H.icon = null
|
||||
H.invisibility = 101
|
||||
var/atom/movable/overlay/animation = new /atom/movable/overlay( usr.loc )
|
||||
animation.icon_state = "blank"
|
||||
animation.icon = 'icons/mob/mob.dmi'
|
||||
animation.master = src
|
||||
flick("monkey2h", animation)
|
||||
sleep(48)
|
||||
qdel(animation)
|
||||
qdel(H.mutantrace)
|
||||
H.set_mutantrace(null)
|
||||
H.transforming = 0
|
||||
H.canmove = 1
|
||||
H.icon = initial(H.icon)
|
||||
H.invisibility = initial(H.invisibility)
|
||||
H.update_face()
|
||||
H.update_body()
|
||||
H.update_clothing()
|
||||
H.real_name = last_used_name
|
||||
logTheThing("combat", H, null, "leaves lesser form as a changeling, [log_loc(H)].")
|
||||
return 0
|
||||
else if (istype(H.mutantrace, /datum/mutantrace/abomination))
|
||||
boutput(H, "We cannot transform in this form.")
|
||||
return 1
|
||||
else
|
||||
boutput(H, "We cannot transform in this form.")
|
||||
return 1
|
||||
else
|
||||
if (alert("Are we sure?","Assume lesser form?","Yes","No") != "Yes")
|
||||
return 1
|
||||
last_used_name = H.real_name
|
||||
H.monkeyize()
|
||||
logTheThing("combat", H, null, "enters lesser form as a changeling, [log_loc(H)].")
|
||||
return 0
|
||||
|
||||
/datum/targetable/changeling/transform
|
||||
name = "Transform"
|
||||
desc = "Become someone else!"
|
||||
icon_state = "transform"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
human_only = 1
|
||||
can_use_in_container = 1
|
||||
dont_lock_holder = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/datum/abilityHolder/changeling/H = holder
|
||||
if (!istype(H))
|
||||
boutput(holder.owner, __red("That ability is incompatible with our abilities. We should report this to a coder."))
|
||||
return 1
|
||||
|
||||
if (H.absorbed_dna.len < 2)
|
||||
boutput(holder.owner, __red("We need to absorb more DNA to use this ability."))
|
||||
return 1
|
||||
|
||||
var/target_name = input("Select the target DNA: ", "Target DNA", null) as null|anything in H.absorbed_dna
|
||||
if (!target_name)
|
||||
boutput(holder.owner, __blue("We change our mind."))
|
||||
return 1
|
||||
|
||||
var/datum/bioHolder/D = H.absorbed_dna[target_name]
|
||||
|
||||
holder.owner.visible_message(text("<span style=\"color:red\"><B>[holder.owner] transforms!</B></span>"))
|
||||
logTheThing("combat", holder.owner, target_name, "transforms into [target_name] as a changeling [log_loc(holder.owner)].")
|
||||
var/mob/living/carbon/human/C = holder.owner
|
||||
C.real_name = target_name
|
||||
C.bioHolder.CopyOther(D)
|
||||
C.bioHolder.RemoveEffect("husk")
|
||||
if (istype(C))
|
||||
C.set_mutantrace(null)
|
||||
C.update_face()
|
||||
C.update_body()
|
||||
C.update_clothing()
|
||||
return 0
|
||||
@@ -0,0 +1,89 @@
|
||||
/obj/screen/ability/critter
|
||||
clicked(params)
|
||||
var/datum/targetable/critter/spell = owner
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (!isturf(usr.loc))
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
usr:targeting_spell = owner
|
||||
usr.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
|
||||
/datum/abilityHolder/critter
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Abilities"
|
||||
|
||||
// ----------------------------------------
|
||||
// Generic abilities that critters may have
|
||||
// ----------------------------------------
|
||||
|
||||
/datum/targetable/critter
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // TODO.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
var/disabled = 0
|
||||
var/toggled = 0
|
||||
var/is_on = 0 // used if a toggle ability
|
||||
preferred_holder_type = /datum/abilityHolder/critter
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/critter/B = new /obj/screen/ability/critter(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/critter()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (disabled)
|
||||
object.name = "[src.name] (unavailable)"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else if (src.last_cast > world.time)
|
||||
object.name = "[src.name] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else if (toggled)
|
||||
if (is_on)
|
||||
object.name = "[src.name] (on)"
|
||||
object.icon_state = src.icon_state
|
||||
else
|
||||
object.name = "[src.name] (off)"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
object.name = src.name
|
||||
object.icon_state = src.icon_state
|
||||
|
||||
proc/incapacitationCheck()
|
||||
var/mob/living/M = holder.owner
|
||||
return M.restrained() || M.stat || M.paralysis || M.stunned || M.weakened
|
||||
|
||||
castcheck()
|
||||
if (incapacitationCheck())
|
||||
boutput(holder.owner, __red("Not while incapacitated."))
|
||||
return 0
|
||||
if (disabled)
|
||||
boutput(holder.owner, __red("You cannot use that ability at this time."))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
@@ -0,0 +1,33 @@
|
||||
// -----------------
|
||||
// Simple bite skill
|
||||
// -----------------
|
||||
/datum/targetable/critter/bite
|
||||
name = "Chomp"
|
||||
desc = "Chomp down on a mob, causing damage and a short stun."
|
||||
cooldown = 150
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
var/datum/projectile/slam/proj = new
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to bite there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to bite."))
|
||||
return 1
|
||||
playsound(target, "sound/weapons/werewolf_attack1.ogg", 50, 1, -1)
|
||||
var/mob/MT = target
|
||||
MT.TakeDamage("All", 16, 0, 0, DAMAGE_CRUSH)
|
||||
MT.stunned += 2
|
||||
holder.owner.visible_message(__red("<b>[holder.owner] bites [MT]!</b>"), __red("You bite [MT]!"))
|
||||
return 0
|
||||
@@ -0,0 +1,36 @@
|
||||
// -----------------
|
||||
// Simple bite skill
|
||||
// -----------------
|
||||
/datum/targetable/critter/cauterize
|
||||
name = "Cauterize"
|
||||
desc = "Cauterize a mob, stopping all bleeding immediately but inflicting mild fire damage."
|
||||
cooldown = 150
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
var/datum/projectile/slam/proj = new
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to cauterize there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to cauterize."))
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
if (MT.is_heat_resistant())
|
||||
boutput(holder.owner, __red("[MT] cannot be cauterized."))
|
||||
return 1
|
||||
MT.TakeDamage("All", 0, 8, 0, DAMAGE_BURN)
|
||||
holder.owner.visible_message(__blue("<b>[holder.owner] cauterizes [MT]!</b>"), __blue("You cauterize [MT]!"))
|
||||
//if (MT.bleeding)
|
||||
// boutput(MT, __blue("Your bleeding stops!"))
|
||||
return 0
|
||||
@@ -0,0 +1,80 @@
|
||||
// -----------------------------------
|
||||
// Devour using an action as the timer
|
||||
// -----------------------------------
|
||||
|
||||
/datum/action/bar/icon/devourAbility
|
||||
duration = 40
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "critter_devour"
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "devour_over"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/critter/devour/devour
|
||||
|
||||
New(Target, Devour)
|
||||
target = Target
|
||||
devour = Devour
|
||||
..()
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour || !devour.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onStart()
|
||||
..()
|
||||
if(get_dist(owner, target) > 1 || target == null || owner == null || !devour || !devour.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
for(var/mob/O in AIviewers(owner))
|
||||
O.show_message("<span style=\"color:red\"><B>[owner] attempts to devour [target]!</B></span>", 1)
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
var/mob/ownerMob = owner
|
||||
if(owner && ownerMob && target && get_dist(owner, target) <= 1 && devour && devour.cooldowncheck())
|
||||
logTheThing("combat", ownerMob, target, "devours %target%.")
|
||||
for(var/mob/O in AIviewers(ownerMob))
|
||||
O.show_message("<span style=\"color:red\"><B>[owner] devours [target]!</B></span>", 1)
|
||||
playsound(get_turf(ownerMob), pick("sound/misc/burp_alien.ogg"), 50, 0)
|
||||
ownerMob.health = ownerMob.max_health
|
||||
if (target == owner)
|
||||
boutput(owner, "<span class='color:green'>Good. Job.</span>")
|
||||
target.ghostize()
|
||||
devour.actionFinishCooldown()
|
||||
qdel(target)
|
||||
|
||||
/datum/targetable/critter/devour
|
||||
name = "Devour"
|
||||
desc = "After a short delay, instantly devour a mob. Both you and the target must stand still for this."
|
||||
cooldown = 0
|
||||
var/actual_cooldown = 200
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
proc/actionFinishCooldown()
|
||||
cooldown = actual_cooldown
|
||||
doCooldown()
|
||||
cooldown = initial(cooldown)
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to devour there."))
|
||||
return 1
|
||||
if (!istype(target, /mob/living))
|
||||
boutput(holder.owner, __red("Invalid target."))
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to devour."))
|
||||
return 1
|
||||
actions.start(new/datum/action/bar/icon/devourAbility(target, src), holder.owner)
|
||||
return 0
|
||||
@@ -0,0 +1,43 @@
|
||||
// --------------------------------------------------
|
||||
// Fire elemental ability - shoot flames in direction
|
||||
// --------------------------------------------------
|
||||
/datum/targetable/critter/flamethrower
|
||||
name = "Flamethrower"
|
||||
desc = "Throw flames towards a target location up to three squares away."
|
||||
cooldown = 150
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (target && !isturf(target))
|
||||
target = get_turf(target)
|
||||
if (!target)
|
||||
return 1
|
||||
var/turf/OT = get_turf(holder.owner)
|
||||
var/it = 7
|
||||
while (get_dist(OT, target) > 3)
|
||||
target = get_step(target, get_dir(target, OT))
|
||||
it--
|
||||
if (it <= 0)
|
||||
return 1
|
||||
while (get_dist(OT, target) < 3)
|
||||
target = get_step(target, get_dir(OT, target))
|
||||
it--
|
||||
if (it <= 0)
|
||||
return 1
|
||||
if (target == holder.owner || target == OT)
|
||||
return 1
|
||||
playsound(target, "sound/effects/spray.ogg", 50, 1, -1)
|
||||
var/list/L = getline(OT, target)
|
||||
for (var/turf/T in L)
|
||||
if (T == OT)
|
||||
continue
|
||||
fireflash_sm(T, 0, 3000, 0)
|
||||
for (var/mob/living/M in T)
|
||||
if (!M.is_heat_resistant())
|
||||
M.TakeDamage("All", 0, 15, 0, DAMAGE_BURN)
|
||||
M.stunned += 2
|
||||
M.emote("scream")
|
||||
return 0
|
||||
@@ -0,0 +1,72 @@
|
||||
// --------------------
|
||||
// Wendigo style frenzy
|
||||
// --------------------
|
||||
/datum/targetable/critter/frenzy
|
||||
name = "Frenzy"
|
||||
desc = "Go into a bloody frenzy on a weakened target and rip them to shreds."
|
||||
cooldown = 350
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
icon_state = "frenzy"
|
||||
|
||||
var/datum/projectile/slam/proj = new
|
||||
|
||||
cast(atom/target)
|
||||
if (disabled && world.time > last_cast)
|
||||
disabled = 0 // break the deadlock
|
||||
if (disabled)
|
||||
return 1
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
for (var/mob/living/M in target)
|
||||
if (M != src && M.weakened)
|
||||
target = M
|
||||
break
|
||||
if (!ismob(target))
|
||||
boutput(holder.owner, __red("Nothing to frenzy at there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to frenzy."))
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
if (!MT.weakened && !MT.paralysis && !MT.stat)
|
||||
boutput(holder.owner, __red("That is moving around far too much to pounce."))
|
||||
return 1
|
||||
playsound(get_turf(holder.owner), "sound/misc/wendigo_roar.ogg", 80, 1)
|
||||
disabled = 1
|
||||
spawn(0)
|
||||
var/frenz = rand(10, 20)
|
||||
holder.owner.canmove = 0
|
||||
while (frenz > 0 && MT && !MT.disposed)
|
||||
MT.weakened = max(MT.weakened, 2)
|
||||
MT.canmove = 0
|
||||
if (MT.loc)
|
||||
holder.owner.set_loc(MT.loc)
|
||||
holder.owner.stunned = max(holder.owner.stunned, 1)
|
||||
if (holder.owner.stunned > 1 || holder.owner.weakened || holder.owner.paralysis)
|
||||
break
|
||||
playsound(get_turf(holder.owner), "sound/misc/wendigo_maul.ogg", 80, 1)
|
||||
holder.owner.visible_message("<span style=\"color:red\"><b>[holder.owner] [pick("mauls", "claws", "slashes", "tears at", "lacerates", "mangles")] [MT]!</b></span>")
|
||||
holder.owner.dir = pick(cardinal)
|
||||
holder.owner.pixel_x = rand(-5, 5)
|
||||
holder.owner.pixel_y = rand(-5, 5)
|
||||
random_brute_damage(MT, 10)
|
||||
take_bleeding_damage(MT, null, 5, DAMAGE_CUT, 0, get_turf(MT))
|
||||
if(prob(33)) // don't make quite so much mess
|
||||
bleed(MT, 5, 5, get_step(get_turf(MT), pick(alldirs)), 1)
|
||||
sleep(4)
|
||||
frenz--
|
||||
if (MT)
|
||||
MT.canmove = 1
|
||||
doCooldown()
|
||||
disabled = 0
|
||||
holder.owner.pixel_x = 0
|
||||
holder.owner.pixel_y = 0
|
||||
holder.owner.canmove = 1
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,75 @@
|
||||
// ------------------------------------------------
|
||||
// Martian psychic gib using an action as the timer
|
||||
// ------------------------------------------------
|
||||
|
||||
/datum/action/bar/icon/gibstareAbility
|
||||
duration = 60
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "critter_devour"
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "devour_over"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/critter/gibstare/gibstare
|
||||
|
||||
New(Target, Gibstare)
|
||||
target = Target
|
||||
gibstare = Gibstare
|
||||
..()
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
if(!(target in view(owner)) || target == null || owner == null || !gibstare || !gibstare.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onStart()
|
||||
..()
|
||||
if(!(target in view(owner)) || target == null || owner == null || !gibstare || !gibstare.cooldowncheck())
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
for(var/mob/O in AIviewers(owner))
|
||||
O.show_message("<span style=\"color:red\"><B>[owner]</B> stares at [target]!</span>", 1)
|
||||
var/mob/ownerMob = owner
|
||||
playsound(ownerMob.loc, "sound/weapons/phaseroverload.ogg", 100, 1)
|
||||
boutput(target, "<span style=\"color:red\">You feel a horrible pain in your head!</span>")
|
||||
target.stunned += 1
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
var/mob/ownerMob = owner
|
||||
if(owner && ownerMob && target && (target in view(owner)) && gibstare && gibstare.cooldowncheck())
|
||||
logTheThing("combat", ownerMob, target, "gibs %target% using martin gib stare.")
|
||||
for(var/mob/O in AIviewers(ownerMob))
|
||||
O.show_message("<span style=\"color:red\"><b>[target.name]'s</b> head explodes!</span>", 1)
|
||||
if (target == owner)
|
||||
boutput(owner, "<span class='color:green'>Good. Job.</span>")
|
||||
target.gib()
|
||||
gibstare.actionFinishCooldown()
|
||||
|
||||
/datum/targetable/critter/gibstare
|
||||
name = "Psychic Stare"
|
||||
desc = "After a medium delay, instantly devour a mob. You must stand still for this and maintain vision of the target."
|
||||
cooldown = 0
|
||||
var/actual_cooldown = 600
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
proc/actionFinishCooldown()
|
||||
cooldown = actual_cooldown
|
||||
doCooldown()
|
||||
cooldown = initial(cooldown)
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to gib there."))
|
||||
return 1
|
||||
actions.start(new/datum/action/bar/icon/gibstareAbility(target, src), holder.owner)
|
||||
return 0
|
||||
@@ -0,0 +1,33 @@
|
||||
// ----------------------------------
|
||||
// Grow into a bigger form of critter
|
||||
// ----------------------------------
|
||||
/datum/targetable/critter/grow
|
||||
name = "Grow"
|
||||
desc = "Use this to grow into a bigger, better, robuster form."
|
||||
var/newtype = null
|
||||
cooldown = 6000
|
||||
start_on_cooldown = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (!newtype)
|
||||
return 1
|
||||
var/mob/ow = holder.owner
|
||||
if (!ow.mind && !ow.client)
|
||||
return 1
|
||||
var/mob/nw = new newtype(get_turf(ow))
|
||||
if (ow.mind)
|
||||
ow.mind.transfer_to(nw)
|
||||
else if (ow.client)
|
||||
var/client/cli = ow.client
|
||||
cli.mob = nw
|
||||
nw.mind = new /datum/mind()
|
||||
ticker.minds += nw.mind
|
||||
nw.mind.key = cli.key
|
||||
nw.mind.current = nw
|
||||
boutput(nw, __blue("You grow into <b>[nw]</b>!"))
|
||||
qdel(ow)
|
||||
|
||||
// spiderbaby
|
||||
// newtype = /mob/living/critter/spider
|
||||
@@ -0,0 +1,106 @@
|
||||
// ----------------------
|
||||
// Fade into invisibility
|
||||
// ----------------------
|
||||
/datum/action/invisibility
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "invisibility"
|
||||
var/icon = 'icons/mob/critter_ui.dmi'
|
||||
var/icon_state = "invisible_over"
|
||||
var/obj/overlay/iicon = null
|
||||
var/datum/targetable/critter/fadeout/ability = null
|
||||
var/did_fadein = 0
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
if (ability && owner && state == ACTIONSTATE_RUNNING)
|
||||
owner.invisibility = ability.inv_level
|
||||
|
||||
onInterrupt(var/flag = 0)
|
||||
..()
|
||||
if (did_fadein)
|
||||
return
|
||||
did_fadein = 1
|
||||
if (owner)
|
||||
owner.attached_objs -= iicon
|
||||
if (ability)
|
||||
ability.fade_in()
|
||||
else if (owner)
|
||||
owner.invisibility = initial(owner.invisibility)
|
||||
if (iicon)
|
||||
del iicon
|
||||
qdel(src)
|
||||
|
||||
onStart()
|
||||
..()
|
||||
state = ACTIONSTATE_INFINITE
|
||||
if (!owner || !ability)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
ability.last_action = src
|
||||
if (!iicon)
|
||||
iicon = new
|
||||
iicon.mouse_opacity = 0
|
||||
iicon.name = null
|
||||
iicon.icon = icon
|
||||
iicon.icon_state = icon_state
|
||||
iicon.pixel_y = 5
|
||||
owner << iicon
|
||||
|
||||
onDelete()
|
||||
..()
|
||||
if (iicon)
|
||||
del iicon
|
||||
return
|
||||
|
||||
/datum/targetable/critter/fadeout
|
||||
name = "Fade Out"
|
||||
desc = "Become invisible until you move. Invisibility lingers for a few seconds after moving or acting."
|
||||
var/inv_level = 16
|
||||
var/fade_out_icon_state = null
|
||||
var/fade_in_icon_state = null
|
||||
var/fade_anim_length = 3
|
||||
var/linger_time = 30
|
||||
var/datum/action/invisibility/last_action
|
||||
cooldown = 300
|
||||
icon_state = "invisibility"
|
||||
|
||||
cast(atom/target)
|
||||
if (disabled)
|
||||
return 1
|
||||
if (..())
|
||||
return 1
|
||||
disabled = 1
|
||||
boutput(holder.owner, __blue("You fade out of sight."))
|
||||
var/datum/action/invisibility/I = new
|
||||
I.owner = holder.owner
|
||||
I.ability = src
|
||||
var/wait = 5
|
||||
if (fade_out_icon_state)
|
||||
flick(fade_out_icon_state, holder.owner)
|
||||
wait = fade_anim_length
|
||||
else
|
||||
animate(holder.owner, alpha=64, time=5)
|
||||
spawn (wait)
|
||||
holder.owner.invisibility = inv_level
|
||||
holder.owner.alpha = 64
|
||||
actions.start(I, holder.owner)
|
||||
return 0
|
||||
|
||||
proc/fade_in()
|
||||
if (holder.owner)
|
||||
boutput(holder.owner, __red("You fade back into sight!"))
|
||||
disabled = 0
|
||||
doCooldown()
|
||||
spawn(linger_time)
|
||||
holder.owner.invisibility = 0
|
||||
if (fade_in_icon_state)
|
||||
flick(fade_in_icon_state, holder.owner)
|
||||
holder.owner.alpha = 255
|
||||
else
|
||||
holder.owner.alpha = 64
|
||||
animate(holder.owner, alpha=255, time=5)
|
||||
|
||||
wendigo
|
||||
fade_in_icon_state = "wendigo_appear"
|
||||
fade_out_icon_state = "wendigo_melt"
|
||||
fade_anim_length = 12
|
||||
@@ -0,0 +1,44 @@
|
||||
// -------------------------
|
||||
// Martian psychic mindblast
|
||||
// -------------------------
|
||||
/datum/targetable/critter/psyblast
|
||||
name = "Psyblast"
|
||||
desc = "Unleash a powerful psychic blast at a human, knocking them out for a while."
|
||||
cooldown = 300
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living/carbon/human) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to psyblast there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
var/mob/living/carbon/human/MT = target
|
||||
if (!istype(MT))
|
||||
boutput(holder.owner, __red("Nothing to psyblast there."))
|
||||
return 1
|
||||
playsound(MT.loc, "sound/effects/ghost2.ogg", 100, 1)
|
||||
if (istype(MT.head, /obj/item/clothing/head/tinfoil_hat))
|
||||
boutput(MT, "<span style=\"color:blue\">Your tinfoil hat protects you from the psyblast!</span>")
|
||||
boutput(holder.owner, "<span style=\"color:red\">That target is protected against psyblasts.</span>")
|
||||
else
|
||||
boutput(MT, "<span style=\"color:red\">You are blasted by psychic energy!</span>")
|
||||
MT.paralysis += 5
|
||||
MT.stuttering += 60
|
||||
MT.take_brain_damage(20)
|
||||
MT.TakeDamage("head", 0, 5, 0, DAMAGE_BURN)
|
||||
return 0
|
||||
|
||||
martian
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
holder.owner.say("PSYBLAST!", 1)
|
||||
return 0
|
||||
@@ -0,0 +1,127 @@
|
||||
// ------------------------------------------------------------
|
||||
// Experimental: charge-slam using a projectile as a line mover
|
||||
// ------------------------------------------------------------
|
||||
/datum/projectile/slam
|
||||
name = "slam"
|
||||
icon = null
|
||||
icon_state = null
|
||||
power = 1
|
||||
ks_ratio = 0
|
||||
damage_type = D_SPECIAL
|
||||
hit_ground_chance = 0
|
||||
dissipation_delay = 3
|
||||
projectile_speed = 32
|
||||
dissipation_rate = 1
|
||||
shot_sound = null
|
||||
|
||||
|
||||
on_launch(var/obj/projectile/O)
|
||||
if (!("owner" in O.special_data))
|
||||
O.die()
|
||||
return
|
||||
O.special_data["valid_loc"] = get_turf(O)
|
||||
O.special_data["orig_turf"] = get_turf(O)
|
||||
var/datum/targetable/critter/slam/owner = O.special_data["owner"]
|
||||
var/mob/charger = owner.holder.owner
|
||||
O.special_data["charger"] = charger
|
||||
charger.transforming = 1
|
||||
charger.canmove = 0
|
||||
charger.loc = O
|
||||
O.dir = angleToDir(O.angle)
|
||||
O.name = charger.name
|
||||
O.icon = null
|
||||
O.overlays += charger
|
||||
O.transform = null
|
||||
|
||||
tick(var/obj/projectile/O)
|
||||
if (O.disposed)
|
||||
return
|
||||
var/mob/charger = O.special_data["charger"]
|
||||
var/obj/overlay/dummy = new(get_turf(O))
|
||||
dummy.mouse_opacity = 0
|
||||
dummy.name = null
|
||||
dummy.density = 0
|
||||
dummy.anchored = 1
|
||||
dummy.opacity = 0
|
||||
dummy.icon = null
|
||||
dummy.overlays += charger
|
||||
dummy.alpha = 255
|
||||
dummy.pixel_x = O.pixel_x
|
||||
dummy.pixel_y = O.pixel_y
|
||||
dummy.dir = O.dir
|
||||
animate(dummy, alpha=0, time=3)
|
||||
spawn(3)
|
||||
qdel(dummy)
|
||||
|
||||
on_hit(atom/hit, angle, var/obj/projectile/O)
|
||||
O.special_data["valid_loc"] = get_turf(hit)
|
||||
var/mob/charger = O.special_data["charger"]
|
||||
if (isturf(hit))
|
||||
hit.visible_message(__red("[charger] slams into [hit]!"), "You hear something slam!")
|
||||
boutput(charger, __red("You slam into [hit]! Ouch!"))
|
||||
charger.stunned = max(charger.stunned, 3)
|
||||
playsound(get_turf(hit), "sound/weapons/genhit1.ogg", 50, 1, -1)
|
||||
else if (isobj(hit))
|
||||
var/obj/H = hit
|
||||
if (H.anchored)
|
||||
hit.visible_message(__red("[charger] slams into [hit]!"), "You hear something slam!")
|
||||
boutput(charger, __red("You slam into [hit]! Ouch!"))
|
||||
charger.stunned = max(charger.stunned, 3)
|
||||
playsound(get_turf(hit), "sound/weapons/genhit1.ogg", 50, 1, -1)
|
||||
else
|
||||
hit.visible_message(__red("[charger] slams into [hit]!"), "You hear something slam!")
|
||||
playsound(get_turf(hit), "sound/weapons/genhit1.ogg", 50, 1, -1)
|
||||
boutput(charger, __red("You slam into [hit]!"))
|
||||
var/kbdir = angleToDir(angle)
|
||||
step(H, kbdir, 2)
|
||||
if (prob(10))
|
||||
spawn(2)
|
||||
step(H, kbdir, 2)
|
||||
else if (ismob(hit))
|
||||
var/mob/M = hit
|
||||
playsound(get_turf(hit), "sound/weapons/genhit1.ogg", 50, 1, -1)
|
||||
hit.visible_message(__red("[charger] slams into [hit]!"), "You hear something slam!")
|
||||
boutput(charger, __red("You slam into [hit]!"))
|
||||
boutput(M, __red("<b>[charger] slams into you!</b>"))
|
||||
logTheThing("combat", charger, M, "slams %target%.")
|
||||
var/kbdir = angleToDir(angle)
|
||||
step(M, kbdir, 2)
|
||||
M.weakened = max(M.weakened, 4)
|
||||
|
||||
on_end(var/obj/projectile/O)
|
||||
var/keys = ""
|
||||
for (var/dp in O.special_data)
|
||||
keys = "[keys][dp], "
|
||||
var/mob/charger = O.special_data["charger"]
|
||||
charger.transforming = 0
|
||||
charger.canmove = 1
|
||||
charger.loc = get_turf(O)
|
||||
charger.dir = get_dir(O.special_data["orig_turf"], charger.loc)
|
||||
if (!charger.loc)
|
||||
charger.loc = O.special_data["valid_loc"]
|
||||
|
||||
/datum/targetable/critter/slam
|
||||
name = "Slam"
|
||||
desc = "Charge over a short distance, until you hit a mob or an object. Knocks down mobs."
|
||||
cooldown = 100
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
var/datum/projectile/slam/proj = new
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
var/turf/T = get_turf(target)
|
||||
if (!T)
|
||||
return 1
|
||||
var/mob/M = holder.owner
|
||||
var/turf/S = get_turf(M)
|
||||
var/obj/projectile/O = initialize_projectile_ST(S, proj, T)
|
||||
if (!O)
|
||||
return 1
|
||||
if (!O.was_setup)
|
||||
O.setup()
|
||||
O.special_data["owner"] = src
|
||||
O.launch()
|
||||
return 0
|
||||
@@ -0,0 +1,52 @@
|
||||
// -------------------------
|
||||
// Inject someone with venom
|
||||
// -------------------------
|
||||
/datum/targetable/critter/sting
|
||||
name = "Venomous Sting"
|
||||
desc = "Transfer some toxins into your target."
|
||||
var/stealthy = 0
|
||||
var/venom_id = "toxin"
|
||||
var/inject_amount = 25
|
||||
cooldown = 600
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to sting there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to sting."))
|
||||
return 1
|
||||
var/mob/MT = target
|
||||
if (!MT.reagents)
|
||||
boutput(holder.owner, __red("That does not hold reagents, apparently."))
|
||||
if (!stealthy)
|
||||
holder.owner.visible_message(__red("<b>[holder.owner] stings [target]!</b>"))
|
||||
else
|
||||
holder.owner.show_message(__blue("You stealthily sting [target]."))
|
||||
MT.reagents.add_reagent(venom_id, inject_amount)
|
||||
|
||||
ice
|
||||
name = "Freezing Sting"
|
||||
desc = "Transfer some cryostylane into your target."
|
||||
venom_id = "cryostylane"
|
||||
|
||||
sedative
|
||||
name = "Sedative Sting"
|
||||
desc = "Transfer some morphine into your target."
|
||||
venom_id = "morphine"
|
||||
|
||||
eggs
|
||||
name = "Plant Eggs"
|
||||
desc = "Inject eggs into your target."
|
||||
venom_id = "spidereggs"
|
||||
inject_amount = 6
|
||||
@@ -0,0 +1,32 @@
|
||||
// ------
|
||||
// Tackle
|
||||
// ------
|
||||
/datum/targetable/critter/tackle
|
||||
name = "Tackle"
|
||||
desc = "Tackle a mob, making them fall over."
|
||||
cooldown = 150
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
|
||||
var/datum/projectile/slam/proj = new
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (isobj(target))
|
||||
target = get_turf(target)
|
||||
if (isturf(target))
|
||||
target = locate(/mob/living) in target
|
||||
if (!target)
|
||||
boutput(holder.owner, __red("Nothing to tackle there."))
|
||||
return 1
|
||||
if (target == holder.owner)
|
||||
return 1
|
||||
if (get_dist(holder.owner, target) > 1)
|
||||
boutput(holder.owner, __red("That is too far away to tackle."))
|
||||
return 1
|
||||
playsound(get_turf(target), "sound/weapons/genhit1.ogg", 50, 1, -1)
|
||||
var/mob/MT = target
|
||||
MT.weakened += 3
|
||||
holder.owner.visible_message(__red("<b>[holder.owner] tackles [MT]!</b>"), __red("You tackle [MT]!"))
|
||||
return 0
|
||||
@@ -0,0 +1,27 @@
|
||||
// ---------------------
|
||||
// Martian teleportation
|
||||
// ---------------------
|
||||
/datum/targetable/critter/teleport
|
||||
name = "Teleport"
|
||||
desc = "Phase yourself to a nearby visible spot."
|
||||
cooldown = 300
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (!isturf(target))
|
||||
target = get_turf(target)
|
||||
if (target == get_turf(holder.owner))
|
||||
return 1
|
||||
var/turf/T = target
|
||||
holder.owner.set_loc(T)
|
||||
spawn()
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(5, 1, holder.owner)
|
||||
s.start()
|
||||
playsound(T, "sound/effects/ghost2.ogg", 100, 1)
|
||||
holder.owner.say("TELEPORT!", 1)
|
||||
return 0
|
||||
@@ -0,0 +1,111 @@
|
||||
/obj/screen/ability/topBar/cruiser
|
||||
clicked(params)
|
||||
var/datum/targetable/cruiser/spell = owner
|
||||
var/datum/abilityHolder/holder = owner.holder
|
||||
|
||||
|
||||
if(params["left"] && params["ctrl"])
|
||||
if(owner.waiting_for_hotkey)
|
||||
holder.cancel_action_binding()
|
||||
else
|
||||
owner.waiting_for_hotkey = 1
|
||||
boutput(usr, "<span style=\"color:blue\">Please press a number to bind this ability to...</span>")
|
||||
else if(params["left"])
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
usr:targeting_spell = owner
|
||||
usr.update_cursor()
|
||||
return
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
|
||||
owner.holder.updateButtons()
|
||||
|
||||
/datum/abilityHolder/cruiser
|
||||
topBarRendered = 1
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Cruiser Controls"
|
||||
|
||||
// ----------------------------------------
|
||||
// Controls for the cruiser ships.
|
||||
// ----------------------------------------
|
||||
|
||||
/datum/targetable/cruiser
|
||||
icon = 'icons/mob/cruiser_ui.dmi'
|
||||
icon_state = ""
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
check_range = 0
|
||||
var/disabled = 0
|
||||
var/toggled = 0
|
||||
var/is_on = 0 // used if a toggle ability
|
||||
preferred_holder_type = /datum/abilityHolder/cruiser
|
||||
ignore_sticky_cooldown = 1
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/topBar/cruiser/B = new /obj/screen/ability/topBar/cruiser(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/topBar/cruiser()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (disabled)
|
||||
object.name = "[src.name] (unavailable)"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else if (src.last_cast > world.time)
|
||||
object.name = "[src.name] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else if (toggled)
|
||||
if (is_on)
|
||||
object.name = "[src.name] (on)"
|
||||
object.icon_state = src.icon_state
|
||||
else
|
||||
object.name = "[src.name] (off)"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
object.name = src.name
|
||||
object.icon_state = src.icon_state
|
||||
|
||||
proc/incapacitationCheck()
|
||||
var/mob/living/M = holder.owner
|
||||
return M.restrained() || M.stat || M.paralysis || M.stunned || M.weakened
|
||||
|
||||
castcheck()
|
||||
if (incapacitationCheck())
|
||||
boutput(holder.owner, __red("Not while incapacitated."))
|
||||
return 0
|
||||
if (disabled)
|
||||
boutput(holder.owner, __red("You cannot use that ability at this time."))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
doCooldown()
|
||||
if (!holder)
|
||||
return
|
||||
last_cast = world.time + cooldown
|
||||
holder.updateButtons()
|
||||
spawn(cooldown + 5)
|
||||
holder.updateButtons()
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
@@ -0,0 +1,171 @@
|
||||
/datum/targetable/cruiser/cancel_camera
|
||||
name = "Cancel camera view"
|
||||
desc = "Cancels your current camera view."
|
||||
icon_state = "cancelcam"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/mob/M = holder.owner
|
||||
M.set_eye(null)
|
||||
M.client.view = world.view
|
||||
holder.removeAbility(/datum/targetable/cruiser/cancel_camera)
|
||||
|
||||
/datum/targetable/cruiser/exit_pod
|
||||
name = "Exit Pod"
|
||||
desc = "Exit the pod you are currently in."
|
||||
icon_state = "cruiser_exit"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1 // Dunno about your WIP stuff. Adjust as needed.
|
||||
ignore_holder_lock = 1
|
||||
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
if(istype(holder.owner.loc, /obj/machinery/cruiser_destroyable/cruiser_pod))
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
C.exitPod(holder.owner)
|
||||
|
||||
/datum/targetable/cruiser/warp
|
||||
name = "Warp"
|
||||
desc = "Warp to a beacon."
|
||||
icon_state = "warp"
|
||||
cooldown = 10
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.warp()
|
||||
|
||||
/datum/targetable/cruiser/fire_weapons
|
||||
name = "Fire Weapons"
|
||||
desc = "Fire the cruisers main weapons at the specified target."
|
||||
icon_state = "cruiser_shoot"
|
||||
cooldown = 10
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
sticky = 1
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
cooldown = P.fireAt(target)
|
||||
|
||||
/datum/targetable/cruiser/shield_overload
|
||||
name = "Overload shield (90 Power/5)"
|
||||
desc = "Overloads the cruiser's shields, providing increased shield regeneration even during sustained damage, for 15 seconds."
|
||||
icon_state = "shieldboost"
|
||||
cooldown = 200
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.overload_shields()
|
||||
|
||||
/datum/targetable/cruiser/weapon_overload
|
||||
name = "Overload weapons (90 Power/5)"
|
||||
desc = "Overloads the cruiser's weapons, reducing cooldown times for 10 seconds."
|
||||
icon_state = "weaponboost"
|
||||
cooldown = 250
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.overload_weapons()
|
||||
|
||||
/datum/targetable/cruiser/shield_modulation
|
||||
name = "Modulate shields (90 Power, Toggle)"
|
||||
desc = "Continually modulates the frequency of the cruiser's shields while active, eliminating the weakness to energy weapons."
|
||||
icon_state = "shieldmod"
|
||||
cooldown = 10
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.toggleShieldModulation()
|
||||
|
||||
/datum/targetable/cruiser/firemode
|
||||
name = "Switch fire mode"
|
||||
desc = "Changes which weapons fire."
|
||||
icon_state = "firemode"
|
||||
cooldown = 0
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.switchFireMode()
|
||||
|
||||
/datum/targetable/cruiser/ram
|
||||
name = "Ramming mode"
|
||||
desc = "Enabled ramming mode."
|
||||
icon_state = "ram"
|
||||
cooldown = 100
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/machinery/cruiser_destroyable/cruiser_pod/C = holder.owner.loc
|
||||
var/area/ship_interior/I = C.loc.loc
|
||||
var/obj/machinery/cruiser/P = I.ship
|
||||
P.enableRamming()
|
||||
@@ -0,0 +1,166 @@
|
||||
// Converted everything related to grinches from client procs to ability holders and used
|
||||
// the opportunity to do some clean-up as well (Convair880).
|
||||
|
||||
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
||||
|
||||
/mob/proc/make_grinch()
|
||||
if (ishuman(src) || iscritter(src))
|
||||
if (ishuman(src))
|
||||
var/datum/abilityHolder/grinch/A = src.get_ability_holder(/datum/abilityHolder/grinch)
|
||||
if (A && istype(A))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/grinch/G = src.add_ability_holder(/datum/abilityHolder/grinch)
|
||||
G.addAbility(/datum/targetable/grinch/vandalism)
|
||||
G.addAbility(/datum/targetable/grinch/poison)
|
||||
G.addAbility(/datum/targetable/grinch/instakill)
|
||||
G.addAbility(/datum/targetable/grinch/grinch_cloak)
|
||||
|
||||
spawn (25) // Don't remove.
|
||||
if (src) src.assign_gimmick_skull()
|
||||
|
||||
else if (iscritter(src))
|
||||
var/mob/living/critter/C = src
|
||||
|
||||
if (isnull(C.abilityHolder)) // They do have a critter AH by default...or should.
|
||||
var/datum/abilityHolder/grinch/A2 = C.add_ability_holder(/datum/abilityHolder/grinch)
|
||||
if (!A2 || !istype(A2, /datum/abilityHolder/))
|
||||
return
|
||||
|
||||
C.abilityHolder.addAbility(/datum/targetable/grinch/vandalism)
|
||||
C.abilityHolder.addAbility(/datum/targetable/grinch/poison)
|
||||
C.abilityHolder.addAbility(/datum/targetable/grinch/instakill)
|
||||
C.abilityHolder.addAbility(/datum/targetable/grinch/grinch_cloak)
|
||||
|
||||
if (src.mind && src.mind.special_role != "omnitraitor")
|
||||
src << browse(grabResource("html/traitorTips/grinchTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
else return
|
||||
|
||||
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/grinch
|
||||
clicked(params)
|
||||
var/datum/targetable/grinch/spell = owner
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (!isturf(owner.holder.owner.loc))
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this ability here.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
return
|
||||
|
||||
/datum/abilityHolder/grinch
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Grinch"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">You aren't strong enough to use this ability.</span>"
|
||||
|
||||
/////////////////////////////////////////////// Grinch spell parent ////////////////////////////
|
||||
|
||||
/datum/targetable/grinch
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // No custom sprites yet.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
pointCost = 0
|
||||
preferred_holder_type = /datum/abilityHolder/grinch
|
||||
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
||||
var/not_when_handcuffed = 0
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/grinch/B = new /obj/screen/ability/grinch(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
return
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/grinch()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
return
|
||||
|
||||
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
switch (stunned_only_is_okay)
|
||||
if (0)
|
||||
if (M.stat != 0 || M.stunned > 0 || M.paralysis > 0 || M.weakened > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
if (1)
|
||||
if (M.stat != 0 || M.paralysis > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
else
|
||||
return 1
|
||||
|
||||
castcheck()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 0
|
||||
|
||||
if (!(ishuman(M) || iscritter(M)))
|
||||
boutput(M, __red("You cannot use any powers in your current form."))
|
||||
return 0
|
||||
|
||||
if (M.transforming)
|
||||
boutput(M, __red("You can't use any powers right now."))
|
||||
return 0
|
||||
|
||||
if (incapacitation_check(src.when_stunned) != 1)
|
||||
boutput(M, __red("You can't use this ability while incapacitated!"))
|
||||
return 0
|
||||
|
||||
if (src.not_when_handcuffed == 1 && M.restrained())
|
||||
boutput(M, __red("You can't use this ability when restrained!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
return
|
||||
@@ -0,0 +1,55 @@
|
||||
/datum/targetable/grinch/grinch_cloak
|
||||
name = "Activate cloak (temp.)"
|
||||
desc = "Activates a cloaking ability for a limited amount of time."
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 3600
|
||||
start_on_cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 0
|
||||
var/cloak_duration = 120
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (iscritter(M)) // Placeholder because only humans use bioeffects at the moment.
|
||||
if (M.invisibility != 0)
|
||||
boutput(M, __red("You are already invisible."))
|
||||
return 1
|
||||
|
||||
M.invisibility = 2
|
||||
M.UpdateOverlays(image('icons/mob/mob.dmi', "icon_state" = "shield"), "shield")
|
||||
boutput(M, __blue("<b>Your cloak will remain active for the next [src.cloak_duration / 60] minutes.</b>"))
|
||||
|
||||
spawn (src.cloak_duration * 10)
|
||||
if (M && iscritter(M))
|
||||
M.invisibility = 0
|
||||
M.UpdateOverlays(null, "shield")
|
||||
boutput(M, __red("<b>You are no longer invisible.</b>"))
|
||||
|
||||
else if (ishuman(M))
|
||||
var/mob/living/carbon/human/MM = M
|
||||
if (!MM.bioHolder)
|
||||
boutput(MM, __red("You can't use this ability in your current form."))
|
||||
return 1
|
||||
|
||||
if (MM.bioHolder.HasEffect("chameleon"))
|
||||
boutput(M, __red("You are already invisible."))
|
||||
return 1
|
||||
else
|
||||
var/datum/bioEffect/power/chameleon/CC = MM.bioHolder.AddEffect("chameleon", 0, src.cloak_duration)
|
||||
if (CC && istype(CC))
|
||||
CC.active = 1 // Important!
|
||||
MM.set_body_icon_dirty()
|
||||
boutput(M, __blue("<b>Your chameleon cloak is available for the next [src.cloak_duration / 60] minutes. Stand still to become invisible.</b>"))
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,47 @@
|
||||
/datum/targetable/grinch/instakill
|
||||
name = "Murder"
|
||||
desc = "Induces instant cardiac arrest in a target."
|
||||
targeted = 1
|
||||
target_anything = 0
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 4800
|
||||
start_on_cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to kill yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("It would be a waste of time to murder the dead."))
|
||||
return 1
|
||||
|
||||
if (!iscarbon(target))
|
||||
boutput(M, __red("[target] is immune to the disease."))
|
||||
return 1
|
||||
|
||||
var/mob/living/L = target
|
||||
|
||||
playsound(M.loc, 'sound/misc/loudcrunch.ogg', 75, 1, -1)
|
||||
M.visible_message("<span style=\"color:red\"><b>[M] shrinks [L]'s heart down two sizes too small!</b></span>")
|
||||
L.add_fingerprint(M) // Why not leave some forensic evidence?
|
||||
L.contract_disease(/datum/ailment/disease/flatline, null, null, 1) // path, name, strain, bypass resist
|
||||
|
||||
logTheThing("combat", M, L, "uses the murder ability to induce cardiac arrest on %target% at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,74 @@
|
||||
/datum/targetable/grinch/poison
|
||||
name = "Poison food"
|
||||
desc = "Ruin a food item or drink by adding horrible poison to it."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 1800
|
||||
start_on_cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
var/list/the_poison = list("coniine", "cyanide", "curare")
|
||||
var/amount_per_poison = 7
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target)
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to poison yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
// Written in such a way that adding other reagent containers (e.g. medicine) would be trivial.
|
||||
var/obj/item/reagent_containers/RC = null
|
||||
var/attempt_success = 0
|
||||
|
||||
if (istype(target, /obj/item/reagent_containers/food)) // Food and drinking glass/bottle parent.
|
||||
RC = target
|
||||
else
|
||||
boutput(M, __red("You can't poison [target], only food items and drinks."))
|
||||
return 1
|
||||
|
||||
if (RC && istype(RC))
|
||||
if (src.the_poison.len > 1)
|
||||
if (!RC.reagents)
|
||||
RC.reagents = new /datum/reagents(src.amount_per_poison * src.the_poison.len)
|
||||
RC.reagents.my_atom = RC
|
||||
|
||||
if (RC.reagents)
|
||||
for (var/P in src.the_poison)
|
||||
if (RC.reagents.total_volume + src.amount_per_poison >= RC.reagents.maximum_volume)
|
||||
RC.reagents.maximum_volume += src.amount_per_poison
|
||||
RC.reagents.add_reagent(P, src.amount_per_poison)
|
||||
|
||||
if (istype(RC, /obj/item/reagent_containers/food/))
|
||||
var/obj/item/reagent_containers/food/F = RC
|
||||
F.festivity -= 3
|
||||
|
||||
RC.add_fingerprint(M)
|
||||
attempt_success = 1
|
||||
else
|
||||
attempt_success = 0
|
||||
else
|
||||
attempt_success = 0
|
||||
else
|
||||
attempt_success = 0
|
||||
|
||||
if (attempt_success == 1)
|
||||
boutput(M, __blue("You successfully poisoned [target]."))
|
||||
logTheThing("combat", M, null, "poisons [target] [log_reagents(target)] at [log_loc(M)].")
|
||||
return 0
|
||||
else
|
||||
boutput(M, __red("You failed to poison [target]."))
|
||||
return 1
|
||||
@@ -0,0 +1,59 @@
|
||||
/datum/targetable/grinch/vandalism
|
||||
name = "Vandalize"
|
||||
desc = "Drop christmas cheer via graffiti and acts of destruction."
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 1800
|
||||
start_on_cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
var/objects_fucked_up = 0
|
||||
|
||||
for (var/obj/xmastree/X in oview(1, M))
|
||||
boutput(M, __blue("You set the christmas tree on fire!"))
|
||||
X.change_fire_state(1) // Christmas cheer modifier included.
|
||||
objects_fucked_up++
|
||||
|
||||
for (var/obj/stocking/S in oview(1, M))
|
||||
if (S.booby_trapped)
|
||||
continue
|
||||
S.booby_trapped = 1
|
||||
boutput(M, __blue("You put a venomous snake in the stocking!"))
|
||||
objects_fucked_up++
|
||||
|
||||
for (var/obj/decal/D in oview(1, M))
|
||||
if (istype(D, /obj/decal/garland) || istype(D, /obj/decal/tinsel) || istype(D, /obj/decal/xmas_lights))
|
||||
boutput(M, __blue("You tear down [D] and stomp all over it!"))
|
||||
modify_christmas_cheer(-1)
|
||||
objects_fucked_up++
|
||||
qdel(D)
|
||||
|
||||
for (var/turf/simulated/wall/T in oview(1, M))
|
||||
if (locate(/obj/decal/cleanable/grinch_graffiti) in T)
|
||||
continue
|
||||
boutput(M, __blue("You scrawl graffiti all over the wall!"))
|
||||
new /obj/decal/cleanable/grinch_graffiti(T)
|
||||
modify_christmas_cheer(-1)
|
||||
objects_fucked_up++
|
||||
|
||||
if (objects_fucked_up > 0)
|
||||
M.emote("laugh")
|
||||
M.visible_message("<span style=\"color:red\">[M] laughs smugly!</span>")
|
||||
logTheThing("combat", M, null, "uses the vandalize ability at [log_loc(M)].")
|
||||
return 0
|
||||
else
|
||||
boutput(M, __red("You couldn't find anything to vandalize. You should try again near some walls or christmas decorations."))
|
||||
return 1
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/targetable/predator/predator_trophycount
|
||||
name = "Check trophy value"
|
||||
desc = "Displays the combined value of all trophies in your possession."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 3
|
||||
not_when_handcuffed = 0
|
||||
predator_only = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
var/count = M.get_skull_value()
|
||||
|
||||
if (count <= 0)
|
||||
boutput(M, __red("<b>Combined trophy value: 0</b>"))
|
||||
else
|
||||
boutput(M, __blue("<b>Combined trophy value: [count]</b>"))
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,72 @@
|
||||
/datum/targetable/predator/predator_gearspawn
|
||||
name = "Order hunting gear"
|
||||
desc = "Teleports hunting gear to your location."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 0
|
||||
predator_only = 0
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !ishuman(M))
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/private/icon/predator_transform(src), M)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/predator_transform
|
||||
duration = 50
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_ACTION
|
||||
id = "predator_transform"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "grabbed"
|
||||
var/datum/targetable/predator/predator_gearspawn/transform
|
||||
|
||||
New(Transform)
|
||||
transform = Transform
|
||||
..()
|
||||
|
||||
onStart()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (M == null || !ishuman(M) || M.stat != 0 || M.paralysis > 0 || !transform)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
boutput(M, __red("<B>Request acknowledged. You must stand still.</B>"))
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (M == null || !ishuman(M) || M.stat != 0 || M.paralysis > 0 || !transform)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/living/carbon/human/M = owner
|
||||
var/datum/abilityHolder/H = transform.holder
|
||||
|
||||
if (M.predator_transform() != 1)
|
||||
boutput(M, __red("Gearspawn failed. Make sure you're a human and try again later."))
|
||||
else
|
||||
H.removeAbility(/datum/targetable/predator/predator_gearspawn)
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
boutput(M, __red("You were interrupted!"))
|
||||
@@ -0,0 +1,180 @@
|
||||
/datum/targetable/predator/predator_taketrophy
|
||||
name = "Take trophy"
|
||||
desc = "Retrieves a trophy skull from the victim or severed head, mutilating them in the process."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
predator_only = 1
|
||||
restricted_area_check = 2
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target)
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to take your own skull?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (!istype(target, /obj/item/organ/head))
|
||||
if (!ishuman(target)) // Only human mobs and severed human heads have a skull.
|
||||
if (issilicon(target))
|
||||
boutput(M, __red("Mechanical trophies are of no interest to you."))
|
||||
return 1
|
||||
|
||||
else if (istype(target, /mob/living/carbon/wall))
|
||||
boutput(M, __red("This prey is so weak you daren't sully your claws on it!"))
|
||||
return 1
|
||||
|
||||
else
|
||||
boutput(M, __red("There's no trophy to be found here."))
|
||||
return 1
|
||||
|
||||
else
|
||||
var/mob/living/carbon/human/HH = target
|
||||
if (!ischangeling(HH) && (ismonkey(HH) || HH.bioHolder && HH.bioHolder.HasEffect("monkey"))) // Lesser form doesn't count.
|
||||
boutput(M, __red("This pitiful creature isn't worth your time."))
|
||||
return 1
|
||||
|
||||
if (HH.stat != 2)
|
||||
boutput(M, __red("It would be dishonorable to do that to something you haven't killed yet!"))
|
||||
return 1
|
||||
|
||||
else
|
||||
var/obj/item/organ/head/SH = target
|
||||
if (!(SH.skull && istype(SH.skull, /obj/item/skull/)))
|
||||
boutput(M, __red("The skull appears to be missing."))
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/private/icon/predator_taketrophy(target, src), M)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/predator_taketrophy
|
||||
duration = 60
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "predator_taketrophy"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "grabbed"
|
||||
var/target
|
||||
var/datum/targetable/predator/predator_taketrophy/trophy
|
||||
|
||||
New(Target, Trophy)
|
||||
target = Target
|
||||
trophy = Trophy
|
||||
..()
|
||||
|
||||
onStart()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/A = trophy.holder
|
||||
|
||||
if (ismob(target))
|
||||
var/mob/living/HH = target
|
||||
if (!trophy || get_dist(M, HH) > trophy.max_range || HH == null || M == null || !ishuman(HH) || HH.stat != 2)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
else
|
||||
var/obj/item/organ/head/SH = target
|
||||
if (!trophy || get_dist(M, SH) > trophy.max_range || SH == null || M == null || !istype(SH) || !(SH.skull && istype(SH.skull, /obj/item/skull/)))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] unsheaths [his_or_her(M)] claws and begins to cut into [target]!</B></span>")
|
||||
A.locked = 1
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (ismob(target))
|
||||
var/mob/living/HH = target
|
||||
if (!trophy || get_dist(M, HH) > trophy.max_range || HH == null || M == null || !ishuman(HH) || HH.stat != 2)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
else
|
||||
var/obj/item/organ/head/SH = target
|
||||
if (!trophy || get_dist(M, SH) > trophy.max_range || SH == null || M == null || !istype(SH) || !(SH.skull && istype(SH.skull, /obj/item/skull/)))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/A = trophy.holder
|
||||
A.locked = 0
|
||||
|
||||
var/tvalue = 0
|
||||
var/no_of_skulls = 0
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><b>[M] completely rips [target] apart!</b></span>")
|
||||
|
||||
// Assign_gimmick_skull() takes care of skull replacements.
|
||||
if (ismob(target))
|
||||
var/mob/living/carbon/human/HH = target
|
||||
if (ishuman(HH))
|
||||
for (var/obj/item/W in HH)
|
||||
if (istype(W, /obj/item/skull/))
|
||||
var/obj/item/skull/S = W
|
||||
S.name = "[HH.real_name]'s skull"
|
||||
tvalue += S.value // Can might have another skull in their pocket, who knows.
|
||||
no_of_skulls++
|
||||
S.set_loc(get_turf(HH)) // We always want to drop that skull, since gib ejectables are a RNG thing.
|
||||
else
|
||||
HH.u_equip(W)
|
||||
if (W)
|
||||
W.set_loc(get_turf(HH))
|
||||
W.dropped(HH)
|
||||
W.layer = initial(W.layer)
|
||||
|
||||
logTheThing("combat", M, HH, "uses take trophy on %target%, gibbing them at [log_loc(M)].")
|
||||
HH.gib(1)
|
||||
|
||||
else
|
||||
var/obj/item/organ/head/SH = target
|
||||
if (istype(SH) && SH.skull && istype(SH.skull, /obj/item/skull/))
|
||||
var/obj/item/skull/S2 = SH.skull
|
||||
tvalue += S2.value
|
||||
no_of_skulls++
|
||||
S2.set_loc(get_turf(SH))
|
||||
SH.update_icon()
|
||||
|
||||
gibs(get_turf(SH))
|
||||
qdel(SH)
|
||||
|
||||
switch (no_of_skulls)
|
||||
if (0)
|
||||
boutput(M, __red("<b>Their skull was missing. No trophy for you.</b>"))
|
||||
if (1)
|
||||
if (tvalue <= 0)
|
||||
boutput(M, __red("<b>This trophy is completely worthless!</b>"))
|
||||
if (tvalue == 1)
|
||||
boutput(M, __blue("<b>This trophy has a value of [tvalue].</b>"))
|
||||
if (tvalue > 1)
|
||||
boutput(M, __blue("<b>You have slain a powerful opponent!<br>This trophy has a value of [tvalue].</b>"))
|
||||
else
|
||||
boutput(M, __blue("<b>You found mulitple trophies. They have a combined value of [tvalue].</b>"))
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/A = trophy.holder
|
||||
|
||||
A.locked = 0
|
||||
boutput(M, __red("Your attempt to take the trophy was interrupted!"))
|
||||
@@ -0,0 +1,219 @@
|
||||
/datum/targetable/werewolf/werewolf_feast
|
||||
name = "Maul victim"
|
||||
desc = "Feast on the target to quell your hunger."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
werewolf_only = 1
|
||||
restricted_area_check = 2
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to maul yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (!ishuman(target)) // Critter mobs include robots and combat drones. There's not a lot of meat on them.
|
||||
boutput(M, __red("[target] probably wouldn't taste very good."))
|
||||
return 1
|
||||
|
||||
if (target.canmove)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
return 1
|
||||
|
||||
logTheThing("combat", M, target, "starts to maul %target% at [log_loc(M)].")
|
||||
actions.start(new/datum/action/bar/private/icon/werewolf_feast(target, src), M)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/werewolf_feast
|
||||
duration = 300
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "werewolf_feast"
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "devour_over"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/werewolf/werewolf_feast/feast
|
||||
var/last_complete = 0
|
||||
var/do_we_get_points = 0 // For the specialist objective. Did we feed on the target long enough?
|
||||
|
||||
New(Target, Feast)
|
||||
target = Target
|
||||
feast = Feast
|
||||
..()
|
||||
|
||||
onStart()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/A = feast.holder
|
||||
|
||||
if (!feast || get_dist(M, target) > feast.max_range || target == null || M == null || !ishuman(target) || !ishuman(M) || !A || !istype(A))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
// It's okay when the victim expired half-way through the feast, but plain corpses are too cheap.
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("Urgh, this cadaver tasted horrible. Better find some fresh meat."))
|
||||
target.visible_message("<span style=\"color:red\"><B>[M] completely rips [target]'s corpse to pieces!</B></span>")
|
||||
target.gib()
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
A.locked = 1
|
||||
playsound(M.loc, pick('sound/misc/werewolf_attack1.ogg', 'sound/misc/werewolf_attack2.ogg', 'sound/misc/werewolf_attack3.ogg'), 50, 1)
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] lunges at [target]!</b></span>")
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/A = feast.holder
|
||||
|
||||
if (!feast || get_dist(M, target) > feast.max_range || target == null || M == null || !ishuman(target) || !ishuman(M) || !A || !istype(A))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/done = world.time - started
|
||||
var/complete = max(min((done / duration), 1), 0)
|
||||
|
||||
if (complete >= 0.1 && last_complete < 0.1)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.2 && last_complete < 0.2)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.3 && last_complete < 0.3)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.4 && last_complete < 0.4)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.5 && last_complete < 0.5)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.6 && last_complete < 0.6)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (complete >= 0.7 && last_complete < 0.7)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (target.stat != 2 && !(ismonkey(target) || target.bioHolder && target.bioHolder.HasEffect("monkey"))) // Can't farm monkeys.
|
||||
src.do_we_get_points = 1
|
||||
|
||||
if (complete >= 0.8 && last_complete < 0.8)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (target.stat != 2 && !(ismonkey(target) || target.bioHolder && target.bioHolder.HasEffect("monkey")))
|
||||
src.do_we_get_points = 1
|
||||
|
||||
if (complete >= 0.9 && last_complete < 0.9)
|
||||
if (M.werewolf_attack(target, "feast") != 1)
|
||||
boutput(M, __red("[target] is moving around too much."))
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (target.stat != 2 && !(ismonkey(target) || target.bioHolder && target.bioHolder.HasEffect("monkey")))
|
||||
src.do_we_get_points = 1
|
||||
|
||||
last_complete = complete
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/datum/abilityHolder/A = feast.holder
|
||||
var/mob/living/M = owner
|
||||
var/mob/living/carbon/human/HH = target
|
||||
|
||||
// AH parent var for AH.locked vs. specific one for the feed objective.
|
||||
// Critter mobs only use one specific type of abilityHolder for instance.
|
||||
if (istype(A, /datum/abilityHolder/werewolf))
|
||||
var/datum/abilityHolder/werewolf/W = A
|
||||
if (W.feed_objective && istype(W.feed_objective, /datum/objective/specialist/werewolf/feed/))
|
||||
if (src.do_we_get_points == 1)
|
||||
if (istype(HH) && HH.bioHolder)
|
||||
if (!W.feed_objective.mobs_fed_on.Find(HH.bioHolder.Uid))
|
||||
W.feed_objective.mobs_fed_on.Add(HH.bioHolder.Uid)
|
||||
W.feed_objective.feed_count++
|
||||
boutput(M, __blue("You finish chewing on [HH], but what a feast it was!"))
|
||||
else
|
||||
boutput(M, __red("You've mauled [HH] before and didn't like the aftertaste. Better find a different prey."))
|
||||
else
|
||||
boutput(M, __red("What a meagre meal. You're still hungry..."))
|
||||
else
|
||||
boutput(M, __red("What a meagre meal. You're still hungry..."))
|
||||
else
|
||||
boutput(M, __red("You finish chewing on [HH]."))
|
||||
else
|
||||
boutput(M, __red("You finish chewing on [HH]."))
|
||||
|
||||
if (A && istype(A))
|
||||
A.locked = 0
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
|
||||
var/datum/abilityHolder/A = feast.holder
|
||||
var/mob/living/M = owner
|
||||
var/mob/living/carbon/human/HH = target
|
||||
|
||||
if (istype(A, /datum/abilityHolder/werewolf))
|
||||
var/datum/abilityHolder/werewolf/W = A
|
||||
if (W.feed_objective && istype(W.feed_objective, /datum/objective/specialist/werewolf/feed/))
|
||||
if (src.do_we_get_points == 1)
|
||||
if (istype(HH) && HH.bioHolder)
|
||||
if (!W.feed_objective.mobs_fed_on.Find(HH.bioHolder.Uid))
|
||||
W.feed_objective.mobs_fed_on.Add(HH.bioHolder.Uid)
|
||||
W.feed_objective.feed_count++
|
||||
boutput(M, __blue("Your feast was interrupted, but it satisfied your hunger for the time being."))
|
||||
else
|
||||
boutput(M, __red("You've mauled [HH] before and didn't like the aftertaste. Better find a different prey."))
|
||||
else
|
||||
boutput(M, __red("Your feast was interrupted and you're still hungry..."))
|
||||
else
|
||||
boutput(M, __red("Your feast was interrupted and you're still hungry..."))
|
||||
else
|
||||
boutput(M, __red("Your feast was interrupted."))
|
||||
else
|
||||
boutput(M, __red("Your feast was interrupted."))
|
||||
|
||||
if (A && istype(A))
|
||||
A.locked = 0
|
||||
@@ -0,0 +1,67 @@
|
||||
/datum/targetable/werewolf/werewolf_transform
|
||||
name = "Transform"
|
||||
desc = "Switch between human and wolf form, Takes a couple seconds to complete."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 600
|
||||
pointCost = 0
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 0
|
||||
werewolf_only = 0
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/private/icon/werewolf_transform(src), M)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/werewolf_transform
|
||||
duration = 50
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_ACTION
|
||||
id = "werewolf_transform"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "grabbed"
|
||||
var/datum/targetable/werewolf/werewolf_transform/transform
|
||||
|
||||
New(Transform)
|
||||
transform = Transform
|
||||
..()
|
||||
|
||||
onStart()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (M == null || !ishuman(M) || M.stat != 0 || M.paralysis > 0 || !transform)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
boutput(M, __red("<B>You feel a strong burning sensation all over your body!</B>"))
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (M == null || !ishuman(M) || M.stat != 0 || M.paralysis > 0 || !transform)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
M.werewolf_transform(0, 1)
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
boutput(M, __red("Your transformation was interrupted!"))
|
||||
@@ -0,0 +1,355 @@
|
||||
// Converted everything related to predators from client procs to ability holders and used
|
||||
// the opportunity to do some clean-up as well (Convair880).
|
||||
|
||||
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
||||
|
||||
/mob/proc/make_predator()
|
||||
if (ishuman(src))
|
||||
var/datum/abilityHolder/predator/A = src.get_ability_holder(/datum/abilityHolder/predator)
|
||||
if (A && istype(A))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/predator/P = src.add_ability_holder(/datum/abilityHolder/predator)
|
||||
P.addAbility(/datum/targetable/predator/predator_gearspawn)
|
||||
P.addAbility(/datum/targetable/predator/predator_taketrophy)
|
||||
P.addAbility(/datum/targetable/predator/predator_trophycount)
|
||||
|
||||
if (src.mind && src.mind.special_role != "omnitraitor")
|
||||
src << browse(grabResource("html/traitorTips/predatorTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
else return
|
||||
|
||||
////////////////////////////////////////////// Helper procs //////////////////////////////
|
||||
|
||||
/mob/living/carbon/human/proc/predator_transform()
|
||||
if (ishuman(src))
|
||||
var/mob/living/carbon/human/M = src
|
||||
|
||||
M.real_name = "predator"
|
||||
|
||||
M.jitteriness = 0
|
||||
M.stunned = 0
|
||||
M.weakened = 0
|
||||
M.paralysis = 0
|
||||
M.slowed = 0
|
||||
M.change_misstep_chance(-INFINITY)
|
||||
M.stuttering = 0
|
||||
M.drowsyness = 0
|
||||
|
||||
if (M.handcuffed)
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] rips apart the handcuffs with pure brute strength!</b></span>")
|
||||
qdel(M.handcuffed)
|
||||
M.handcuffed = null
|
||||
M.buckled = null
|
||||
|
||||
if (M.mutantrace)
|
||||
qdel(M.mutantrace)
|
||||
M.set_mutantrace(/datum/mutantrace/predator)
|
||||
|
||||
M.unequip_all()
|
||||
|
||||
var/obj/item/implant/microbomb/predator/B = new /obj/item/implant/microbomb/predator(M)
|
||||
B.implanted = 1
|
||||
B.implanted(M)
|
||||
|
||||
M.equip_if_possible(new /obj/item/clothing/under/gimmick/predator(M), slot_w_uniform) // Must be at the top of the list.
|
||||
M.equip_if_possible(new /obj/item/clothing/mask/predator(M), slot_wear_mask)
|
||||
M.equip_if_possible(new /obj/item/storage/belt/predator(M), slot_belt)
|
||||
M.equip_if_possible(new /obj/item/clothing/shoes/cowboy/predator(M), slot_shoes)
|
||||
M.equip_if_possible(new /obj/item/device/radio/headset(M), slot_ears)
|
||||
M.equip_if_possible(new /obj/item/storage/backpack(M), slot_back)
|
||||
M.equip_if_possible(new /obj/item/cloaking_device(M), slot_r_store)
|
||||
M.equip_if_possible(new /obj/item/knife_butcher/predspear(M), slot_l_hand)
|
||||
M.equip_if_possible(new /obj/item/gun/energy/laser_gun/pred(M), slot_r_hand)
|
||||
|
||||
M.set_face_icon_dirty()
|
||||
M.set_body_icon_dirty()
|
||||
M.update_clothing()
|
||||
|
||||
boutput(M, __blue("<h3>You have received your equipment. Let the hunt begin!</h3>"))
|
||||
logTheThing("combat", M, null, "transformed into a predator at [log_loc(M)].")
|
||||
return 1
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
// Called for every human mob spawn and mutantrace change. The value of non-standard skulls is defined in organ.dm.
|
||||
#define default_skull_desc "A trophy from a less interesting kill."
|
||||
#define default_skull_value 1
|
||||
/mob/proc/assign_gimmick_skull()
|
||||
if (!src || !ismob(src))
|
||||
return
|
||||
|
||||
if (ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
|
||||
if (!H.organHolder)
|
||||
sleep (20)
|
||||
if (!H.organHolder)
|
||||
return
|
||||
|
||||
for (var/obj/item/W in H)
|
||||
if (istype(W, /obj/item/skull/) && W == H.organHolder.skull)
|
||||
var/obj/item/skull/S = H.organHolder.skull
|
||||
var/skull_type = null
|
||||
var/skull_value = default_skull_value
|
||||
var/skull_desc = default_skull_desc // The examine desc for predators.
|
||||
|
||||
// Cluwnes first.
|
||||
if (iscluwne(H))
|
||||
skull_type = /obj/item/skull/noface
|
||||
skull_desc = "A meaningless trophy from a weak opponent. You feel disgusted to even look at it."
|
||||
|
||||
else
|
||||
// Antagonist check.
|
||||
if (checktraitor(H))
|
||||
switch (H.mind.special_role) // Ordered by skull value.
|
||||
if ("omnitraitor")
|
||||
skull_type = /obj/item/skull/crystal
|
||||
skull_desc = "A trophy taken from a mystic, all-powerful creature. It is an immeasurable honor."
|
||||
if ("predator")
|
||||
skull_type = /obj/item/skull/strange
|
||||
skull_desc = "A trophy taken from a predator, the finest hunters of all."
|
||||
if ("changeling")
|
||||
skull_type = /obj/item/skull/odd
|
||||
skull_desc = "A trophy taken from a shapeshifting alien! It is an immense honor."
|
||||
if ("werewolf")
|
||||
skull_value = 4
|
||||
skull_desc = "A grand trophy from a lycanthrope, a very capable hunter. It is an immense honor."
|
||||
if ("wizard")
|
||||
skull_type = /obj/item/skull/peculiar
|
||||
skull_desc = "A grand trophy from a powerful magician. It brings you great honor."
|
||||
if ("vampire")
|
||||
skull_value = 3
|
||||
skull_desc = "A trophy taken from an undead vampire! It brings you great honor."
|
||||
else
|
||||
skull_value = 2
|
||||
skull_desc = "A worthy trophy from a capable opponent."
|
||||
|
||||
else
|
||||
// Mutantrace and ability holder check for non-antagonists.
|
||||
if (ischangeling(H) || isvampire(H))
|
||||
if (ischangeling(H))
|
||||
skull_type = /obj/item/skull/odd
|
||||
skull_desc = "A trophy taken from a shapeshifting alien! It is an immense honor."
|
||||
else if (isvampire(H))
|
||||
skull_value = 3
|
||||
skull_desc = "A trophy taken from an undead vampire! It brings you great honor."
|
||||
|
||||
else
|
||||
if (!isnull(H.mutantrace))
|
||||
if (ispredator(H))
|
||||
skull_type = /obj/item/skull/strange
|
||||
skull_desc = "A trophy taken from a predator, the finest hunters of all."
|
||||
if (iswerewolf(H))
|
||||
skull_value = 4
|
||||
skull_desc = "A grand trophy from a lycanthrope, a very capable hunter. It is an immense honor."
|
||||
if (ismonkey(H) || H.bioHolder && H.bioHolder.HasEffect("monkey"))
|
||||
skull_value = 0
|
||||
skull_desc = "A meaningless trophy from a lab monkey. You feel disgusted to even look at it."
|
||||
|
||||
// Everything's still default, so check for assigned_role. Could be a lizard captain or whatever.
|
||||
if (isnull(skull_type) && skull_value == default_skull_value && skull_desc == default_skull_desc)
|
||||
if (H.mind)
|
||||
if (H.mind.special_role == "macho man") // Not in ticker.Agimmicks.
|
||||
skull_type = /obj/item/skull/gold
|
||||
skull_desc = "A trophy taken from a legendary wrestler. It is an immeasurable honor."
|
||||
else
|
||||
switch (H.mind.assigned_role)
|
||||
if ("Head of Security")
|
||||
skull_value = 3
|
||||
skull_desc = "A grand trophy from a very worthy foe. It brings you great honor."
|
||||
if ("Captain")
|
||||
skull_value = 3
|
||||
skull_desc = "A grand trophy from a very worthy foe. It brings you great honor."
|
||||
if ("Security Officer")
|
||||
skull_value = 2
|
||||
skull_desc = "A worthy trophy from a capable opponent."
|
||||
if ("Detective")
|
||||
skull_value = 2
|
||||
skull_desc = "A worthy trophy from a capable opponent."
|
||||
if ("Vice Officer")
|
||||
skull_value = 2
|
||||
skull_desc = "A worthy trophy from a capable opponent."
|
||||
if ("Head of Personnel")
|
||||
skull_value = 2
|
||||
skull_desc = "A worthy trophy from a capable opponent."
|
||||
if ("Clown")
|
||||
skull_value = -1
|
||||
skull_desc = "A meaningless trophy from a weak opponent. You feel disgusted to even look at it."
|
||||
|
||||
// Assign new skull or change value/desc.
|
||||
if (!isnull(skull_type))
|
||||
var/obj/item/skull/new_skull = new skull_type
|
||||
skull_value = new_skull.value // Defined in organ.dm. Copied because there isn't always a need to replace the skull.
|
||||
|
||||
if (S.type != new_skull.type)
|
||||
new_skull.donor = H
|
||||
new_skull.preddesc = skull_desc
|
||||
new_skull.set_loc(H)
|
||||
H.organHolder.skull = new_skull
|
||||
qdel(S)
|
||||
//DEBUG("[H]'s skull: [new_skull.type] (V: [new_skull.value], D: [new_skull.preddesc])")
|
||||
else
|
||||
qdel(new_skull)
|
||||
S.value = skull_value
|
||||
S.preddesc = skull_desc
|
||||
//DEBUG("[H]'s skull: [S.type] (V: [S.value], D: [S.preddesc])")
|
||||
else
|
||||
S.value = skull_value
|
||||
S.preddesc = skull_desc
|
||||
//DEBUG("[H]'s skull: [S.type] (V: [S.value], D: [S.preddesc])")
|
||||
|
||||
return
|
||||
#undef default_skull_value
|
||||
#undef default_skull_desc
|
||||
|
||||
// Returns the combined value of all trophies in the player's possession.
|
||||
/mob/proc/get_skull_value()
|
||||
if (!src || !ismob(src))
|
||||
return 0
|
||||
|
||||
var/value = 0
|
||||
|
||||
var/list/L = src.get_all_items_on_mob()
|
||||
if (L && L.len)
|
||||
for (var/obj/item/skull/S in L)
|
||||
if (ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
if (H.organHolder.skull == S)
|
||||
continue // Your own skull doesn't count, dummy!
|
||||
value += S.value
|
||||
return value
|
||||
|
||||
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/predator
|
||||
clicked(params)
|
||||
var/datum/targetable/predator/spell = owner
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (!isturf(owner.holder.owner.loc))
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this ability here.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
return
|
||||
|
||||
/datum/abilityHolder/predator
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Predator"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">You aren't strong enough to use this ability.</span>"
|
||||
|
||||
/////////////////////////////////////////////// Predator spell parent ////////////////////////////
|
||||
|
||||
/datum/targetable/predator
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // No custom sprites yet.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
pointCost = 0
|
||||
preferred_holder_type = /datum/abilityHolder/predator
|
||||
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
||||
var/not_when_handcuffed = 0
|
||||
var/predator_only = 0
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/predator/B = new /obj/screen/ability/predator(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
return
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/predator()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
return
|
||||
|
||||
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
switch (stunned_only_is_okay)
|
||||
if (0)
|
||||
if (M.stat != 0 || M.stunned > 0 || M.paralysis > 0 || M.weakened > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
if (1)
|
||||
if (M.stat != 0 || M.paralysis > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
else
|
||||
return 1
|
||||
|
||||
castcheck()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/carbon/human/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 0
|
||||
|
||||
if (!ishuman(M)) // Only humans use mutantrace datums.
|
||||
boutput(M, __red("You cannot use any powers in your current form."))
|
||||
return 0
|
||||
|
||||
if (M.transforming)
|
||||
boutput(M, __red("You can't use any powers right now."))
|
||||
return 0
|
||||
|
||||
if (predator_only == 1 && !ispredator(M))
|
||||
boutput(M, __red("You're not quite sure how to go about doing that in your current form."))
|
||||
return 0
|
||||
|
||||
if (incapacitation_check(src.when_stunned) != 1)
|
||||
boutput(M, __red("You can't use this ability while incapacitated!"))
|
||||
return 0
|
||||
|
||||
if (src.not_when_handcuffed == 1 && M.restrained())
|
||||
boutput(M, __red("You can't use this ability when restrained!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
return
|
||||
@@ -0,0 +1,519 @@
|
||||
/datum/abilityHolder/revenant
|
||||
topBarRendered = 1
|
||||
usesPoints = 1
|
||||
var/channeling = 0
|
||||
|
||||
var/datum/abilityHolder/wraith/relay = null
|
||||
var/datum/bioEffect/hidden/revenant/revenant = null
|
||||
pointName = "Wraith Points"
|
||||
|
||||
Stat()
|
||||
if (relay)
|
||||
relay.Stat()
|
||||
if (owner)
|
||||
stat("Human vessel integrity:", "[(owner.max_health + 50) / 1.5]%")
|
||||
|
||||
generatePoints()
|
||||
if (relay)
|
||||
relay.generatePoints()
|
||||
|
||||
deductPoints(cost)
|
||||
if (relay)
|
||||
return relay.deductPoints(cost)
|
||||
return 1
|
||||
|
||||
pointCheck(cost)
|
||||
if (!relay)
|
||||
return 1
|
||||
if (!relay.usesPoints)
|
||||
return 1
|
||||
if (relay.points < 0) // Just-in-case fallback.
|
||||
logTheThing("debug", usr, null, "'s ability holder ([relay.type]) was set to an invalid value (points less than 0), resetting.")
|
||||
relay.points = 0
|
||||
if (cost > relay.points)
|
||||
boutput(owner, relay.notEnoughPointsMessage)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/bioEffect/hidden/revenant
|
||||
name = "Revenant"
|
||||
desc = "The subject appears to be possessed by a wraith."
|
||||
id = "revenant"
|
||||
effectType = effectTypePower
|
||||
isBad = 0 // depends on who you ask really
|
||||
var/isDying = 0
|
||||
var/mob/wraith/wraith = null
|
||||
var/ghoulTouchActive = 0
|
||||
|
||||
var/list/abilities
|
||||
|
||||
proc/ghoulTouch(var/mob/living/carbon/human/poorSob, var/obj/item/affecting)
|
||||
if (poorSob.bioHolder.HasEffect("training_chaplain"))
|
||||
poorSob.visible_message("<span style=\"color:red\">[poorSob]'s faith shields them from [owner]'s ethereal force!", "<span style=\"color:blue\">Your faith protects you from [owner]'s ethereal force!</span>")
|
||||
return
|
||||
else
|
||||
poorSob.visible_message("<span style=\"color:red\">[poorSob] is hit by [owner]'s ethereal force!</span>", "<span style=\"color:red\">You are hit by [owner]'s ethereal force!</span>")
|
||||
if (istype(affecting))
|
||||
affecting.take_damage(4, 4, 0, DAMAGE_BLUNT)
|
||||
else
|
||||
poorSob.TakeDamage("All", 4, 4, 0, DAMAGE_BLUNT)
|
||||
poorSob.weakened += 2
|
||||
step_away(poorSob, owner, 15)
|
||||
sleep(3)
|
||||
step_away(poorSob, owner, 15)
|
||||
|
||||
|
||||
proc/wraithPossess(var/mob/wraith/W)
|
||||
if (!W.mind && !W.client)
|
||||
return
|
||||
if (owner.client || owner.mind)
|
||||
var/mob/dead/observer/O = owner.ghostize()
|
||||
if (O)
|
||||
O.corpse = null
|
||||
owner.ghost = null
|
||||
if (owner.ghost)
|
||||
owner.ghost.corpse = null
|
||||
owner.ghost = null
|
||||
src.wraith = W
|
||||
W.invisibility = 50
|
||||
W.set_loc(src.owner)
|
||||
W.abilityHolder.suspendAllAbilities()
|
||||
|
||||
message_admins("[key_name(wraith)] possessed the corpse of [owner] as a revenant at [showCoords(owner.x, owner.y, owner.z)].")
|
||||
logTheThing("combat", usr, null, "possessed the corpse of [owner] as a revenant at [showCoords(owner.x, owner.y, owner.z)].")
|
||||
|
||||
|
||||
if (src.wraith.mind) // theoretically shouldn't happen
|
||||
src.wraith.mind.transfer_to(owner)
|
||||
else
|
||||
src.wraith.client.mob = owner
|
||||
|
||||
owner.visible_message("<span style=\"color:red\"><strong>[pick("[owner] suddenly rises from the floor!", "[owner] suddenly looks a lot less dead!", "A dark light shines from [owner]'s eyes!")]</strong></span>",\
|
||||
"<span style=\"color:blue\">[pick("You force your will into [owner]'s corpse.", "Your dark will forces [owner] to rise.", "You assume direct control of [owner].")]</span>")
|
||||
|
||||
src.addRevenantVerbs()
|
||||
|
||||
OnAdd()
|
||||
if (ishuman(owner) && owner.stat == 2)
|
||||
switch (owner:decomp_stage)
|
||||
if (0)
|
||||
owner.max_health = 100
|
||||
if (1)
|
||||
owner.max_health = 75
|
||||
if (2)
|
||||
owner.max_health = 50
|
||||
if (3)
|
||||
owner.max_health = 25
|
||||
if (4)
|
||||
// todo: send message, tell the player to fuck off, or something
|
||||
owner.bioHolder.RemoveEffect("revenant")
|
||||
qdel(src)
|
||||
return
|
||||
else
|
||||
// do not possess non-humans; do not possess living people; do not pass go; do not collect $200
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
owner.full_heal()
|
||||
owner.reagents.clear_reagents()
|
||||
owner.blinded = 0
|
||||
owner.lying = 0
|
||||
if (owner.bioHolder.HasEffect("husk"))
|
||||
owner.bioHolder.RemoveEffect("husk")
|
||||
owner.set_mutantrace(null)
|
||||
owner.set_face_icon_dirty()
|
||||
owner.set_body_icon_dirty()
|
||||
animate_levitate(owner)
|
||||
|
||||
proc/RevenantDeath()
|
||||
if (isDying)
|
||||
return
|
||||
isDying = 1
|
||||
if (!src.wraith)
|
||||
src.owner.bioHolder.RemoveEffect("revenant")
|
||||
return
|
||||
if (!src.owner.mind && !src.owner.client)
|
||||
return
|
||||
|
||||
message_admins("Revenant [key_name(owner)] died at [showCoords(owner.x, owner.y, owner.z)].")
|
||||
logTheThing("combat", usr, null, "died as a revenant at [showCoords(owner.x, owner.y, owner.z)].")
|
||||
if (owner.mind)
|
||||
owner.mind.transfer_to(src.wraith)
|
||||
else if (owner.client)
|
||||
owner.client.mob = src.wraith
|
||||
src.wraith.invisibility = 10
|
||||
src.wraith.set_loc(get_turf(owner))
|
||||
src.wraith.abilityHolder.resumeAllAbilities()
|
||||
src.wraith.abilityHolder.regenRate /= 3
|
||||
owner.bioHolder.RemoveEffect("revenant")
|
||||
owner:decomp_stage = 4
|
||||
if (ishuman(owner) && owner:organHolder && owner:organHolder:brain)
|
||||
qdel(owner:organHolder:brain)
|
||||
particleMaster.SpawnSystem(new /datum/particleSystem/localSmoke("#000000", 5, locate(owner.x, owner.y, owner.z)))
|
||||
animate(owner)
|
||||
src.wraith = null
|
||||
return
|
||||
|
||||
OnLife()
|
||||
if (!src.wraith)
|
||||
return
|
||||
if (ghoulTouchActive)
|
||||
ghoulTouchActive--
|
||||
if (!ghoulTouchActive)
|
||||
owner.show_message("<span style=\"color:red\">You are no longer empowered by the netherworld.</span>")
|
||||
|
||||
src.wraith.Life()
|
||||
|
||||
owner.max_health -= 1.5
|
||||
|
||||
owner:ailments.len = 0
|
||||
owner.take_toxin_damage(-INFINITY)
|
||||
owner.take_oxygen_deprivation(-INFINITY)
|
||||
owner.take_eye_damage(-INFINITY)
|
||||
owner.take_eye_damage(-INFINITY, 1)
|
||||
owner.losebreath = 0
|
||||
owner.paralysis = 0
|
||||
owner.stunned = 0
|
||||
owner.weakened = 0
|
||||
owner.slowed = 0
|
||||
owner.radiation = 0
|
||||
owner.take_ear_damage(-INFINITY)
|
||||
owner.take_ear_damage(-INFINITY, 1)
|
||||
owner.take_brain_damage(-120)
|
||||
owner.bodytemperature = owner.base_body_temp
|
||||
owner.stat = 0
|
||||
|
||||
owner.updatehealth()
|
||||
|
||||
if (owner.health < -50)
|
||||
boutput(owner, "<span style=\"color:red\"><strong>This vessel has grown too weak to maintain your presence.</strong></span>")
|
||||
owner.death(0) // todo: add custom death
|
||||
return
|
||||
|
||||
var/e_decomp_stage = 0
|
||||
if (owner.max_health < 75)
|
||||
e_decomp_stage++
|
||||
if (owner.max_health < 50)
|
||||
e_decomp_stage++
|
||||
if (owner.max_health < 25)
|
||||
e_decomp_stage++
|
||||
if (owner.max_health < 0)
|
||||
e_decomp_stage++
|
||||
if (ishuman(owner)) // technically we won't let it be anything else but who knows what might happen
|
||||
if (owner:decomp_stage != e_decomp_stage)
|
||||
owner:decomp_stage = e_decomp_stage
|
||||
owner.set_face_icon_dirty()
|
||||
owner.set_body_icon_dirty()
|
||||
|
||||
proc/addRevenantVerbs()
|
||||
var/datum/abilityHolder/revenant/RH = owner.add_ability_holder(/datum/abilityHolder/revenant)
|
||||
RH.relay = src.wraith.abilityHolder
|
||||
RH.revenant = src
|
||||
src.wraith.abilityHolder.regenRate *= 3
|
||||
RH.addAbility(/datum/targetable/revenantAbility/massCommand)
|
||||
RH.addAbility(/datum/targetable/revenantAbility/shockwave)
|
||||
RH.addAbility(/datum/targetable/revenantAbility/touchOfEvil)
|
||||
RH.addAbility(/datum/targetable/revenantAbility/push)
|
||||
RH.addAbility(/datum/targetable/revenantAbility/crush)
|
||||
RH.addAbility(/datum/targetable/revenantAbility/help)
|
||||
|
||||
/*proc/removeRevenantVerbs()
|
||||
if (owner.mind)
|
||||
owner.mind.spells.len = 0
|
||||
return*/
|
||||
|
||||
/datum/targetable/revenantAbility
|
||||
icon = 'icons/mob/wraith_ui.dmi'
|
||||
preferred_holder_type = /datum/abilityHolder/revenant
|
||||
New()
|
||||
var/obj/screen/ability/topBar/B = new /obj/screen/ability/topBar(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
|
||||
cast(atom/target)
|
||||
return
|
||||
|
||||
castcheck()
|
||||
if (holder && holder.owner)
|
||||
return 1
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">You're not a revenant, what the heck are you doing?</span>")
|
||||
return 0
|
||||
|
||||
doCooldown()
|
||||
if (!holder)
|
||||
return
|
||||
last_cast = world.time + cooldown
|
||||
holder.updateButtons()
|
||||
spawn(cooldown + 5)
|
||||
holder.updateButtons()
|
||||
|
||||
|
||||
/datum/targetable/revenantAbility/massCommand
|
||||
name = "Mass Command"
|
||||
desc = "Launch an assortment of nearby objects at a target location."
|
||||
icon_state = "masscomm"
|
||||
special_screen_loc = "NORTH-1,WEST"
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 500
|
||||
cooldown = 300
|
||||
|
||||
cast(atom/target)
|
||||
if (istype(holder, /datum/abilityHolder/revenant))
|
||||
var/datum/abilityHolder/revenant/RH = holder
|
||||
RH.channeling = 0
|
||||
holder.owner.visible_message("<span style=\"color:red\"><strong>[holder.owner]</strong> gestures upwards, then at [target] with a swift striking motion!</span>")
|
||||
var/list/thrown = list()
|
||||
var/current_prob = 100
|
||||
if (ishuman(target))
|
||||
var/mob/living/carbon/T = target
|
||||
if (T.bioHolder.HasEffect("training_chaplain"))
|
||||
target.visible_message("<span style=\"color: red\"> [target] gives a rude gesture right back to [holder.owner]!</span>")
|
||||
return 1
|
||||
else
|
||||
target:stunned = max(max(target:weakened, target:stunned), 3)
|
||||
target:lying = 0
|
||||
target:weakened = 0
|
||||
target:show_message("<span style=\"color:red\">A ghostly force compels you to be still on your feet.</span>")
|
||||
for (var/obj/O in view(7, holder.owner))
|
||||
if (!O.anchored && isturf(O.loc))
|
||||
if (prob(current_prob))
|
||||
current_prob *= 0.75
|
||||
thrown += O
|
||||
animate_float(O)
|
||||
spawn(10)
|
||||
for (var/obj/O in thrown)
|
||||
O.throw_at(target, 32, 2)
|
||||
|
||||
/datum/targetable/revenantAbility/shockwave
|
||||
name = "Shockwave"
|
||||
desc = "Emit a shockwave, breaking nearby lights and walls, and stunning nearby humans for a short time."
|
||||
icon_state = "shockwave"
|
||||
targeted = 0
|
||||
pointCost = 750
|
||||
cooldown = 350 // 35s
|
||||
var/propagation_percentage = 60
|
||||
var/iteration_depth = 6
|
||||
special_screen_loc = "NORTH-1,WEST+1"
|
||||
var/static/list/prev = list("1" = NORTHWEST, "5" = NORTH, "4" = NORTHEAST, "6" = EAST, "2" = SOUTHEAST, "10" = SOUTH, "8" = SOUTHWEST, "9" = WEST)
|
||||
var/static/list/next = list("1" = NORTHEAST, "5" = EAST, "4" = SOUTHEAST, "6" = SOUTH, "2" = SOUTHWEST, "10" = WEST, "8" = NORTHWEST, "9" = NORTH)
|
||||
|
||||
proc/shock(var/turf/T)
|
||||
spawn(0)
|
||||
for (var/mob/living/carbon/human/M in T)
|
||||
if (M != holder.owner && !M.bioHolder.HasEffect("training_chaplain"))
|
||||
M.weakened += 2
|
||||
animate_revenant_shockwave(T, 1, 3)
|
||||
spawn(3)
|
||||
for (var/mob/living/carbon/human/M in T)
|
||||
if (M != holder.owner && !M.bioHolder.HasEffect("training_chaplain"))
|
||||
M.weakened += 6
|
||||
M.show_message("<span style=\"color:red\">A shockwave sweeps you off your feet!</span>")
|
||||
for (var/obj/machinery/light/L in T)
|
||||
L.broken()
|
||||
for (var/obj/window/W in T)
|
||||
W.health = 0
|
||||
W.smash()
|
||||
if (istype(T, /turf/simulated/wall))
|
||||
T:dismantle_wall()
|
||||
else if (istype(T, /turf/simulated/floor) && prob(75))
|
||||
if (prob(50))
|
||||
T:to_plating()
|
||||
else
|
||||
T:break_tile()
|
||||
spawn(10)
|
||||
T.pixel_y = 0
|
||||
T.transform = null
|
||||
|
||||
cast()
|
||||
var/list/next = list()
|
||||
var/list/NN = list()
|
||||
var/turf/origin = get_turf(holder.owner)
|
||||
if (!origin)
|
||||
return 1
|
||||
if (istype(holder, /datum/abilityHolder/revenant))
|
||||
var/datum/abilityHolder/revenant/RH = holder
|
||||
RH.channeling = 0
|
||||
shock(origin)
|
||||
for (var/turf/T in orange(1, origin))
|
||||
next += T
|
||||
next[T] = get_dir(origin, T)
|
||||
spawn(0)
|
||||
for (var/i = 1, i <= iteration_depth, i++)
|
||||
for (var/turf/T in next)
|
||||
shock(T)
|
||||
if (!T.density)
|
||||
var/base_dir = next[T]
|
||||
var/left_dir = src.prev["[base_dir]"]
|
||||
var/right_dir = src.next["[base_dir]"] // ugly & fuck you byond for making me do this
|
||||
if (prob(propagation_percentage / 2))
|
||||
var/turf/A = get_step(T, left_dir)
|
||||
if (A && !(A in NN))
|
||||
NN += A
|
||||
NN[A] = left_dir
|
||||
if (prob(propagation_percentage))
|
||||
var/turf/B = get_step(T, base_dir)
|
||||
if (B && !(B in NN))
|
||||
NN += B
|
||||
NN[B] = base_dir
|
||||
if (prob(propagation_percentage / 2))
|
||||
var/turf/C = get_step(T, right_dir)
|
||||
if (C && !(C in NN))
|
||||
NN += C
|
||||
NN[C] = right_dir
|
||||
next = NN
|
||||
NN = list()
|
||||
sleep(3)
|
||||
return 0
|
||||
|
||||
/datum/targetable/revenantAbility/touchOfEvil
|
||||
name = "Touch of Evil"
|
||||
desc = "Empower your hand-to-hand attacks for a short time, causing additional damage and knockdown."
|
||||
icon_state = "eviltouch"
|
||||
targeted = 0
|
||||
pointCost = 1000
|
||||
cooldown = 300
|
||||
special_screen_loc = "NORTH-1,WEST+2"
|
||||
|
||||
cast()
|
||||
if (istype(holder, /datum/abilityHolder/revenant))
|
||||
var/datum/abilityHolder/revenant/RH = holder
|
||||
RH.channeling = 0
|
||||
var/datum/bioEffect/hidden/revenant/R = RH.revenant
|
||||
R.ghoulTouchActive = 4
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] glows with ethereal power!</span>", "<span style=\"color:blue\">You feel ghostly strength pulsing through you.</span>")
|
||||
return 0
|
||||
holder.owner.show_message("<span style='color:red'>You cannot cast that ability!</span>")
|
||||
|
||||
/datum/targetable/revenantAbility/push
|
||||
name = "Push"
|
||||
desc = "Pushes a target object or mob away from the revenant."
|
||||
icon_state = "push"
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 50
|
||||
cooldown = 150
|
||||
special_screen_loc = "NORTH-1,WEST+3"
|
||||
|
||||
cast(atom/target)
|
||||
if (isturf(target))
|
||||
holder.owner.show_message("<span style=\"color:red\">You must target an object or mob with this ability.</span>")
|
||||
return 1
|
||||
if (istype(holder, /datum/abilityHolder/revenant))
|
||||
var/datum/abilityHolder/revenant/RH = holder
|
||||
RH.channeling = 0
|
||||
var/mob/source = src.holder.owner
|
||||
var/throwat = get_edge_target_turf(target, get_dir(source, target))
|
||||
var/atom/movable/M = target
|
||||
|
||||
if (ismob(target))
|
||||
var/mob/T = target
|
||||
if (T.bioHolder && T.bioHolder.HasEffect("training_chaplain"))
|
||||
holder.owner.show_message("<span style=\"color: red\">Some mysterious force protects [target] from your influence.</span>")
|
||||
return 1
|
||||
else
|
||||
holder.owner.show_message("<span style=\"color:blue\">You hurl [target] away from you!</span>")
|
||||
T.throw_at(throwat, 32, 2)
|
||||
T.show_message("<span style=\"color:red\">An unknown force hurls you away!</span>")
|
||||
else
|
||||
holder.owner.show_message("<span style=\"color:blue\">You hurl [target] away from you!</span>")
|
||||
M.throw_at(throwat, 32, 2)
|
||||
|
||||
return 0
|
||||
|
||||
/datum/targetable/revenantAbility/crush
|
||||
name = "Crush"
|
||||
desc = "Channel your telekinetic abilities at a human target, causing damage as long as you stand still. Casting any other spell will interrupt this!"
|
||||
icon_state = "crush"
|
||||
targeted = 1
|
||||
pointCost = 2500
|
||||
cooldown = 600
|
||||
special_screen_loc = "NORTH-1,WEST+4"
|
||||
|
||||
cast(atom/target)
|
||||
if (!ishuman(target))
|
||||
holder.owner.show_message("<span style=\"color:red\">You must target a human with this ability.</span>")
|
||||
return 1
|
||||
var/mob/living/carbon/human/H = target
|
||||
if (!isturf(holder.owner.loc))
|
||||
holder.owner.show_message("<span style=\"color:red\">You cannot cast this ability inside a [holder.owner.loc].</span>")
|
||||
return 1
|
||||
if (holder.owner.equipped())
|
||||
holder.owner.show_message("<span style=\"color:red\">You require a free hand to cast this ability.</span>")
|
||||
return 1
|
||||
if (H.bioHolder.HasEffect("training_chaplain"))
|
||||
holder.owner.show_message("<span style=\"color: red\">Some mysterious force shields [target] from your influence.</span>")
|
||||
return 1
|
||||
|
||||
var/location = holder.owner.loc
|
||||
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] reaches out towards [H], making a crushing motion.</span>", "<span style=\"color:blue\">You reach out towards [H].</span>")
|
||||
H.weakened += 2
|
||||
|
||||
var/datum/abilityHolder/revenant/RH
|
||||
if (istype(holder, /datum/abilityHolder/revenant))
|
||||
RH = holder
|
||||
RH.channeling = 1
|
||||
if (!RH || !istype(RH, /datum/abilityHolder/revenant/))
|
||||
return
|
||||
|
||||
spawn(5)
|
||||
var/iterations = 0
|
||||
while (holder.owner.loc == location && !holder.owner.equipped())
|
||||
iterations++
|
||||
if (!holder.owner)
|
||||
RH.channeling = 0
|
||||
break
|
||||
if (RH.channeling == 0)
|
||||
holder.owner.show_message("<span style=\"color:red\">You were interrupted!</span>")
|
||||
break
|
||||
if (!H)
|
||||
holder.owner.show_message("<span style=\"color:red\">You were interrupted!</span>")
|
||||
RH.channeling = 0
|
||||
break
|
||||
if (get_dist(holder.owner, H) > 7)
|
||||
holder.owner.show_message("<span style=\"color:red\">[H] is pulled from your telekinetic grip!</span>")
|
||||
RH.channeling = 0
|
||||
break
|
||||
H.weakened += (2 + rand(0, iterations))
|
||||
H.TakeDamage("chest", 4 + rand(0, iterations), 0, 0, DAMAGE_CRUSH)
|
||||
if (prob(40))
|
||||
H.visible_message("<span style=\"color:red\">[H]'s bones crack loudly!</span>", "<span style=\"color:red\">You feel like you're about to be [pick("crushed", "destroyed", "vaporized")].</span>")
|
||||
if (prob(50))
|
||||
H.emote("scream")
|
||||
if (iterations > 12 && prob((iterations - 12) * 5))
|
||||
H.visible_message("<span style=\"color:red\">[H]'s body gives in to the telekinetic grip!</span>", "<span style=\"color:red\">You are completely crushed.</span>")
|
||||
H.gib()
|
||||
return
|
||||
sleep(7)
|
||||
holder.owner.show_message("<span style=\"color:red\">You were interrupted!</span>")
|
||||
return 0
|
||||
|
||||
/datum/targetable/revenantAbility/help
|
||||
name = "Toggle Help Mode"
|
||||
desc = "Enter or exit help mode."
|
||||
icon_state = "help0"
|
||||
targeted = 0
|
||||
cooldown = 0
|
||||
helpable = 0
|
||||
special_screen_loc = "SOUTH,EAST"
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (holder.help_mode)
|
||||
holder.help_mode = 0
|
||||
else
|
||||
holder.help_mode = 1
|
||||
boutput(holder.owner, "<span style=\"color:blue\"><strong>Help Mode has been activated To disable it, click on this button again.</strong></span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">Hold down Shift, Ctrl or Alt while clicking the button to set it to that key.</span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">You will then be able to use it freely by holding that button and left-clicking a tile.</span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">Alternatively, you can click with your middle mouse button to use the ability on your current tile.</span>")
|
||||
src.object.icon_state = "help[holder.help_mode]"
|
||||
holder.updateButtons()
|
||||
return 0
|
||||
@@ -0,0 +1,383 @@
|
||||
// Converted everything related to vampires from client procs to ability holders and used
|
||||
// the opportunity to do some clean-up as well (Convair880).
|
||||
|
||||
/////////////////////////////////////////////////// Setup //////////////////////////////////////////
|
||||
|
||||
/mob/proc/make_vampire()
|
||||
if (ishuman(src) || iscritter(src))
|
||||
if (ishuman(src))
|
||||
var/datum/abilityHolder/vampire/A = src.get_ability_holder(/datum/abilityHolder/vampire)
|
||||
if (A && istype(A))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/vampire/V = src.add_ability_holder(/datum/abilityHolder/vampire)
|
||||
V.addAbility(/datum/targetable/vampire/vampire_bite)
|
||||
V.addAbility(/datum/targetable/vampire/blood_tracking)
|
||||
V.addAbility(/datum/targetable/vampire/cancel_stuns)
|
||||
V.addAbility(/datum/targetable/vampire/glare)
|
||||
V.addAbility(/datum/targetable/vampire/hypnotize)
|
||||
|
||||
if (src.mind)
|
||||
src.mind.is_vampire = V
|
||||
|
||||
spawn (25) // Don't remove.
|
||||
if (src) src.assign_gimmick_skull()
|
||||
|
||||
else if (iscritter(src)) // For testing. Just give them all abilities that are compatible.
|
||||
var/mob/living/critter/C = src
|
||||
|
||||
if (isnull(C.abilityHolder)) // They do have a critter AH by default...or should.
|
||||
var/datum/abilityHolder/vampire/A2 = C.add_ability_holder(/datum/abilityHolder/vampire)
|
||||
if (!A2 || !istype(A2, /datum/abilityHolder/))
|
||||
return
|
||||
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/cancel_stuns/mk2)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/glare)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/hypnotize)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/plague_touch)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/phaseshift_vampire)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/call_bats)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/vampire_scream)
|
||||
C.abilityHolder.addAbility(/datum/targetable/vampire/enthrall)
|
||||
|
||||
if (C.mind)
|
||||
C.mind.is_vampire = C.abilityHolder
|
||||
|
||||
if (src.mind && src.mind.special_role != "omnitraitor")
|
||||
src << browse(grabResource("html/traitorTips/vampireTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
else return
|
||||
|
||||
////////////////////////////////////////////////// Helper procs ////////////////////////////////////////////////
|
||||
|
||||
// Just a little helper or two since vampire parameters aren't tracked by mob vars anymore.
|
||||
/mob/proc/get_vampire_blood(var/total_blood = 0)
|
||||
if (!isvampire(src))
|
||||
return 0
|
||||
|
||||
var/datum/abilityHolder/vampire/AH = src.get_ability_holder(/datum/abilityHolder/vampire)
|
||||
if (AH && istype(AH))
|
||||
if (total_blood)
|
||||
return AH.vamp_blood
|
||||
else
|
||||
return AH.points
|
||||
else
|
||||
return 0
|
||||
|
||||
/mob/proc/change_vampire_blood(var/change = 0, var/total_blood = 0, var/set_null = 0)
|
||||
if (!isvampire(src))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/vampire/AH = src.get_ability_holder(/datum/abilityHolder/vampire)
|
||||
if (AH && istype(AH))
|
||||
if (total_blood)
|
||||
if (AH.vamp_blood < 0)
|
||||
AH.vamp_blood = 0
|
||||
if (haine_blood_debug) logTheThing("debug", src, null, "<b>HAINE BLOOD DEBUG:</b> [src]'s vamp_blood dropped below 0 and was reset to 0")
|
||||
|
||||
if (set_null == 1)
|
||||
AH.vamp_blood = 0
|
||||
else
|
||||
AH.vamp_blood = max(AH.vamp_blood + change, 0)
|
||||
|
||||
else
|
||||
if (AH.points < 0)
|
||||
AH.points = 0
|
||||
if (haine_blood_debug) logTheThing("debug", src, null, "<b>HAINE BLOOD DEBUG:</b> [src]'s vamp_blood_remaining dropped below 0 and was reset to 0")
|
||||
|
||||
if (set_null == 1)
|
||||
AH.points = 0
|
||||
else
|
||||
AH.points = max(AH.points + change, 0)
|
||||
|
||||
return
|
||||
|
||||
/mob/proc/check_vampire_power(var/which_power = 3) // 1: thermal | 2: xray | 3: full power
|
||||
if (!isvampire(src))
|
||||
return 0
|
||||
|
||||
if (!which_power)
|
||||
return 0
|
||||
|
||||
var/datum/abilityHolder/vampire/AH = src.get_ability_holder(/datum/abilityHolder/vampire)
|
||||
if (AH && istype(AH))
|
||||
switch (which_power)
|
||||
if (1)
|
||||
if (AH.has_thermal == 1)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
if (2)
|
||||
if (AH.has_xray == 1)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
if (3)
|
||||
if (AH.has_fullpower == 1)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
else
|
||||
return 0
|
||||
|
||||
////////////////////////////////////////////////// Ability holder /////////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/vampire
|
||||
clicked(params)
|
||||
var/datum/targetable/vampire/spell = owner
|
||||
var/datum/abilityHolder/holder = owner.holder
|
||||
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
|
||||
if(params["shift"] && params["ctrl"])
|
||||
if(owner.waiting_for_hotkey)
|
||||
holder.cancel_action_binding()
|
||||
return
|
||||
else
|
||||
owner.waiting_for_hotkey = 1
|
||||
src.updateIcon()
|
||||
boutput(usr, "<span style=\"color:blue\">Please press a number to bind this ability to...</span>")
|
||||
return
|
||||
|
||||
if (!isturf(owner.holder.owner.loc))
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this spell here.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
return
|
||||
|
||||
/datum/abilityHolder/vampire
|
||||
usesPoints = 1
|
||||
regenRate = 0
|
||||
tabName = "Vampire"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">You need more blood to use this ability.</span>"
|
||||
var/vamp_blood = 0
|
||||
points = 0 // Replaces the old vamp_blood_remaining var.
|
||||
var/vamp_blood_tracking = 1
|
||||
var/mob/vamp_isbiting = null
|
||||
|
||||
// Note: please use mob.get_vampire_blood() & mob.change_vampire_blood() instead of changing the numbers directly.
|
||||
|
||||
// At the time of writing, sight (thermal, x-ray) and chapel checks can be found in human.dm.
|
||||
var/has_thermal = 0
|
||||
var/has_xray = 0
|
||||
var/has_fullpower = 0
|
||||
|
||||
// These are thresholds in relation to vamp_blood. Last_power exists only for unlock checks as stuff
|
||||
// might deduct something from vamp_blood, though it shouldn't happen on a regular basis.
|
||||
var/last_power = 0
|
||||
var/level1 = 200
|
||||
var/level2 = 300
|
||||
var/level3 = 400
|
||||
var/level4 = 600
|
||||
var/level5 = 800
|
||||
var/level6 = 1000 // Full power.
|
||||
|
||||
onAbilityStat() // In the 'Vampire' tab.
|
||||
..()
|
||||
stat("Blood:", src.vamp_blood)
|
||||
stat("Blood remaining:", src.points)
|
||||
return
|
||||
|
||||
proc/blood_tracking_output(var/deduct = 0)
|
||||
if (!src.owner || !ismob(src.owner))
|
||||
return
|
||||
|
||||
if (!istype(src, /datum/abilityHolder/vampire))
|
||||
return
|
||||
|
||||
if (!src.vamp_blood_tracking)
|
||||
return
|
||||
|
||||
if (deduct > 1)
|
||||
boutput(src.owner, __blue("You used [deduct] units of blood, and have [src.points - deduct] remaining."))
|
||||
|
||||
else
|
||||
boutput(src.owner, __blue("You have accumulated [src.vamp_blood] units of blood and [src.points] left to use."))
|
||||
|
||||
return
|
||||
|
||||
proc/check_for_unlocks()
|
||||
if (!src.owner || !ismob(src.owner))
|
||||
return
|
||||
|
||||
if (!istype(src, /datum/abilityHolder/vampire))
|
||||
return
|
||||
|
||||
if (!src.last_power && src.vamp_blood >= src.level1)
|
||||
src.last_power = 1
|
||||
|
||||
src.has_thermal = 1
|
||||
boutput(src.owner, __blue("<h3>Your vampiric vision has improved (thermal)!</h3>"))
|
||||
|
||||
src.addAbility(/datum/targetable/vampire/plague_touch)
|
||||
|
||||
if (src.last_power == 1 && src.vamp_blood >= src.level2)
|
||||
src.last_power = 2
|
||||
|
||||
src.addAbility(/datum/targetable/vampire/phaseshift_vampire)
|
||||
src.addAbility(/datum/targetable/vampire/radio_jammer)
|
||||
|
||||
if (src.last_power == 2 && src.vamp_blood >= src.level3)
|
||||
src.last_power = 3
|
||||
|
||||
src.addAbility(/datum/targetable/vampire/call_bats)
|
||||
src.addAbility(/datum/targetable/vampire/vampire_scream)
|
||||
|
||||
if (src.last_power == 3 && src.vamp_blood >= src.level4)
|
||||
src.last_power = 4
|
||||
|
||||
src.removeAbility(/datum/targetable/vampire/cancel_stuns)
|
||||
src.addAbility(/datum/targetable/vampire/cancel_stuns/mk2)
|
||||
src.addAbility(/datum/targetable/vampire/vamp_cloak)
|
||||
|
||||
if (src.last_power == 4 && src.vamp_blood >= src.level5)
|
||||
src.last_power = 5
|
||||
|
||||
src.addAbility(/datum/targetable/vampire/enthrall)
|
||||
|
||||
if (src.last_power == 5 && src.vamp_blood >= src.level6)
|
||||
src.last_power = 6
|
||||
|
||||
src.has_xray = 1
|
||||
src.has_fullpower = 1
|
||||
boutput(src.owner, __blue("<h3>Your vampiric vision has improved (x-ray)!</h3>"))
|
||||
boutput(src.owner, __blue("<h3>You have attained full power and are now too powerful to be harmed or stopped by the chapel's aura.</h3>"))
|
||||
|
||||
return
|
||||
|
||||
///////////////////////////////////////////// Vampire spell parent //////////////////////////////////////////////////
|
||||
|
||||
// If you change the blood cost, cooldown etc of an ability, don't forget to update vampireTips.html too!
|
||||
|
||||
// Notes:
|
||||
// - If an ability isn't available from the beginning, add an unlock_message to notify the player of unlocks.
|
||||
// - Vampire abilities are logged. Please keep it that way when you make additions.
|
||||
// - Add this snippet at the bottom of cast() if the ability isn't free. Optional but basic feedback for the player.
|
||||
// var/datum/abilityHolder/vampire/H = holder
|
||||
// if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
// - You should also call the proc if you make the player pay for an interrupted attempt to use the ability, for
|
||||
// instance when employing do_mob() checks.
|
||||
|
||||
/datum/targetable/vampire
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // Vampire ability sprites don't exist yet.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
pointCost = 0
|
||||
preferred_holder_type = /datum/abilityHolder/vampire
|
||||
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
||||
var/not_when_handcuffed = 0
|
||||
var/unlock_message = null
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/vampire/B = new /obj/screen/ability/vampire(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
return
|
||||
|
||||
onAttach(var/datum/abilityHolder/H)
|
||||
..() // Start_on_cooldown check.
|
||||
if (src.unlock_message && src.holder && src.holder.owner)
|
||||
boutput(src.holder.owner, __blue("<h3>[src.unlock_message]</h3>"))
|
||||
return
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/vampire()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
return
|
||||
|
||||
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
switch (stunned_only_is_okay)
|
||||
if (0)
|
||||
if (M.stat != 0 || M.stunned > 0 || M.paralysis > 0 || M.weakened > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
if (1)
|
||||
if (M.stat != 0 || M.paralysis > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
else
|
||||
return 1
|
||||
|
||||
castcheck()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 0
|
||||
|
||||
if (!(iscarbon(M) || iscritter(M)))
|
||||
boutput(M, __red("You cannot use any powers in your current form."))
|
||||
return 0
|
||||
|
||||
if (M.transforming)
|
||||
boutput(M, __red("You can't use any powers right now."))
|
||||
return 0
|
||||
|
||||
if (incapacitation_check(src.when_stunned) != 1)
|
||||
boutput(M, __red("You can't use this ability while incapacitated!"))
|
||||
return 0
|
||||
|
||||
if (src.not_when_handcuffed == 1 && M.restrained())
|
||||
boutput(M, __red("You can't use this ability when restrained!"))
|
||||
return 0
|
||||
|
||||
if (istype(get_area(M), /area/station/chapel) && M.check_vampire_power(3) != 1)
|
||||
boutput(M, __red("Your powers do not work in this holy place!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
return
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/targetable/vampire/blood_tracking
|
||||
name = "Toggle blood tracking"
|
||||
desc = "Toggles blood gain/loss messages."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 2
|
||||
not_when_handcuffed = 0
|
||||
dont_lock_holder = 1
|
||||
ignore_holder_lock = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (iscritter(M) && !istype(H))
|
||||
boutput(M, __red("Critter mobs currently don't have to worry about blood. Lucky you."))
|
||||
return 1
|
||||
|
||||
if (H.vamp_blood_tracking == 1)
|
||||
H.vamp_blood_tracking = 0
|
||||
else
|
||||
H.vamp_blood_tracking = 1
|
||||
|
||||
boutput(M, __blue("Blood tracking turned [H.vamp_blood_tracking == 1 ? "on" : "off"]."))
|
||||
return 0
|
||||
@@ -0,0 +1,43 @@
|
||||
/datum/targetable/vampire/call_bats
|
||||
name = "Call bats"
|
||||
desc = "Calls a swarm of bats to attack your foes."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 1200
|
||||
pointCost = 150
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
unlock_message = "You have gained call bats, which summons bats to fight for you."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (M.wear_mask && istype(M.wear_mask, /obj/item/clothing/mask/muzzle))
|
||||
boutput(M, __red("How do you expect this to work? You're muzzled!"))
|
||||
M.visible_message("<span style=\"color:red\"><b>[M]</b> makes a loud noise.</span>")
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
return 0 // Cooldown because spam is bad.
|
||||
|
||||
var/turf/T = get_turf(M)
|
||||
if (T && isturf(T))
|
||||
M.say("BATT PHAR")
|
||||
new /obj/critter/bat/buff(T)
|
||||
new /obj/critter/bat/buff(T)
|
||||
new /obj/critter/bat/buff(T)
|
||||
for (var/obj/critter/bat/buff/B in range(M, 1))
|
||||
B.friends += M
|
||||
else
|
||||
boutput(M, __red("The bats did not respond to your call!"))
|
||||
return 1 // No cooldown here, though.
|
||||
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
logTheThing("combat", M, null, "uses call bats at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,77 @@
|
||||
/datum/targetable/vampire/cancel_stuns
|
||||
name = "Cancel stuns"
|
||||
desc = "Recover from being stunned."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 600
|
||||
pointCost = 0
|
||||
when_stunned = 2
|
||||
not_when_handcuffed = 0
|
||||
|
||||
proc/remove_stuns(var/message_type = 1)
|
||||
if (!holder)
|
||||
return
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return
|
||||
|
||||
M.stunned = 0
|
||||
M.weakened = 0
|
||||
M.paralysis = 0
|
||||
M.slowed = 0
|
||||
M.change_misstep_chance(-INFINITY)
|
||||
M.stuttering = 0
|
||||
M.drowsyness = 0
|
||||
|
||||
if (M.get_stamina() != (STAMINA_MAX + M.get_stam_mod_max())) // Tasers etc.
|
||||
M.set_stamina(STAMINA_MAX + M.get_stam_mod_max())
|
||||
|
||||
if (message_type == 2)
|
||||
boutput(M, __blue("You feel your flesh knitting itself back together."))
|
||||
else
|
||||
boutput(M, __blue("You feel refreshed and ready to get back into the fight."))
|
||||
|
||||
logTheThing("combat", M, null, "uses cancel stuns at [log_loc(M)].")
|
||||
return
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
src.remove_stuns(1)
|
||||
return 0
|
||||
|
||||
/datum/targetable/vampire/cancel_stuns/mk2
|
||||
name = "Cancel stuns Mk2"
|
||||
desc = "Recover from being stunned. Restores a minor amount of health."
|
||||
cooldown = 600
|
||||
pointCost = 0
|
||||
when_stunned = 2
|
||||
unlock_message = "Your cancel stuns power now heals you in addition to its original effect."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (M.get_burn_damage() > 0 || M.get_toxin_damage() > 0 || M.get_brute_damage() > 0 || M.get_oxygen_deprivation() > 0 || M.losebreath > 0)
|
||||
M.HealDamage("All", 40, 40)
|
||||
M.take_toxin_damage(-40)
|
||||
M.take_oxygen_deprivation(-40)
|
||||
M.losebreath = min(usr.losebreath - 40)
|
||||
M.updatehealth()
|
||||
|
||||
src.remove_stuns(2)
|
||||
return 0
|
||||
@@ -0,0 +1,194 @@
|
||||
/datum/targetable/vampire/enthrall
|
||||
name = "Enthrall"
|
||||
desc = "Makes the target a loyal mindslave. Takes a long time to cast."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 1800
|
||||
pointCost = 400
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
restricted_area_check = 2
|
||||
unlock_message = "You have gained enthrall. It allows you to enslave humans and synthetics."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to enslave yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("[target] is dead!"))
|
||||
return 1
|
||||
|
||||
if (!target.mind || !target.client)
|
||||
boutput(M, __red("[target] is braindead!"))
|
||||
return 1
|
||||
|
||||
if (target.is_mentally_dominated_by(M))
|
||||
boutput(M, __red("[target] is already loyal to you."))
|
||||
return 1
|
||||
|
||||
// Don't remove this unless you are willing to adjust all existing mindslave-related procs for this very rare and specific case.
|
||||
if (checktraitor(target))
|
||||
boutput(M, __red("[target] is not susceptible to being enthralled!"))
|
||||
return 1
|
||||
|
||||
if (issilicon(target))
|
||||
var/mob/living/silicon/S = target
|
||||
if (!S.syndicate_possible)
|
||||
boutput(M, __red("[target] is not susceptible to being enthralled!"))
|
||||
return 1
|
||||
|
||||
if (istype(H) && H.vamp_isbiting)
|
||||
boutput(M, __red("You are already biting someone!"))
|
||||
return 1
|
||||
|
||||
actions.start(new/datum/action/bar/private/icon/vampire_enthrall(target, src), M)
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
return 0
|
||||
|
||||
/datum/action/bar/private/icon/vampire_enthrall
|
||||
duration = 250
|
||||
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
|
||||
id = "vampire_enthrall"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "grabbed"
|
||||
var/mob/living/target
|
||||
var/datum/targetable/vampire/enthrall/enslave
|
||||
var/last_complete = 0
|
||||
|
||||
New(Target, Enslave)
|
||||
target = Target
|
||||
enslave = Enslave
|
||||
..()
|
||||
|
||||
onStart()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/vampire/H = enslave.holder
|
||||
|
||||
if (!enslave || get_dist(M, target) > enslave.max_range || target == null || M == null || target.stat == 2 || !target.mind || !target.client)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] bites [target]!</B></span>")
|
||||
boutput(M, __blue("You begin to pump your polluted blood into [target]'s [issilicon(target) ? "serial port" : "neck"]."))
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("New device found. Attempting plug & play configuration."))
|
||||
else
|
||||
boutput(target, __red("You feel a little cold all the sudden."))
|
||||
if (istype(H)) H.vamp_isbiting = target
|
||||
target.vamp_beingbitten = 1
|
||||
|
||||
onUpdate()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
|
||||
if (!enslave || get_dist(M, target) > enslave.max_range || target == null || M == null || target.stat == 2 || !target.mind || !target.client)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
if (target.bioHolder && target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(M, __red("Wait, this is a chaplain!!! <B>AGDFHSKFGBLDFGLHSFDGHDFGH</B>"))
|
||||
boutput(target, __blue("Your divine protection saves you from enthrallment, and brands [M] as a thing of evil!"))
|
||||
M.emote("scream")
|
||||
M.weakened = max(M.weakened, 15)
|
||||
M.name_suffix("the Dracula")
|
||||
M.UpdateName()
|
||||
M.TakeDamage("chest", 0, 30)
|
||||
interrupt(INTERRUPT_ALWAYS)
|
||||
return
|
||||
|
||||
var/done = world.time - started
|
||||
var/complete = max(min((done / duration), 1), 0)
|
||||
|
||||
if (complete >= 0.2 && last_complete < 0.2)
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("Coolant systems register decreased load near serial interface."))
|
||||
else
|
||||
boutput(target, __red("You feel a chill spreading out from your neck."))
|
||||
boutput(M, __blue("You continue to pump blood into [target]."))
|
||||
|
||||
if (complete >= 0.4 && last_complete < 0.4)
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("System temperature continues to decrease."))
|
||||
else
|
||||
boutput(target, __red("The cold spreads through your upper torso."))
|
||||
boutput(M, __blue("You continue to pump blood into [target]."))
|
||||
|
||||
if (complete >= 0.6 && last_complete < 0.6)
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("Low temperature region approaching memory core. Temperature variation may affect memory access!"))
|
||||
else
|
||||
boutput(target, __red("The icy cold spreads to your lower torso and arms."))
|
||||
boutput(M, __blue("You continue to pump blood into [target]."))
|
||||
|
||||
if (complete >= 0.8 && last_complete < 0.8)
|
||||
if (!target.paralysis)
|
||||
target.paralysis = max(target.paralysis, 10)
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("Low temperature reggggggg92309392"))
|
||||
boutput(target, __red("<b>MEM ERR BLK 0 ADDR 30FC500 HAS 010F NOT 0000</b>"))
|
||||
else
|
||||
boutput(target, __red("The freezing cold envelops your entire body."))
|
||||
boutput(M, __blue("[target] has almost been enslaved."))
|
||||
|
||||
last_complete = complete
|
||||
|
||||
onEnd()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/vampire/H = enslave.holder
|
||||
|
||||
if (target.mind)
|
||||
target.mind.special_role = "vampthrall"
|
||||
target.mind.master = M.ckey
|
||||
if (!(target.mind in ticker.mode.Agimmicks))
|
||||
ticker.mode.Agimmicks += target.mind
|
||||
|
||||
boutput(target, __red("<b>You awaken filled with purpose - you must serve your master, [M.real_name]!</B>"))
|
||||
target << browse(grabResource("html/mindslave/implanted.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
if (issilicon(target))
|
||||
boutput(target, __red("<b>You must serve your master. All previous laws are irrelevant.</b>"))
|
||||
|
||||
target.paralysis = 0
|
||||
if (istype(H)) H.vamp_isbiting = null
|
||||
target.vamp_beingbitten = 0
|
||||
|
||||
boutput(M, __blue("[target] has been enslaved and is now your thrall."))
|
||||
logTheThing("combat", M, target, "enthralled %target%, making them a loyal mindslave at [log_loc(M)].")
|
||||
|
||||
onInterrupt()
|
||||
..()
|
||||
|
||||
var/mob/living/M = owner
|
||||
var/datum/abilityHolder/vampire/H = enslave.holder
|
||||
|
||||
if (istype(H))
|
||||
H.vamp_isbiting = null
|
||||
|
||||
if (target)
|
||||
target.vamp_beingbitten = 0
|
||||
if (target.stat != 2)
|
||||
if (issilicon(target))
|
||||
boutput(target, __blue("System temperature appears to return to normal."))
|
||||
else
|
||||
boutput(target, __blue("The overwhelming feeling of coldness appears to recede. You immediately feel better."))
|
||||
|
||||
boutput(M, __red("Your attempt to enthrall the target was interrupted!"))
|
||||
@@ -0,0 +1,48 @@
|
||||
/datum/targetable/vampire/glare
|
||||
name = "Glare"
|
||||
desc = "Stuns one target for a short time. Blocked by eye protection."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 2
|
||||
cooldown = 600
|
||||
pointCost = 0
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 0
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to stun yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("It would be a waste of time to stun the dead."))
|
||||
return 1
|
||||
|
||||
if (!M.sight_check(1))
|
||||
boutput(M, __red("How do you expect this to work? You can't use your eyes right now."))
|
||||
M.visible_message("<span style=\"color:red\">What was that? There's something odd about [M]'s eyes.</span>")
|
||||
return 0 // Cooldown because spam is bad.
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M]'s eyes emit a blinding flash at [target]!</B></span>")
|
||||
|
||||
if (target.bioHolder && target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(target, __blue("[M]'s foul gaze falters as it stares upon your righteousness!"))
|
||||
target.visible_message("<span style=\"color:red\"><B>[target] glares right back at [M]!</B></span>")
|
||||
else
|
||||
target.apply_flash(30, 15)
|
||||
|
||||
logTheThing("combat", M, target, "uses glare on %target% at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,63 @@
|
||||
/datum/targetable/vampire/hypnotize
|
||||
name = "Hypnotize"
|
||||
desc = "KO's the target for a long time. Takes a few seconds to cast."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 1200
|
||||
pointCost = 25
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 0
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to stun yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("It would be a waste of time to stun the dead."))
|
||||
return 1
|
||||
|
||||
if (!isliving(target) || (isliving(target) && issilicon(target)))
|
||||
boutput(M, __red("This spell would have no effect on [target]."))
|
||||
return 1
|
||||
|
||||
if (!M.sight_check(1))
|
||||
boutput(M, __red("How do you expect this to work? You can't use your eyes right now."))
|
||||
M.visible_message("<span style=\"color:red\">What was that? There's something odd about [M]'s eyes.</span>")
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
return 0 // Cooldown because spam is bad.
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] stares into [target]'s eyes!</B></span>")
|
||||
boutput(M, __red("You have to stand still..."))
|
||||
|
||||
if (do_mob(M, target, 20))
|
||||
if (target.bioHolder && target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(target, __blue("Your faith protects you from [M]'s dark designs!"))
|
||||
target.visible_message("<span style=\"color:red\"><b>[target] just stares right back at [M]!</b></span>")
|
||||
|
||||
else if (target.sight_check(1)) // Can't stare through a blindfold very well, no?
|
||||
boutput(target, __red("Your consciousness is overwhelmed by [M]'s dark glare!"))
|
||||
boutput(M, __blue("Your piercing gaze knocks out [target]."))
|
||||
target.stunned = max(target.stunned, 50)
|
||||
target.weakened = max(target.weakened, 50)
|
||||
target.paralysis = max(target.paralysis, 33)
|
||||
else
|
||||
boutput(M, __red("Your attempt to hypnotize the target was interrupted!"))
|
||||
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
logTheThing("combat", M, target, "uses hypnotise on [target ? "%target%" : "*UNKNOWN*"] at [log_loc(M)].") // Target might have been gibbed, who knows.
|
||||
return 0
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/targetable/vampire/phaseshift_vampire
|
||||
name = "Mist form"
|
||||
desc = "Phase through walls. Only works when you can't be seen."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 600
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 0
|
||||
restricted_area_check = 1
|
||||
var/duration = 50
|
||||
unlock_message = "You have gained mist form. It temporarily turns you incorporeal, allowing you to pass through solid objects."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (spell_invisibility(M, 1, 1, 0, 1) != 1) // Dry run. Can we phaseshift?
|
||||
return 1
|
||||
|
||||
spell_invisibility(M, src.duration, 1)
|
||||
H.locked = 1 // Can't use any powers during phaseshift.
|
||||
spawn (src.duration)
|
||||
if (H) H.locked = 0
|
||||
|
||||
logTheThing("combat", M, null, "uses mist form at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,49 @@
|
||||
/datum/targetable/vampire/plague_touch
|
||||
name = "Diseased touch"
|
||||
desc = "Infects the target with a deadly, non-contagious disease."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 1800
|
||||
pointCost = 50
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
unlock_message = "You have gained diseased touch, which inflicts someone with a deadly, non-contagious disease."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to infect yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (target.stat == 2)
|
||||
boutput(M, __red("It would be a waste of time to infect the dead."))
|
||||
return 1
|
||||
|
||||
if (!iscarbon(target))
|
||||
boutput(M, __red("[target] is immune to the disease."))
|
||||
return 1
|
||||
|
||||
var/mob/living/L = target
|
||||
|
||||
playsound(M.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
M.visible_message("<span style=\"color:blue\">[M] shakes [L], trying to wake them up!</span>")
|
||||
L.add_fingerprint(M) // Why not leave some forensic evidence?
|
||||
if (!(L.bioHolder && L.bioHolder.HasEffect("training_chaplain")))
|
||||
L.contract_disease(/datum/ailment/disease/vamplague, null, null, 1) // path, name, strain, bypass resist
|
||||
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
logTheThing("combat", M, L, "uses diseased touch on %target% at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,39 @@
|
||||
/datum/targetable/vampire/radio_jammer
|
||||
name = "Radio interference"
|
||||
desc = "Temporarily disrupts all radio communication in the immediate vicinity."
|
||||
targeted = 0
|
||||
cooldown = 1800
|
||||
pointCost = 50
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 0
|
||||
var/duration = 300
|
||||
unlock_message = "You have gained radio interference. It temporarily disables all headsets and intercoms close to you."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (!(radio_controller && istype(radio_controller)))
|
||||
boutput(M, __red("Couldn't find the global radio controller. Please report this to a coder."))
|
||||
return 1
|
||||
|
||||
if (radio_controller.active_jammers.Find(M))
|
||||
boutput(M, __red("You're already jamming radio signals."))
|
||||
return 1
|
||||
|
||||
boutput(M, __blue("<b>You will disrupt radio signals in your immediate vicinity for the next [src.duration / 10] seconds.</b>"))
|
||||
radio_controller.active_jammers.Add(M)
|
||||
spawn (src.duration)
|
||||
if (M && istype(M) && radio_controller && istype(radio_controller) && radio_controller.active_jammers.Find(M))
|
||||
boutput(M, __red("<b>You no longer disrupt radio signals.</b>"))
|
||||
radio_controller.active_jammers.Remove(M)
|
||||
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
logTheThing("combat", M, null, "uses radio interference at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,46 @@
|
||||
/datum/targetable/vampire/vampire_scream
|
||||
name = "Chiropteran screech"
|
||||
desc = "Deafens nearby foes, smashes windows and lights. Blocked by ear protection."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 1200
|
||||
pointCost = 60
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 0
|
||||
unlock_message = "You have gained chiropteran screech. It deafens nearby foes, smashes windows and lights."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (M.wear_mask && istype(M.wear_mask, /obj/item/clothing/mask/muzzle))
|
||||
boutput(M, __red("How do you expect this to work? You're muzzled!"))
|
||||
M.visible_message("<span style=\"color:red\"><b>[M]</b> makes a loud noise.</span>")
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
return 0 // Cooldown because spam is bad.
|
||||
|
||||
M.emote("scream")
|
||||
|
||||
for (var/mob/living/HH in hearers(M, null))
|
||||
if (HH == M) continue
|
||||
if (isvampire(HH) && HH.check_vampire_power(3) == 1)
|
||||
boutput(HH, __blue("You are immune to [M]'s screech!"))
|
||||
continue
|
||||
if (HH.bioHolder && HH.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(HH, __blue("[M]'s scream only strengthens your resolve!"))
|
||||
continue
|
||||
|
||||
HH.apply_sonic_stun(0, 0, 40, 0, 15, 8, 12)
|
||||
|
||||
sonic_attack_environmental_effect(M, 7, list("light", "window", "r_window"))
|
||||
|
||||
if (istype(H)) H.blood_tracking_output(src.pointCost)
|
||||
logTheThing("combat", M, null, "uses chiropteran screech at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,40 @@
|
||||
/datum/targetable/vampire/vamp_cloak
|
||||
name = "Toggle cloak"
|
||||
desc = "Toggles your cloak of darkness, which is only effective in dark areas."
|
||||
targeted = 0
|
||||
target_nodamage_check = 0
|
||||
max_range = 0
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 0
|
||||
unlock_message = "You have gained cloak of darkness. It makes you invisible in dark areas and is a toggleable, permanent effect."
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
if (!ishuman(M)) // Only humans use bioeffects at the moment.
|
||||
boutput(M, __red("You can't use this ability in your current form."))
|
||||
return 1
|
||||
|
||||
var/mob/living/carbon/human/MM = M
|
||||
if (!MM.bioHolder)
|
||||
boutput(MM, __red("You can't use this ability in your current form."))
|
||||
return 1
|
||||
|
||||
if (MM.bioHolder.HasEffect("cloak_of_darkness"))
|
||||
MM.bioHolder.RemoveEffect("cloak_of_darkness")
|
||||
MM.set_body_icon_dirty() // Might help to get rid of those overlay issues.
|
||||
else
|
||||
var/datum/bioEffect/power/darkcloak/DC = MM.bioHolder.AddEffect("cloak_of_darkness")
|
||||
if (DC && istype(DC))
|
||||
DC.active = 1 // Important!
|
||||
MM.set_body_icon_dirty()
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,156 @@
|
||||
/datum/targetable/vampire/vampire_bite
|
||||
name = "Bite victim"
|
||||
desc = "Bite the victim's neck to drain them of blood."
|
||||
targeted = 1
|
||||
target_nodamage_check = 1
|
||||
max_range = 1
|
||||
cooldown = 0
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
dont_lock_holder = 1
|
||||
restricted_area_check = 2
|
||||
|
||||
proc/can_bite(var/mob/living/carbon/human/target)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M || !target)
|
||||
return 0
|
||||
|
||||
if (!ishuman(target)) // Only humans use the blood system.
|
||||
boutput(M, __red("You can't seem to find any blood vessels."))
|
||||
return 0
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to bite yourself?"))
|
||||
return 0
|
||||
|
||||
if (iscritter(M) && !istype(H))
|
||||
boutput(M, __red("Critter mobs currently don't have to worry about blood. Lucky you."))
|
||||
return 0
|
||||
|
||||
if (istype(H) && H.vamp_isbiting)
|
||||
boutput(M, __red("You are already draining someone's blood!"))
|
||||
return 0
|
||||
|
||||
if (target.head && target.head.body_parts_covered & HEAD)
|
||||
boutput(M, __red("You need to remove their headgear first."))
|
||||
return 0
|
||||
|
||||
if (target.wear_mask && target.wear_mask.body_parts_covered & HEAD)
|
||||
boutput(M, __red("You need to remove their facemask first."))
|
||||
return 0
|
||||
|
||||
if (check_target_immunity(target) == 1)
|
||||
target.visible_message("<span style=\"color:red\"><B>[M] bites [target], but fails to even pierce their skin!</B></span>")
|
||||
return 0
|
||||
|
||||
if ((target.mind && target.mind.special_role == "vampthrall") && target.is_mentally_dominated_by(M))
|
||||
boutput(M, __red("You can't drink the blood of your own thralls!"))
|
||||
return 0
|
||||
|
||||
if (ismonkey(target) || (target.bioHolder && target.bioHolder.HasEffect("monkey")))
|
||||
boutput(M, __red("Drink monkey blood?! That's disgusting!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/vampire/H = holder
|
||||
|
||||
if (!M || !target || !ismob(target))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (src.can_bite(target) != 1)
|
||||
return 1
|
||||
|
||||
logTheThing("combat", M, target, "bites %target%'s neck at [log_loc(M)].")
|
||||
|
||||
boutput(M, __blue("You bite [target] and begin to drain them of blood."))
|
||||
target.visible_message("<span style=\"color:red\"><B>[M] bites [target]!</B></span>")
|
||||
if (istype(H)) H.vamp_isbiting = target
|
||||
target.vamp_beingbitten = 1
|
||||
|
||||
var/mob/living/carbon/human/HH = target
|
||||
|
||||
while (do_mob(M, HH, 30))
|
||||
if (HH.blood_volume > 0)
|
||||
|
||||
if (HH.stat == 2)
|
||||
if (prob(20))
|
||||
boutput(M, __red("The blood of the dead provides little sustenance..."))
|
||||
M.change_vampire_blood(5, 1)
|
||||
M.change_vampire_blood(5, 0)
|
||||
if (HH.blood_volume < 20)
|
||||
HH.blood_volume = 0
|
||||
else
|
||||
HH.blood_volume -= 20
|
||||
if (istype(H)) H.blood_tracking_output()
|
||||
|
||||
else if (HH.bioHolder && HH.bioHolder.HasEffect("training_chaplain"))
|
||||
M.visible_message("<span style=\"color:red\"><b>[M]</b> begins to crisp and burn!</span>", "<span style=\"color:red\">You drank the blood of a holy man! It burns!</span>")
|
||||
M.emote("scream")
|
||||
if (M.get_vampire_blood() >= 20)
|
||||
M.change_vampire_blood(-20, 0)
|
||||
else
|
||||
M.change_vampire_blood(0, 0, 1)
|
||||
M.TakeDamage("chest", 0, 30)
|
||||
|
||||
else
|
||||
if (isvampire(HH))
|
||||
if (HH.get_vampire_blood() >= 20)
|
||||
HH.change_vampire_blood(-20, 0)
|
||||
HH.change_vampire_blood(-20, 1) // Otherwise, two vampires could perpetually feed off of each other, trading blood endlessly.
|
||||
M.change_vampire_blood(20, 0)
|
||||
M.change_vampire_blood(20, 1)
|
||||
if (istype(H)) H.blood_tracking_output()
|
||||
if (prob(50))
|
||||
boutput(M, __red("This is the blood of a fellow vampire!"))
|
||||
else
|
||||
HH.change_vampire_blood(0, 0, 1)
|
||||
HH.vamp_beingbitten = 0
|
||||
if (istype(H)) H.vamp_isbiting = null
|
||||
boutput(M, __red("[HH] doesn't have enough blood left to drink."))
|
||||
return 0
|
||||
|
||||
else
|
||||
M.change_vampire_blood(10, 1)
|
||||
M.change_vampire_blood(10, 0)
|
||||
if (HH.blood_volume < 20)
|
||||
HH.blood_volume = 0
|
||||
else
|
||||
HH.blood_volume -= 20
|
||||
if (HH.blood_volume < 300 && prob(15))
|
||||
if (HH.paralysis == 0)
|
||||
boutput(HH, __red("Your vision fades to blackness."))
|
||||
HH.paralysis = min(HH.paralysis + 5, 10)
|
||||
else
|
||||
if (prob(65))
|
||||
HH.weakened = min(HH.weakened + 3, 10)
|
||||
HH.stuttering = min(HH.stuttering + 3, 10)
|
||||
if (istype(H)) H.blood_tracking_output()
|
||||
|
||||
if (istype(H)) H.check_for_unlocks()
|
||||
|
||||
else
|
||||
boutput(M, __red("[HH] doesn't have enough blood left to drink."))
|
||||
if (istype(H)) H.vamp_isbiting = null
|
||||
HH.vamp_beingbitten = 0
|
||||
return 0
|
||||
|
||||
boutput(M, __red("Your feast was interrupted."))
|
||||
if (istype(H)) H.vamp_isbiting = null
|
||||
if (HH) HH.vamp_beingbitten = 0 // Victim might have been gibbed, who knowns.
|
||||
return 0
|
||||
@@ -0,0 +1,377 @@
|
||||
// Converted everything related to werewolves from client procs to ability holders and used
|
||||
// the opportunity to do some clean-up as well (Convair880).
|
||||
|
||||
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
||||
|
||||
/mob/proc/make_werewolf()
|
||||
if (ishuman(src))
|
||||
var/datum/abilityHolder/werewolf/A = src.get_ability_holder(/datum/abilityHolder/werewolf)
|
||||
if (A && istype(A))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/werewolf/W = src.add_ability_holder(/datum/abilityHolder/werewolf)
|
||||
W.addAbility(/datum/targetable/werewolf/werewolf_transform)
|
||||
W.addAbility(/datum/targetable/werewolf/werewolf_feast)
|
||||
|
||||
src.resistances += /datum/ailment/disease/lycanthropy
|
||||
|
||||
if (src.mind && src.mind.special_role != "omnitraitor")
|
||||
src << browse(grabResource("html/traitorTips/werewolfTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
else return
|
||||
|
||||
////////////////////////////////////////////// Helper procs //////////////////////////////
|
||||
|
||||
// Avoids C&P code for that werewolf disease.
|
||||
/mob/proc/werewolf_transform(var/source_is_lycanthrophy = 0, var/message_type = 0)
|
||||
if (ishuman(src))
|
||||
var/mob/living/carbon/human/M = src
|
||||
var/which_way = 0
|
||||
|
||||
if (!M.mutantrace || source_is_lycanthrophy == 1)
|
||||
M.jitteriness = 0
|
||||
M.stunned = 0
|
||||
M.weakened = 0
|
||||
M.paralysis = 0
|
||||
M.slowed = 0
|
||||
M.change_misstep_chance(-INFINITY)
|
||||
M.stuttering = 0
|
||||
M.drowsyness = 0
|
||||
|
||||
if (M.handcuffed)
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] rips apart the handcuffs with pure brute strength!</b></span>")
|
||||
qdel(M.handcuffed)
|
||||
M.handcuffed = null
|
||||
M.buckled = null
|
||||
|
||||
playsound(M.loc, 'sound/effects/blobattack.ogg', 50, 1, -1)
|
||||
spawn (5)
|
||||
if (M && M.mutantrace && istype(M.mutantrace, /datum/mutantrace/werewolf))
|
||||
M.emote("howl")
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] [pick("metamorphizes", "transforms", "changes")] into a werewolf! Holy shit!</B></span>")
|
||||
if (message_type == 0)
|
||||
boutput(M, __blue("<h3>You are now a werewolf.</h3>"))
|
||||
else
|
||||
boutput(M, __blue("<h3>You are now a werewolf. You can remain in this form indefinitely or change back at any time.</h3>"))
|
||||
|
||||
if (source_is_lycanthrophy == 1 && M.mutantrace)
|
||||
qdel(M.mutantrace)
|
||||
M.set_mutantrace(/datum/mutantrace/werewolf)
|
||||
M.set_face_icon_dirty()
|
||||
M.set_body_icon_dirty()
|
||||
M.update_clothing()
|
||||
|
||||
which_way = 0
|
||||
|
||||
else
|
||||
if (source_is_lycanthrophy == 1) // Werewolf disease is human -> WW only.
|
||||
return
|
||||
|
||||
boutput(M, __blue("<h3>You transform back into your human form.</h3>"))
|
||||
|
||||
qdel(M.mutantrace)
|
||||
M.set_face_icon_dirty()
|
||||
M.set_body_icon_dirty()
|
||||
M.update_clothing()
|
||||
|
||||
which_way = 1
|
||||
|
||||
logTheThing("combat", M, null, "[which_way == 0 ? "transforms into a werewolf" : "changes back into human form"] at [log_loc(M)].")
|
||||
return
|
||||
|
||||
// There used to be more stuff here, most of which was moved to limb datums.
|
||||
/mob/proc/werewolf_attack(var/mob/target = null, var/attack_type = "")
|
||||
if (!iswerewolf(src))
|
||||
return 0
|
||||
|
||||
var/mob/living/carbon/human/M = src
|
||||
if (!ishuman(M))
|
||||
return 0
|
||||
|
||||
if (!target || !ismob(target))
|
||||
return 0
|
||||
|
||||
if (target == M)
|
||||
return 0
|
||||
|
||||
if (check_target_immunity(target) == 1)
|
||||
target.visible_message("<span style=\"color:red\"><B>[M]'s swipe bounces off of [target] uselessly!</B></span>")
|
||||
return 0
|
||||
|
||||
var/damage = 0
|
||||
var/send_flying = 0 // 1: a little bit | 2: across the room
|
||||
|
||||
switch (attack_type)
|
||||
if ("feast") // Only used by the feast ability.
|
||||
var/mob/living/carbon/human/HH = target
|
||||
|
||||
if (!HH || !ishuman(HH))
|
||||
return 0
|
||||
|
||||
var/healing = 0
|
||||
|
||||
if (!HH.canmove)
|
||||
damage += rand(5,15)
|
||||
healing = damage - 5
|
||||
|
||||
if (prob(40))
|
||||
HH.spread_blood_clothes(HH)
|
||||
M.spread_blood_hands(HH)
|
||||
|
||||
var/obj/decal/cleanable/blood/gibs/G = null // For forensics.
|
||||
G = new /obj/decal/cleanable/blood/gibs(HH.loc)
|
||||
if (HH.bioHolder && HH.bioHolder.Uid && HH.bioHolder.bloodType)
|
||||
G.blood_DNA = HH.bioHolder.Uid
|
||||
G.blood_type = HH.bioHolder.bloodType
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] messily [pick("rips", "tears")] out and [pick("eats", "devours", "wolfs down", "chows on")] some of [HH]'s [pick("guts", "intestines", "entrails")]!</B></span>")
|
||||
|
||||
else
|
||||
HH.spread_blood_clothes(HH)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] [pick("chomps on", "chews off a chunk of", "gnaws on")] [HH]'s [pick("right arm", "left arm", "head", "right leg", "left leg")]!</B></span>")
|
||||
|
||||
if (ismonkey(HH) || HH.bioHolder && HH.bioHolder.HasEffect("monkey"))
|
||||
boutput(M, __red("Monkey flesh just isn't the real deal..."))
|
||||
healing /= 2
|
||||
else if (HH.stat == 2)
|
||||
boutput(M, __red("Fresh meat would be much preferable to this cadaver..."))
|
||||
healing /= 2
|
||||
else if (HH.health < -150)
|
||||
boutput(M, __red("[target] is pretty mangled. There's not a lot of flesh left..."))
|
||||
healing /= 1.5
|
||||
else
|
||||
if (iscluwne(HH))
|
||||
boutput(M, __red("That tasted awful!"))
|
||||
healing /= 2
|
||||
M.take_toxin_damage(5)
|
||||
else if (iswerewolf(HH) || ispredator(HH) || isabomination(HH))
|
||||
boutput(M, __blue("That tasted fantastic!"))
|
||||
healing *= 2
|
||||
else if (HH.nutrition > 100 || HH.bioHolder && HH.bioHolder.HasEffect("fat"))
|
||||
boutput(M, __blue("That tasted amazing!"))
|
||||
M.unlock_medal("Space Ham", 1)
|
||||
healing *= 2
|
||||
else if (HH.mind && HH.mind.assigned_role == "Clown")
|
||||
boutput(M, __blue("That tasted funny, huh."))
|
||||
M.unlock_medal("That tasted funny", 1)
|
||||
else
|
||||
boutput(M, __blue("That tasted good!"))
|
||||
|
||||
HH.add_fingerprint(M) // Just put 'em on the mob itself, like pulling does. Simplifies forensic analysis a bit.
|
||||
M.werewolf_audio_effects(HH, "feast")
|
||||
|
||||
HH.weakened = max(HH.weakened, rand(3,6))
|
||||
if (prob(33) && HH.stat != 2)
|
||||
HH.emote("scream")
|
||||
|
||||
M.remove_stamina(60) // Werewolves have a very large stamina and stamina regen boost.
|
||||
if (healing > 0)
|
||||
M.HealDamage("All", healing, healing)
|
||||
M.updatehealth()
|
||||
|
||||
else // Can't feast on people if they're moving around too much.
|
||||
return 0
|
||||
else
|
||||
return 0
|
||||
|
||||
switch (send_flying)
|
||||
if (1)
|
||||
wrestler_knockdown(M, target)
|
||||
|
||||
if (2)
|
||||
wrestler_backfist(M, target)
|
||||
|
||||
if (damage > 0)
|
||||
random_brute_damage(target, damage)
|
||||
target.updatehealth()
|
||||
target.UpdateDamageIcon()
|
||||
target.set_clothing_icon_dirty()
|
||||
|
||||
return 1
|
||||
|
||||
// Also called by limb datums.
|
||||
/mob/proc/werewolf_audio_effects(var/mob/target = null, var/type = "disarm")
|
||||
if (!src || !ismob(src) || !target || !ismob(target))
|
||||
return
|
||||
|
||||
var/sound_playing = 0
|
||||
|
||||
switch (type)
|
||||
if ("disarm")
|
||||
playsound(src.loc, pick('sound/misc/werewolf_attack1.ogg', 'sound/misc/werewolf_attack2.ogg', 'sound/misc/werewolf_attack3.ogg'), 50, 1)
|
||||
spawn (1)
|
||||
if (src) playsound(src.loc, "swing_hit", 50, 1)
|
||||
|
||||
if ("swipe")
|
||||
if (prob(50))
|
||||
playsound(src.loc, pick('sound/misc/werewolf_attack1.ogg', 'sound/misc/werewolf_attack2.ogg', 'sound/misc/werewolf_attack3.ogg'), 50, 1)
|
||||
else
|
||||
playsound(src.loc, pick('sound/misc/loudcrunch.ogg', 'sound/misc/loudcrunch2.ogg'), 50, 1, -1)
|
||||
|
||||
spawn (1)
|
||||
if (src) playsound(src.loc, "sound/weapons/DSCLAW.ogg", 40, 1, -1)
|
||||
|
||||
if ("feast")
|
||||
if (sound_playing == 0) // It's a long audio clip.
|
||||
playsound(src.loc, "sound/misc/wendigo_maul.ogg", 80, 1)
|
||||
sound_playing = 1
|
||||
spawn (60)
|
||||
sound_playing = 0
|
||||
|
||||
playsound(src.loc, pick('sound/misc/loudcrunch.ogg', 'sound/misc/loudcrunch2.ogg'), 50, 1, -1)
|
||||
playsound(src.loc, "sound/items/eatfood.ogg", 50, 1, -1)
|
||||
if (prob(40))
|
||||
playsound(target.loc, "sound/effects/splat.ogg", 50, 1)
|
||||
spawn (10)
|
||||
if (src && ishuman(src) && prob(50))
|
||||
src.emote("burp")
|
||||
|
||||
return
|
||||
|
||||
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/werewolf
|
||||
clicked(params)
|
||||
var/datum/targetable/werewolf/spell = owner
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (!isturf(owner.holder.owner.loc))
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this ability here.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
if (spell.targeted)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
return
|
||||
|
||||
/datum/abilityHolder/werewolf
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Werewolf"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">You aren't strong enough to use this ability.</span>"
|
||||
var/datum/objective/specialist/werewolf/feed/feed_objective = null
|
||||
|
||||
onAbilityStat() // In the 'Werewolf' tab.
|
||||
..()
|
||||
|
||||
if (src.owner.mind && src.owner.mind.special_role == "werewolf")
|
||||
for (var/datum/objective/specialist/werewolf/feed/O in src.owner.mind.objectives)
|
||||
src.feed_objective = O
|
||||
|
||||
if (src.feed_objective && istype(src.feed_objective))
|
||||
stat("No. of victims:", src.feed_objective.feed_count)
|
||||
|
||||
return
|
||||
|
||||
/////////////////////////////////////////////// Werewolf spell parent ////////////////////////////
|
||||
|
||||
/datum/targetable/werewolf
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // No custom sprites yet.
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
pointCost = 0
|
||||
preferred_holder_type = /datum/abilityHolder/werewolf
|
||||
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
||||
var/not_when_handcuffed = 0
|
||||
var/werewolf_only = 0
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/werewolf/B = new /obj/screen/ability/werewolf(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
return
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/werewolf()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
return
|
||||
|
||||
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
switch (stunned_only_is_okay)
|
||||
if (0)
|
||||
if (M.stat != 0 || M.stunned > 0 || M.paralysis > 0 || M.weakened > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
if (1)
|
||||
if (M.stat != 0 || M.paralysis > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
else
|
||||
return 1
|
||||
|
||||
castcheck()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/carbon/human/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 0
|
||||
|
||||
if (!ishuman(M)) // Only humans use mutantrace datums.
|
||||
boutput(M, __red("You cannot use any powers in your current form."))
|
||||
return 0
|
||||
|
||||
if (M.transforming)
|
||||
boutput(M, __red("You can't use any powers right now."))
|
||||
return 0
|
||||
|
||||
if (werewolf_only == 1 && !iswerewolf(M))
|
||||
boutput(M, __red("You must be in your wolf form to use this ability."))
|
||||
return 0
|
||||
|
||||
if (incapacitation_check(src.when_stunned) != 1)
|
||||
boutput(M, __red("You can't use this ability while incapacitated!"))
|
||||
return 0
|
||||
|
||||
if (src.not_when_handcuffed == 1 && M.restrained())
|
||||
boutput(M, __red("You can't use this ability when restrained!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
return
|
||||
@@ -0,0 +1,242 @@
|
||||
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
||||
|
||||
/proc/equip_wizard(mob/living/carbon/human/wizard_mob, var/robe = 0)
|
||||
if (!ishuman(wizard_mob)) return
|
||||
|
||||
var/datum/abilityHolder/H = wizard_mob.add_ability_holder(/datum/abilityHolder/wizard)
|
||||
H.addAbility(/datum/targetable/spell/phaseshift)
|
||||
H.addAbility(/datum/targetable/spell/magicmissile)
|
||||
H.addAbility(/datum/targetable/spell/clairvoyance)
|
||||
|
||||
if (wizard_mob.mind)
|
||||
wizard_mob.mind.is_wizard = H
|
||||
|
||||
spawn (25) // Don't remove.
|
||||
if (wizard_mob) wizard_mob.assign_gimmick_skull() // For variety and predators (Convair880).
|
||||
|
||||
wizard_mob.bioHolder.mobAppearance.customization_first_color = "#FFFFFF"
|
||||
wizard_mob.bioHolder.mobAppearance.customization_second_color = "#FFFFFF"
|
||||
wizard_mob.cust_two_state = "wiz"
|
||||
wizard_mob.set_face_icon_dirty()
|
||||
|
||||
var/obj/item/SWF_uplink/SB = new /obj/item/SWF_uplink(wizard_mob)
|
||||
if (wizard_mob.mind)
|
||||
SB.wizard_key = wizard_mob.mind.key
|
||||
|
||||
//so that this proc will work for wizards made mid-round who are wearing stuff
|
||||
for(var/obj/item/I in list(wizard_mob.w_uniform, wizard_mob.wear_suit,wizard_mob.head, wizard_mob.ears, wizard_mob.back, wizard_mob.shoes, wizard_mob.r_hand, wizard_mob.l_hand, wizard_mob.r_store, wizard_mob.l_store,wizard_mob.belt))
|
||||
wizard_mob.u_equip(I)
|
||||
I.set_loc(wizard_mob.loc)
|
||||
I.dropped(wizard_mob)
|
||||
I.layer = initial(I.layer)
|
||||
|
||||
if(robe) wizard_mob.equip_if_possible(new /obj/item/clothing/suit/wizrobe(wizard_mob), wizard_mob.slot_wear_suit)
|
||||
wizard_mob.equip_if_possible(new /obj/item/clothing/under/shorts/black(wizard_mob), wizard_mob.slot_w_uniform)
|
||||
wizard_mob.equip_if_possible(new /obj/item/clothing/head/wizard(wizard_mob), wizard_mob.slot_head)
|
||||
wizard_mob.equip_if_possible(new /obj/item/device/radio/headset(wizard_mob), wizard_mob.slot_ears)
|
||||
wizard_mob.equip_if_possible(new /obj/item/storage/backpack(wizard_mob), wizard_mob.slot_back)
|
||||
wizard_mob.equip_if_possible(new /obj/item/clothing/shoes/sandal(wizard_mob), wizard_mob.slot_shoes)
|
||||
wizard_mob.equip_if_possible(new /obj/item/staff(wizard_mob), wizard_mob.slot_r_hand)
|
||||
wizard_mob.equip_if_possible(new /obj/item/teleportation_scroll(wizard_mob), wizard_mob.slot_l_hand)
|
||||
wizard_mob.equip_if_possible(new /obj/item/paper/Wizardry101(wizard_mob), wizard_mob.slot_l_store)
|
||||
wizard_mob.equip_if_possible(SB, wizard_mob.slot_belt)
|
||||
wizard_mob.set_clothing_icon_dirty()
|
||||
|
||||
boutput(wizard_mob, "The Space Wizards Federation has equipped you with a Spellbook on your belt with which to purchase your desired spells.")
|
||||
wizard_mob << browse(grabResource("html/traitorTips/wizardTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
return
|
||||
|
||||
////////////////////////////////////////////// Helper procs ////////////////////////////////////////////////////
|
||||
|
||||
/mob/proc/wizard_spellpower()
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/wizard_spellpower()
|
||||
var/magcount = 0
|
||||
if (!src) return 0 // ??
|
||||
if (src.bioHolder.HasEffect("arcane_power") == 2)
|
||||
magcount += 10
|
||||
for (var/obj/item/clothing/C in src.contents)
|
||||
if (C.magical) magcount += 1
|
||||
if (istype(src.r_hand, /obj/item/staff))
|
||||
magcount += 2
|
||||
if (istype(src.l_hand, /obj/item/staff))
|
||||
magcount += 2
|
||||
if (magcount >= 4) return 1
|
||||
else return 0
|
||||
|
||||
/mob/living/critter/wizard_spellpower()
|
||||
var/magcount = 0
|
||||
for (var/obj/item/clothing/C in src.contents)
|
||||
if (C.magical) magcount += 1
|
||||
if (src.find_in_hands(/obj/item/staff))
|
||||
magcount += 2
|
||||
if (magcount >= 4) return 1
|
||||
else return 0
|
||||
|
||||
/mob/proc/wizard_castcheck(var/offensive = 0)
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/wizard_castcheck(var/offensive = 0)
|
||||
if(src.stat)
|
||||
boutput(src, "You can't cast spells while incapacitated.")
|
||||
return 0
|
||||
if (src.bioHolder.HasEffect("arcane_power") == 2)
|
||||
return 1
|
||||
if(!istype(src.wear_suit, /obj/item/clothing/suit/wizrobe))
|
||||
boutput(src, "You don't feel strong enough without a magical robe.")
|
||||
return 0
|
||||
if(!istype(src.head, /obj/item/clothing/head/wizard))
|
||||
boutput(src, "You don't feel strong enough without a magical hat.")
|
||||
return 0
|
||||
var/area/getarea = get_area(src)
|
||||
if(getarea.name == "Chapel" || getarea.name == "Chapel Office")
|
||||
if (get_corruption_percent() < 40)
|
||||
boutput(src, "You cannot cast spells on hallowed ground!")// Maybe if the station were more corrupted...")
|
||||
return 0
|
||||
if (offensive == 1 && src.bioHolder.HasEffect("arcane_shame"))
|
||||
boutput(src, "You are too consumed with shame to cast that spell!")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/mob/living/critter/wizard_castcheck(var/offensive = 0)
|
||||
if(src.stat)
|
||||
boutput(src, "You can't cast spells while incapacitated.")
|
||||
return 0
|
||||
if(!find_in_equipment(/obj/item/clothing/suit/wizrobe))
|
||||
boutput(src, "You don't feel strong enough without a magical robe.")
|
||||
return 0
|
||||
if(!find_in_equipment(/obj/item/clothing/head/wizard))
|
||||
boutput(src, "You don't feel strong enough without a magical hat.")
|
||||
return 0
|
||||
var/area/getarea = get_area(src)
|
||||
if(getarea.name == "Chapel" || getarea.name == "Chapel Office")
|
||||
if (get_corruption_percent() < 40)
|
||||
boutput(src, "You cannot cast spells on hallowed ground!")// Maybe if the station were more corrupted...")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/spell
|
||||
clicked(params)
|
||||
var/datum/targetable/spell/spell = owner
|
||||
var/datum/abilityHolder/holder = owner.holder
|
||||
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
|
||||
if(params["shift"] && params["ctrl"])
|
||||
if(owner.waiting_for_hotkey)
|
||||
holder.cancel_action_binding()
|
||||
return
|
||||
else
|
||||
owner.waiting_for_hotkey = 1
|
||||
src.updateIcon()
|
||||
boutput(usr, "<span style=\"color:blue\">Please press a number to bind this ability to...</span>")
|
||||
return
|
||||
|
||||
if (!isturf(usr.loc))
|
||||
return
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
var/mob/user = spell.holder.owner
|
||||
if (!user.wizard_castcheck(spell.offensive))
|
||||
return
|
||||
if (spell.targeted)
|
||||
usr:targeting_spell = owner
|
||||
usr.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
|
||||
/datum/abilityHolder/wizard
|
||||
usesPoints = 0
|
||||
topBarRendered = 0
|
||||
tabName = "Wizard"
|
||||
|
||||
/////////////////////////////////////////////// Wizard spell parent ////////////////////////////
|
||||
|
||||
/datum/targetable/spell
|
||||
preferred_holder_type = /datum/abilityHolder/wizard
|
||||
var
|
||||
requires_robes = 0
|
||||
offensive = 0
|
||||
cooldown_staff = 0
|
||||
prepared_count = 0
|
||||
casting_time = 0
|
||||
|
||||
proc/calculate_cooldown()
|
||||
var/cool = src.cooldown
|
||||
var/mob/user = src.holder.owner
|
||||
if (user && user.bioHolder)
|
||||
switch (user.bioHolder.HasEffect("arcane_power"))
|
||||
if (1)
|
||||
cool /= 2
|
||||
if (2)
|
||||
cool = 1
|
||||
if (src.cooldown_staff && !user.wizard_spellpower())
|
||||
cool *= 1.5
|
||||
return cool
|
||||
|
||||
dispose()
|
||||
if (object)
|
||||
qdel(object)
|
||||
|
||||
doCooldown()
|
||||
src.last_cast = world.time + calculate_cooldown()
|
||||
|
||||
tryCast(atom/target)
|
||||
if (!holder || !holder.owner)
|
||||
return 1
|
||||
var/datum/abilityHolder/wizard/H = holder
|
||||
if (H.locked && src.ignore_holder_lock != 1)
|
||||
boutput(holder.owner, "<span style=\"color:red\">You're already casting an ability.</span>")
|
||||
return 1 // ASSHOLES
|
||||
if (src.last_cast > world.time)
|
||||
return 1
|
||||
if (src.restricted_area_check)
|
||||
var/turf/T = get_turf(holder.owner)
|
||||
if (!T || !isturf(T))
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability doesn't seem to work here.</span>")
|
||||
return 1
|
||||
switch (src.restricted_area_check)
|
||||
if (1)
|
||||
if (isrestrictedz(T.z))
|
||||
boutput(holder.owner, "<span style=\"color:red\">That ability doesn't seem to work here.</span>")
|
||||
return 1
|
||||
if (2)
|
||||
var/area/A = get_area(T)
|
||||
if (A && istype(A, /area/sim))
|
||||
boutput(holder.owner, "<span style=\"color:red\">You can't use this ability in virtual reality.</span>")
|
||||
return 1
|
||||
if (src.dont_lock_holder != 1)
|
||||
H.locked = 1
|
||||
if (src.cooldown_staff && !holder.owner.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell takes longer to recharge without a staff to focus it!</span>")
|
||||
var/val = cast(target)
|
||||
H.locked = 0
|
||||
return val
|
||||
|
||||
updateObject()
|
||||
if (!holder || !holder.owner)
|
||||
qdel(src)
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/spell()
|
||||
object.icon = src.icon
|
||||
if (src.last_cast > world.time)
|
||||
object.name = "[src.name] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
object.name = src.name
|
||||
object.icon_state = src.icon_state
|
||||
object.owner = src
|
||||
|
||||
castcheck()
|
||||
return holder.owner.wizard_castcheck(src.offensive)
|
||||
@@ -0,0 +1,35 @@
|
||||
/datum/targetable/spell/animatedead
|
||||
name = "Animate Dead"
|
||||
desc = "Turns a human corpse into a skeletal minion."
|
||||
icon_state = "pet"
|
||||
targeted = 1
|
||||
max_range = 1
|
||||
cooldown = 850
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
cooldown_staff = 1
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
if(target.stat != 2)
|
||||
boutput(holder.owner, "<span style=\"color:red\">That person is still alive! Find a corpse.</span>")
|
||||
return 1 // No cooldown when it fails.
|
||||
|
||||
holder.owner.say("EI NECRIS")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/AnimateDeadLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/obj/critter/magiczombie/UMMACTUALLYITSASKELETONNOWFUCKZOMBIESFOREVER = new /obj/critter/magiczombie(get_turf(target)) // what the fuck
|
||||
UMMACTUALLYITSASKELETONNOWFUCKZOMBIESFOREVER.CustomizeMagZom(target.real_name)
|
||||
|
||||
boutput(holder.owner, "<span style=\"color:blue\">You saturate [target] with dark magic!</span>")
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] rips the skeleton from [target]'s corpse!</span>")
|
||||
|
||||
for(var/obj/item/I in target)
|
||||
if(istype(target, /obj/item))
|
||||
target.u_equip(I)
|
||||
if(I)
|
||||
I.set_loc(target.loc)
|
||||
I.dropped(target)
|
||||
target.gib(1)
|
||||
@@ -0,0 +1,57 @@
|
||||
/datum/targetable/spell/blind
|
||||
name = "Blind"
|
||||
desc = "Makes the victim temporarily unable to see."
|
||||
icon_state = "blind"
|
||||
targeted = 1
|
||||
cooldown = 100
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("YSTIGG MITAZIM")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/BlindLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
|
||||
if (target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[target] has divine protection from magic.</span>")
|
||||
target.visible_message("<span style=\"color:red\">The spell fails to work on [target]!</span>")
|
||||
return
|
||||
|
||||
if (iswizard(target) && target.wizard_spellpower())
|
||||
target.visible_message("<span style=\"color:red\">The spell fails to work on [target]!</span>")
|
||||
return
|
||||
|
||||
var/obj/overlay/B = new /obj/overlay(target.loc)
|
||||
B.icon_state = "blspell"
|
||||
B.icon = 'icons/obj/wizard.dmi'
|
||||
B.name = "spell"
|
||||
B.anchored = 1
|
||||
B.density = 0
|
||||
B.layer = MOB_EFFECT_LAYER
|
||||
target.canmove = 0
|
||||
spawn(5)
|
||||
qdel(B)
|
||||
target.canmove = 1
|
||||
boutput(target, "<span style=\"color:blue\">Your eyes cry out in pain!</span>")
|
||||
target.visible_message("<span style=\"color:red\">Sparks fly out of [target]'s eyes!</span>")
|
||||
if (holder.owner.wizard_spellpower())
|
||||
target.weakened += 2
|
||||
target.bioHolder.AddEffect("bad_eyesight")
|
||||
spawn(450)
|
||||
if (target) target.bioHolder.RemoveEffect("bad_eyesight")
|
||||
target.take_eye_damage(10, 1)
|
||||
target.change_eye_blurry(20)
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell doesn't last as long without a staff to focus it!</span>")
|
||||
target.weakened += 1
|
||||
target.bioHolder.AddEffect("bad_eyesight")
|
||||
spawn(300)
|
||||
target.bioHolder.RemoveEffect("bad_eyesight")
|
||||
target.take_eye_damage(5, 1)
|
||||
target.change_eye_blurry(10)
|
||||
@@ -0,0 +1,67 @@
|
||||
/datum/targetable/spell/blink
|
||||
name = "Blink"
|
||||
desc = "Teleport randomly to a nearby tile."
|
||||
icon_state = "blink"
|
||||
targeted = 0
|
||||
cooldown = 100
|
||||
requires_robes = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
var/mob/living/carbon/human/H = holder.owner
|
||||
|
||||
holder.owner.say("SYCAR TYN")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/BlinkLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/accuracy = 3
|
||||
if(holder.owner.wizard_spellpower())
|
||||
accuracy = 1
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
|
||||
if(H.burning)
|
||||
boutput(holder.owner, "<span style=\"color:blue\">The flames sputter out as you blink away.</span>")
|
||||
H.set_burning(0)
|
||||
|
||||
var/targetx = holder.owner.x
|
||||
var/targety = holder.owner.y
|
||||
|
||||
if(holder.owner.dir == 1)
|
||||
targety = holder.owner.y + 4
|
||||
targetx = holder.owner.x
|
||||
else if(holder.owner.dir == 4)
|
||||
targetx = holder.owner.x + 4
|
||||
targety = holder.owner.y
|
||||
else if(holder.owner.dir == 2)
|
||||
targety = holder.owner.y - 4
|
||||
targetx = holder.owner.x
|
||||
else if(holder.owner.dir == 8)
|
||||
targetx = holder.owner.x - 4
|
||||
targety = holder.owner.y
|
||||
|
||||
var/turf/targetturf = locate(targetx, targety, holder.owner.z)
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/mag_teleport.ogg", 25, 1, -1)
|
||||
|
||||
var/list/turfs = new/list()
|
||||
for(var/turf/T in orange(accuracy,targetturf))
|
||||
if(istype(T,/turf/space)) continue
|
||||
if(T.density) continue
|
||||
if(T.x>world.maxx-4 || T.x<4) continue //putting them at the edge is dumb
|
||||
if(T.y>world.maxy-4 || T.y<4) continue
|
||||
turfs += T
|
||||
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
|
||||
smoke.set_up(10, 0, holder.owner.loc)
|
||||
smoke.start()
|
||||
var/turf/picked = null
|
||||
if (turfs.len) picked = pick(turfs)
|
||||
if(!isturf(picked))
|
||||
boutput(holder.owner, "<span style=\"color:red\">It's too dangerous to blink there!</span>")
|
||||
return
|
||||
if(picked.loc.name == "Chapel" && get_corruption_percent() < 40)
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell fails due to divine intervention! You should move away from the Chapel.</span>")
|
||||
else
|
||||
animate_blink(holder.owner)
|
||||
holder.owner.set_loc(picked)
|
||||
@@ -0,0 +1,61 @@
|
||||
/datum/targetable/spell/bullcharge
|
||||
name = "Bull's Charge"
|
||||
desc = "Records the casters movement for 4 seconds after which the spell will fire and throw & heavily damage everyone in it's recorded Path."
|
||||
icon_state = "scream" // Vaguely matching placeholder.
|
||||
targeted = 0
|
||||
cooldown = 150
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("RAMI TIN")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/BullChargeLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/list/path = list()
|
||||
var/turf/first = holder.owner.loc
|
||||
var/turf/prev = first
|
||||
for(var/i = 0, i < 40, i++)
|
||||
var/turf/curr = holder.owner.loc
|
||||
animate_bullspellground(curr, "#aaddff")
|
||||
if(prev != curr)
|
||||
path += curr
|
||||
prev = curr
|
||||
sleep(1)
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/bull.ogg", 25, 1, -1)
|
||||
|
||||
var/list/affected = list()
|
||||
var/obj/effects/bullshead/B = new/obj/effects/bullshead(first)
|
||||
for(var/turf/T in path)
|
||||
B.dir = get_dir(B, T)
|
||||
B.loc = T
|
||||
animate_bullspellground(T, "#5599ff")
|
||||
for (var/atom/movable/M in T)
|
||||
if (M.anchored || affected.Find(M) || M == holder.owner)
|
||||
continue
|
||||
affected += M
|
||||
spawn(0) M.throw_at(get_edge_cheap(T, B.dir), 30, 1)
|
||||
if (ismob(M))
|
||||
var/mob/some_idiot = M
|
||||
some_idiot.weakened += 3
|
||||
some_idiot.TakeDamage("chest", 33, 0, 0, DAMAGE_BLUNT)
|
||||
sleep(1)
|
||||
|
||||
qdel(B)
|
||||
|
||||
/obj/effects/bullshead
|
||||
name = "magic"
|
||||
desc = "i aint gotta explain shit"
|
||||
density = 0
|
||||
opacity = 0
|
||||
anchored = 1
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "bull"
|
||||
|
||||
New()
|
||||
src.alpha = 245
|
||||
animate(src, alpha = 1, time = 30)
|
||||
@@ -0,0 +1,58 @@
|
||||
/datum/targetable/spell/clairvoyance
|
||||
name = "Clairvoyance"
|
||||
desc = "Finds the location of a target."
|
||||
icon_state = "clairvoyance"
|
||||
targeted = 0
|
||||
cooldown = 600
|
||||
requires_robes = 1
|
||||
cooldown_staff = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("HAIDAN SEEHQ")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/ClairvoyanceLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/list/mob/targets = list()
|
||||
for (var/mob/living/carbon/human/H in world)
|
||||
targets += H
|
||||
|
||||
if (targets.len > 1)
|
||||
targets = sortNames(targets)
|
||||
|
||||
var/t1 = input(holder.owner, "Select target", "Clairvoyance") as null|anything in targets
|
||||
if (!t1)
|
||||
return 1
|
||||
|
||||
var/mob/M = targets[t1]
|
||||
if (!M || !ismob(M))
|
||||
return 1
|
||||
|
||||
var/atom/target_loc = M.loc
|
||||
if (holder.owner.z == 2)
|
||||
if (M.z == 2)
|
||||
boutput(holder.owner, "<span style=\"color:blue\"><B>[M.real_name]</B> is in [target_loc.loc].</span>")
|
||||
return
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\"><B>[M.real_name]</B> isn't in VR!</span>")
|
||||
return
|
||||
if (M.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has divine protection. Your scrying spell fails!</span>")
|
||||
boutput(M, "<span style=\"color:red\">You sense a Wizard's scrying spell!</span>")
|
||||
else
|
||||
var/spellstring = "<B>[M.real_name]</B> is "
|
||||
if (!istype(target_loc, /turf))
|
||||
if (target_loc.loc.name == "Chapel")
|
||||
spellstring = "<span style=\"color:red\">Your scrying spell fails! It just can't seem to find [M.real_name].</span>"
|
||||
boutput(M, "<span style=\"color:red\">You sense a Wizard's scrying spell!</span>")
|
||||
return
|
||||
if(istype(target_loc, /mob))
|
||||
spellstring += "somehow inside <b>[target_loc.name]</b> in <b>[target_loc.loc.loc]</b>."
|
||||
else if(istype(target_loc, /obj))
|
||||
spellstring += "inside \a <b>[target_loc.name]</b> in <b>[target_loc.loc.loc]</b>."
|
||||
else
|
||||
spellstring += "in [target_loc.loc]."
|
||||
if (M.stat == 2)
|
||||
spellstring += " They also seem to be dead."
|
||||
|
||||
boutput(holder.owner, "<span style=\"color:blue\">[spellstring]</span>")
|
||||
@@ -0,0 +1,102 @@
|
||||
/datum/targetable/spell/cluwne
|
||||
name = "Clown's Revenge"
|
||||
desc = "Turns the target into a fat cursed clown."
|
||||
icon_state = "clownrevenge"
|
||||
targeted = 1
|
||||
max_range = 1
|
||||
cooldown = 1250
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
var/mob/living/carbon/human/H = target
|
||||
if (!istype(H))
|
||||
boutput(holder.owner, "Your target must be human!")
|
||||
return 1
|
||||
holder.owner.say("NWOLC EGNEVER")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/CluwneLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
|
||||
smoke.set_up(5, 0, H.loc)
|
||||
smoke.attach(H)
|
||||
smoke.start()
|
||||
|
||||
if (H.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[H] has divine protection from magic.</span>")
|
||||
H.visible_message("<span style=\"color:red\">The spell has no effect on [H]!</span>")
|
||||
return
|
||||
|
||||
if (iswizard(H) && H.wizard_spellpower())
|
||||
H.visible_message("<span style=\"color:red\">The spell has no effect on [H]!</span>")
|
||||
return
|
||||
|
||||
// NPCs should always be cluwnable, I guess (Convair880)?
|
||||
if (H.mind && (H.mind.assigned_role != "Cluwne") || (!H.mind || !H.client))
|
||||
boutput(H, "<span style=\"color:red\"><B>You HONK painfully!</B></span>")
|
||||
H.take_brain_damage(80)
|
||||
H.stuttering = 120
|
||||
if (H.mind)
|
||||
H.mind.assigned_role = "Cluwne"
|
||||
H.contract_disease(/datum/ailment/disability/clumsy,null,null,1)
|
||||
H.contract_disease(/datum/ailment/disease/cluwneing_around,null,null,1)
|
||||
playsound(get_turf(H), pick("sound/voice/cluwnelaugh1.ogg","sound/voice/cluwnelaugh2.ogg","sound/voice/cluwnelaugh3.ogg"), 100, 0, 0, max(0.7, min(1.4, 1.0 + (30 - H.bioHolder.age)/50)))
|
||||
H.nutrition = 9000
|
||||
H.change_misstep_chance(66)
|
||||
|
||||
animate_clownspell(H)
|
||||
//H.unequip_all()
|
||||
H.drop_from_slot(H.w_uniform)
|
||||
H.drop_from_slot(H.shoes)
|
||||
H.drop_from_slot(H.wear_mask)
|
||||
H.drop_from_slot(H.gloves)
|
||||
H.equip_if_possible(new /obj/item/clothing/under/gimmick/cursedclown(H), H.slot_w_uniform)
|
||||
H.equip_if_possible(new /obj/item/clothing/shoes/cursedclown_shoes(H), H.slot_shoes)
|
||||
H.equip_if_possible(new /obj/item/clothing/mask/cursedclown_hat(H), H.slot_wear_mask)
|
||||
H.equip_if_possible(new /obj/item/clothing/gloves/cursedclown_gloves(H), H.slot_gloves)
|
||||
H.real_name = "cluwne"
|
||||
spawn (25) // Don't remove.
|
||||
if (H) H.assign_gimmick_skull() // The mask IS your new face, my friend (Convair880).
|
||||
else
|
||||
boutput(H, "<span style=\"color:red\"><b>You don't feel very funny.</b></span>")
|
||||
H.take_brain_damage(-120)
|
||||
H.stuttering = 0
|
||||
if (H.mind)
|
||||
H.mind.assigned_role = "Lawyer"
|
||||
H.change_misstep_chance(-INFINITY)
|
||||
H.nutrition = 0
|
||||
|
||||
animate_clownspell(H)
|
||||
for(var/datum/ailment_data/A in H.ailments)
|
||||
if(istype(A.master,/datum/ailment/disability/clumsy))
|
||||
H.cure_disease(A)
|
||||
var/obj/old_uniform = H.w_uniform
|
||||
var/obj/item/the_id = H.wear_id
|
||||
|
||||
if(H.w_uniform && findtext("[H.w_uniform.type]","clown"))
|
||||
H.w_uniform = new /obj/item/clothing/under/suit(H)
|
||||
qdel(old_uniform)
|
||||
|
||||
if(H.shoes && findtext("[H.shoes.type]","clown"))
|
||||
qdel(H.shoes)
|
||||
H.shoes = new /obj/item/clothing/shoes/black(H)
|
||||
|
||||
if(the_id && the_id:registered == H.real_name)
|
||||
if (istype(the_id, /obj/item/card/id))
|
||||
the_id:assignment = "Lawyer"
|
||||
the_id:name = "[H.real_name]'s ID Card (Lawyer)"
|
||||
else if (istype(the_id, /obj/item/device/pda2))
|
||||
the_id:assignment = "Lawyer"
|
||||
the_id:ID_card:assignment = "Lawyer"
|
||||
the_id:ID_card:name = "[H.real_name]'s ID Card (Lawyer)"
|
||||
H.wear_id = the_id
|
||||
|
||||
for(var/obj/item/W in H)
|
||||
if (findtext("[W.type]","clown"))
|
||||
H.u_equip(W)
|
||||
if (W)
|
||||
W.set_loc(target.loc)
|
||||
W.dropped(H)
|
||||
W.layer = initial(W.layer)
|
||||
@@ -0,0 +1,94 @@
|
||||
/datum/targetable/spell/doppelganger
|
||||
name = "Doppelganger"
|
||||
desc = "Creates a clone of you while temporarily making you undetectable. The clone keeps moving in whatever direction you were facing when you cast the spell."
|
||||
icon_state = "doppelganger"
|
||||
targeted = 0
|
||||
cooldown = 300
|
||||
requires_robes = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
var/the_dir = holder.owner.dir
|
||||
var/ground = 0
|
||||
|
||||
if (!isturf(holder.owner.loc))
|
||||
return 1
|
||||
|
||||
ground = holder.owner.lying
|
||||
|
||||
var/obj/overlay/P = new/obj/overlay()
|
||||
P.name = holder.owner.name
|
||||
P.icon = holder.owner.icon
|
||||
P.icon_state = holder.owner.icon_state
|
||||
P.density = 1
|
||||
P.desc = "Wait ... that's not [P.name]!!!"
|
||||
|
||||
var/obj/dummy/spell_doppel/D = new/obj/dummy/spell_doppel()
|
||||
|
||||
for(var/X in holder.owner.overlays)
|
||||
var/image/I = X
|
||||
P.overlays += I
|
||||
|
||||
holder.owner.say("GIN EMUS") // ^-- No speech bubble.
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/DopplegangerLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/turf/curr_turf = get_turf(holder.owner)
|
||||
|
||||
P.dir = the_dir
|
||||
P.set_loc(curr_turf)
|
||||
D.set_loc(curr_turf)
|
||||
holder.owner.set_loc(D)
|
||||
|
||||
if(!ground)
|
||||
spawn(0)
|
||||
while(P)
|
||||
step(P, the_dir)
|
||||
sleep(2)
|
||||
|
||||
spawn(100)
|
||||
holder.owner.set_loc(D.loc)
|
||||
qdel(D)
|
||||
qdel(P)
|
||||
|
||||
/obj/dummy/spell_doppel
|
||||
name = ""
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "nothing"
|
||||
invisibility = 100
|
||||
var/can_move = 1
|
||||
mouse_opacity = 0
|
||||
density = 0
|
||||
anchored = 1
|
||||
|
||||
/obj/dummy/spell_doppel/relaymove(var/mob/user, direction)
|
||||
if (!src.can_move) return
|
||||
|
||||
var/turf/newloc = get_step(src, direction)
|
||||
if (newloc.density) return
|
||||
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
src.y++
|
||||
if(SOUTH)
|
||||
src.y--
|
||||
if(EAST)
|
||||
src.x++
|
||||
if(WEST)
|
||||
src.x--
|
||||
if(NORTHEAST)
|
||||
src.y++
|
||||
src.x++
|
||||
if(NORTHWEST)
|
||||
src.y++
|
||||
src.x--
|
||||
if(SOUTHEAST)
|
||||
src.y--
|
||||
src.x++
|
||||
if(SOUTHWEST)
|
||||
src.y--
|
||||
src.x--
|
||||
|
||||
src.can_move = 0
|
||||
spawn(2) src.can_move = 1
|
||||
@@ -0,0 +1,82 @@
|
||||
/datum/targetable/spell/fireball
|
||||
name = "Fireball"
|
||||
desc = "Launches an explosive fireball at the target."
|
||||
icon_state = "fireball"
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
cooldown = 200
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
sticky = 1
|
||||
|
||||
cast(atom/target)
|
||||
holder.owner.say("MHOL HOTTOV")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/FireballLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/obj/overlay/A = new /obj/overlay(holder.owner.loc)
|
||||
playsound(holder.owner.loc, "sound/effects/mag_fireballlaunch.ogg", 25, 1, -1)
|
||||
A.icon_state = "fireball"
|
||||
A.icon = 'icons/obj/wizard.dmi'
|
||||
A.name = "a fireball"
|
||||
A.anchored = 0
|
||||
A.density = 0
|
||||
A.flags |= TABLEPASS
|
||||
A.fingerprintslast = "[holder.owner.key]"
|
||||
//A.sd_SetLuminosity(4)
|
||||
//A.sd_SetColor(0.95, 0.25, 0)
|
||||
spawn(0)
|
||||
for(var/i = 0, i < 100, i++)
|
||||
step_to(A, target, 0)
|
||||
if (get_dist(A, target) <= 1)
|
||||
var/mob/M = target
|
||||
if (istype(M))
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has divine protection from magic.</span>")
|
||||
M.visible_message("<span style=\"color:red\">The fireball strikes [M] with no effect whatsoever!</span>")
|
||||
playsound(M.loc, "sound/effects/bamf.ogg", 25, 1, -1)
|
||||
qdel(A)
|
||||
return
|
||||
if (iswizard(M) && M.wizard_spellpower())
|
||||
M.visible_message("<span style=\"color:red\">The fireball poofs into harmless smoke as it strikes [M]!</span>")
|
||||
playsound(M.loc, "sound/effects/bamf.ogg", 25, 1, -1)
|
||||
qdel(A)
|
||||
return
|
||||
var/turf/T = get_turf(target)
|
||||
if (holder.owner.wizard_spellpower())
|
||||
if (istype(M))
|
||||
random_brute_damage(M, 25)
|
||||
M.lastattacker = holder.owner
|
||||
M.lastattackertime = world.time
|
||||
M.TakeDamage("chest", 0, 20, 0, DAMAGE_BURN)
|
||||
for(var/mob/living/L in range(2, T))
|
||||
spawn(0)
|
||||
L.weakened += 5
|
||||
step(L,get_dir(T, L))
|
||||
spawn(5)
|
||||
if (target)
|
||||
step(L,get_dir(T, L))
|
||||
spawn(10)
|
||||
if (target)
|
||||
step(L,get_dir(T, L))
|
||||
if (target)
|
||||
explosion(A, T, -1, -1, 2, 2)
|
||||
fireflash(T, 1)
|
||||
qdel(A)
|
||||
return
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
if (istype(M))
|
||||
M.weakened += 5
|
||||
M.lastattacker = holder.owner
|
||||
M.lastattackertime = world.time
|
||||
random_brute_damage(M, 10)
|
||||
M.TakeDamage("chest", 0, 10, 0, DAMAGE_BURN)
|
||||
else
|
||||
explosion(A, T, -1, -1, 1, 1)
|
||||
fireflash(T,0)
|
||||
playsound(T, "sound/effects/bamf.ogg", 25, 1, -1)
|
||||
target.visible_message("<span style=\"color:red\">[target] is struck by the fireball!</span>")
|
||||
qdel(A)
|
||||
sleep(2)
|
||||
qdel(A)
|
||||
@@ -0,0 +1,55 @@
|
||||
/datum/targetable/spell/forcewall
|
||||
name = "Forcewall"
|
||||
desc = "Create a forcewall which extends out to your sides."
|
||||
icon_state = "forcewall"
|
||||
targeted = 0
|
||||
cooldown = 100
|
||||
requires_robes = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("BRIXHUN MOHTYR")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/ForcewallLoud.ogg", 50, 0, -1)
|
||||
if(!holder.owner.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/mag_forcewall.ogg", 25, 1, -1)
|
||||
var/forcefield1
|
||||
var/forcefield2
|
||||
var/forcefield3
|
||||
var/forcefield4
|
||||
var/forcefield5
|
||||
|
||||
if (holder.owner.dir == NORTH || holder.owner.dir == SOUTH)
|
||||
forcefield1 = new /obj/forcefield(locate(holder.owner.x, holder.owner.y, holder.owner.z))
|
||||
forcefield2 = new /obj/forcefield(locate(holder.owner.x + 1, holder.owner.y, holder.owner.z))
|
||||
forcefield3 = new /obj/forcefield(locate(holder.owner.x - 1, holder.owner.y, holder.owner.z))
|
||||
if (holder.owner.wizard_spellpower()) forcefield4 = new /obj/forcefield(locate(holder.owner.x + 2, holder.owner.y, holder.owner.z))
|
||||
if (holder.owner.wizard_spellpower()) forcefield5 = new /obj/forcefield(locate(holder.owner.x - 2, holder.owner.y, holder.owner.z))
|
||||
else
|
||||
forcefield1 = new /obj/forcefield(locate(holder.owner.x, holder.owner.y, holder.owner.z))
|
||||
forcefield2 = new /obj/forcefield(locate(holder.owner.x, holder.owner.y + 1, holder.owner.z))
|
||||
forcefield3 = new /obj/forcefield(locate(holder.owner.x, holder.owner.y - 1, holder.owner.z))
|
||||
if (holder.owner.wizard_spellpower()) forcefield4 = new /obj/forcefield(locate(holder.owner.x,holder.owner.y + 2,holder.owner.z))
|
||||
if (holder.owner.wizard_spellpower()) forcefield5 = new /obj/forcefield(locate(holder.owner.x,holder.owner.y - 2,holder.owner.z))
|
||||
|
||||
spawn(300)
|
||||
qdel(forcefield1)
|
||||
qdel(forcefield2)
|
||||
qdel(forcefield3)
|
||||
if (forcefield4)
|
||||
qdel(forcefield4)
|
||||
if (forcefield5)
|
||||
qdel(forcefield5)
|
||||
|
||||
/obj/forcefield
|
||||
desc = "A space wizard's magic wall."
|
||||
name = "Forcewall"
|
||||
desc = "An impenetrable magic barrier. Its only flaw is that it cannot last long."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "forcewall"
|
||||
anchored = 1.0
|
||||
opacity = 0
|
||||
density = 1
|
||||
luminosity = 3
|
||||
@@ -0,0 +1,62 @@
|
||||
/datum/targetable/spell/golem
|
||||
name = "Summon Golem"
|
||||
desc = "Summons a Golem made of the reagent you currently hold."
|
||||
icon_state = "golem"
|
||||
targeted = 0
|
||||
cooldown = 500
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
cooldown_staff = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
var/obj/item/AnItem = null //temp item holder for processing
|
||||
var/datum/reagents/TheReagents = null //reagent holder
|
||||
|
||||
//get reagent container if there is one, and check to see it has some reagents
|
||||
if (holder.owner.r_hand != null)
|
||||
AnItem = holder.owner.r_hand
|
||||
if(istype(AnItem, /obj/item/reagent_containers/))
|
||||
if(AnItem.reagents.total_volume)
|
||||
TheReagents = AnItem.reagents
|
||||
else
|
||||
AnItem = null
|
||||
|
||||
|
||||
|
||||
if (holder.owner.l_hand != null && !AnItem)
|
||||
AnItem = holder.owner.l_hand
|
||||
if(istype(AnItem, /obj/item/reagent_containers/))
|
||||
if(AnItem.reagents.total_volume)
|
||||
TheReagents = AnItem.reagents
|
||||
else
|
||||
AnItem = null
|
||||
|
||||
|
||||
if(!AnItem)
|
||||
boutput(holder.owner, "<span style=\"color:red\">You must be holding a container in your hand.</span>")
|
||||
return 1 // No cooldown when it fails.
|
||||
|
||||
if(!TheReagents)
|
||||
boutput(holder.owner, "<span style=\"color:red\">You have no material to convert into a golem.</span>")
|
||||
return 1
|
||||
|
||||
|
||||
holder.owner.say("CLAE MASHON")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/GolemLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/obj/critter/golem/TheGolem
|
||||
if (istype(AnItem, /obj/item/reagent_containers/food/snacks/ingredient/egg/bee))
|
||||
TheGolem = new /obj/critter/domestic_bee(get_turf(holder.owner))
|
||||
TheGolem.name = "Bee Golem"
|
||||
TheGolem.desc = "A greater domestic space bee that has been created with magic, but is otherwise completely identical to any other member of its species."
|
||||
else
|
||||
TheGolem = new /obj/critter/golem(get_turf(holder.owner))
|
||||
TheGolem.CustomizeGolem(TheReagents)
|
||||
|
||||
qdel(TheReagents)
|
||||
qdel(AnItem)
|
||||
boutput(holder.owner, "<span style=\"color:blue\">You conjure up [TheGolem]!</span>")
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] conjures up [TheGolem]!</span>")
|
||||
playsound(holder.owner.loc, "sound/effects/mag_golem.ogg", 25, 1, -1)
|
||||
@@ -0,0 +1,168 @@
|
||||
/datum/targetable/spell/iceburst
|
||||
name = "Ice Burst"
|
||||
desc = "Launches freezing bolts at nearby foes."
|
||||
icon_state = "iceburst"
|
||||
targeted = 0
|
||||
cooldown = 200
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
var/count = 0
|
||||
var/count2 = 0
|
||||
var/moblimit = 3
|
||||
|
||||
for(var/mob/living/M as mob in oview())
|
||||
if(M.stat == 2) continue
|
||||
count2++
|
||||
if(!count2)
|
||||
boutput(holder.owner, "Noone is in range!")
|
||||
return 1
|
||||
|
||||
holder.owner.say("NYTH ERRIN")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/IceburstLoud.ogg", 50, 0, -1)
|
||||
if(!holder.owner.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
|
||||
for (var/mob/living/M as mob in oview())
|
||||
if(M.stat == 2) continue
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has divine protection! The spell refuses to target \him!</span>")
|
||||
continue
|
||||
if (iswizard(M) && M.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has arcane protection! The spell refuses to target \him!</span>")
|
||||
continue
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/mag_iceburstlaunch.ogg", 25, 1, -1)
|
||||
if ((!holder.owner.wizard_spellpower() && count >= 1) || (count >= moblimit)) break
|
||||
count++
|
||||
spawn(0)
|
||||
var/obj/overlay/A = new /obj/overlay( holder.owner.loc )
|
||||
A.icon_state = "icem"
|
||||
A.icon = 'icons/obj/wizard.dmi'
|
||||
A.name = "ice bolt"
|
||||
A.anchored = 0
|
||||
A.density = 0
|
||||
A.layer = MOB_EFFECT_LAYER
|
||||
//A.sd_SetLuminosity(3)
|
||||
//A.sd_SetColor(0, 0.1, 0.8)
|
||||
var/i
|
||||
for(i=0, i<20, i++)
|
||||
if (holder.owner.wizard_spellpower())
|
||||
if (!locate(/obj/decal/icefloor) in A.loc)
|
||||
var/obj/decal/icefloor/B = new /obj/decal/icefloor(A.loc)
|
||||
//B.sd_SetLuminosity(1)
|
||||
//B.sd_SetColor(0, 0.1, 0.8)
|
||||
spawn(200)
|
||||
qdel (B)
|
||||
step_to(A,M,0)
|
||||
if (get_dist(A,M) == 0)
|
||||
boutput(M, text("<span style=\"color:blue\">You are chilled by a burst of magical ice!</span>"))
|
||||
M.visible_message("<span style=\"color:red\">[M] is struck by magical ice!</span>")
|
||||
playsound(holder.owner.loc, "sound/effects/mag_iceburstimpact.ogg", 25, 1, -1)
|
||||
M.bodytemperature = 0
|
||||
M.lastattacker = holder.owner
|
||||
M.lastattackertime = world.time
|
||||
qdel(A)
|
||||
if(prob(40))
|
||||
M.visible_message("<span style=\"color:red\">[M] is frozen solid!</span>")
|
||||
new /obj/icecube(M.loc, M)
|
||||
return
|
||||
sleep(5)
|
||||
qdel(A)
|
||||
|
||||
// /obj/decal/icefloor moved to decal.dm
|
||||
|
||||
/obj/icecube
|
||||
name = "ice cube"
|
||||
desc = "That is a surprisingly large ice cube."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "icecube"
|
||||
density = 1
|
||||
layer = EFFECTS_LAYER_BASE
|
||||
var/health = 10
|
||||
var/steam_on_death = 1
|
||||
|
||||
New(loc, mob/iced as mob)
|
||||
..()
|
||||
if(iced && !isAI(iced) && !istype(iced, /mob/living/intangible/blob_overmind))
|
||||
if(istype(iced.loc, /obj/icecube)) //Already in a cube?
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
iced.set_loc(src)
|
||||
|
||||
src.underlays += iced
|
||||
boutput(iced, "<span style=\"color:red\">You are trapped within [src]!</span>") // since this is used in at least two places to trap people in things other than ice cubes
|
||||
src.health *= (rand(10,20)/10)
|
||||
return
|
||||
|
||||
relaymove(mob/user as mob)
|
||||
if (user.stat)
|
||||
return
|
||||
|
||||
if(prob(25))
|
||||
src.health--
|
||||
if(src.health <= 0)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
attack_hand(mob/user as mob)
|
||||
user.visible_message("<span class='combat'><b>[user]</b> kicks [src]!</span>", "<span style=\"color:blue\">You kick [src].</span>")
|
||||
|
||||
src.health -= 2
|
||||
if(src.health <= 0)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
bullet_act(var/obj/projectile/P)
|
||||
var/damage = 0
|
||||
damage = round(((P.power/2)*P.proj_data.ks_ratio), 1.0)
|
||||
if (damage < 1)
|
||||
return
|
||||
|
||||
if(src.material) src.material.triggerOnAttacked(src, P.shooter, src, (ismob(P.shooter) ? P.shooter:equipped() : P.shooter) )
|
||||
for(var/atom/A in src)
|
||||
if(A.material)
|
||||
A.material.triggerOnAttacked(A, P.shooter, src, (ismob(P.shooter) ? P.shooter:equipped() : P.shooter))
|
||||
|
||||
switch(P.proj_data.damage_type)
|
||||
if(D_KINETIC)
|
||||
src.health -= (damage*2)
|
||||
if(D_PIERCING)
|
||||
src.health -= (damage/2)
|
||||
if(D_ENERGY)
|
||||
src.health -= (damage/4)
|
||||
|
||||
if(src.health <= 0)
|
||||
qdel(src)
|
||||
|
||||
return
|
||||
|
||||
attackby(obj/item/W as obj, mob/user as mob)
|
||||
src.health -= W.force
|
||||
if(src.health <= 0)
|
||||
qdel(src)
|
||||
return
|
||||
..()
|
||||
return
|
||||
|
||||
disposing()
|
||||
for(var/atom/movable/AM in src)
|
||||
if(ismob(AM))
|
||||
var/mob/M = AM
|
||||
M.visible_message("<span style=\"color:red\"><b>[M]</b> breaks out of [src]!</span>","<span style=\"color:red\">You break out of [src]!</span>")
|
||||
AM.set_loc(src.loc)
|
||||
|
||||
if (steam_on_death)
|
||||
if (!(locate(/datum/effects/system/steam_spread) in src.loc))
|
||||
var/datum/effects/system/steam_spread/steam = unpool(/datum/effects/system/steam_spread)
|
||||
steam.set_up(10, 0, get_turf(src))
|
||||
steam.attach(src)
|
||||
steam.start()
|
||||
|
||||
..()
|
||||
return
|
||||
@@ -0,0 +1,49 @@
|
||||
/datum/targetable/spell/kill
|
||||
name = "Shocking Grasp"
|
||||
desc = "Kills the victim with electrical power. Takes a few seconds to cast."
|
||||
icon_state = "grasp"
|
||||
targeted = 1
|
||||
max_range = 1
|
||||
cooldown = 600
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.visible_message("<span style=\"color:red\"><b>[holder.owner] begins to cast a spell on [target]!</b></span>")
|
||||
playsound(holder.owner.loc, "sound/effects/elec_bzzz.ogg", 25, 1, -1)
|
||||
if (do_mob(holder.owner, target, 20))
|
||||
holder.owner.say("EI NATH")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/ShockingGraspLoud.ogg", 50, 0, -1)
|
||||
playsound(holder.owner.loc, "sound/effects/elec_bigzap.ogg", 25, 1, -1)
|
||||
|
||||
if (ishuman(target))
|
||||
if (target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[target] has divine protection from magic.</span>")
|
||||
target.visible_message("<span style=\"color:red\">The electric charge courses through [target] harmlessly!</span>")
|
||||
return
|
||||
else if (iswizard(target) && target.wizard_spellpower())
|
||||
target.visible_message("<span style=\"color:red\">The electric charge somehow completely misses [target]!</span>")
|
||||
return
|
||||
|
||||
if (holder.owner.wizard_spellpower())
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
target.elecgib()
|
||||
else
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
target.visible_message("<span style=\"color:red\">[target] is severely burned by an electrical charge!</span>")
|
||||
target.lastattacker = holder.owner
|
||||
target.lastattackertime = world.time
|
||||
target.TakeDamage("chest", 0, 80, 0, DAMAGE_BURN)
|
||||
target.stunned += 10
|
||||
target.weakened += 10
|
||||
target.stuttering += 15
|
||||
else
|
||||
return 1 // no cooldown if it fails
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/targetable/spell/knock
|
||||
name = "Knock"
|
||||
desc = "Opens nearby doors."
|
||||
icon_state = "knock"
|
||||
targeted = 0
|
||||
cooldown = 100
|
||||
requires_robes = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("AULIE OXIN FIERA")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/KnockLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/SPrange = 1
|
||||
if (holder.owner.wizard_spellpower())
|
||||
SPrange = 5
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell only works at point blank without a staff to focus it!</span>")
|
||||
for(var/obj/machinery/door/G in oview(SPrange, holder.owner))
|
||||
spawn(1)
|
||||
G.open()
|
||||
for(var/obj/storage/F in oview(SPrange, holder.owner))
|
||||
if (F.locked)
|
||||
F.locked = 0
|
||||
spawn(1)
|
||||
F.open()
|
||||
for(var/mob/living/silicon/robot/E in oview(SPrange, holder.owner))
|
||||
spawn(1)
|
||||
E.spellopen()
|
||||
for(var/obj/machinery/bot/B in oview(SPrange, holder.owner))
|
||||
B.locked = 0
|
||||
B.req_access = null
|
||||
@@ -0,0 +1,76 @@
|
||||
/datum/targetable/spell/magicmissile
|
||||
name = "Magic Missile"
|
||||
desc = "Attacks nearby foes with stunning projectiles."
|
||||
icon_state = "missile"
|
||||
targeted = 0
|
||||
cooldown = 200
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
var/mob_count = 0, mob_count2 = 0
|
||||
var/mob_limit = 6
|
||||
|
||||
for(var/mob/living/M as mob in oview())
|
||||
if(M.stat == 2) continue
|
||||
mob_count++
|
||||
if(!mob_count)
|
||||
boutput(holder.owner, "Noone is in range!")
|
||||
return 1 // cast failed
|
||||
|
||||
holder.owner.say("ICEE BEEYEM")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/MagicMissileLoud.ogg", 50, 0, -1)
|
||||
if(!holder.owner.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
|
||||
for (var/mob/living/M as mob in oview())
|
||||
if (M.stat == 2) continue
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has divine protection! The spell refuses to target \him!</span>")
|
||||
continue
|
||||
if (iswizard(M) && M.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">[M] has arcane protection! The spell refuses to target \him!</span>")
|
||||
continue
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/mag_magmislaunch.ogg", 25, 1, -1)
|
||||
if ((!holder.owner.wizard_spellpower() && mob_count2 >= 1) || (mob_count2 >= mob_limit)) break
|
||||
mob_count2++
|
||||
spawn(0)
|
||||
var/obj/overlay/A = new /obj/overlay(holder.owner.loc)
|
||||
A.icon_state = "magicm"
|
||||
A.icon = 'icons/obj/wizard.dmi'
|
||||
A.name = "a magic missile"
|
||||
A.anchored = 0
|
||||
A.density = 0
|
||||
A.layer = EFFECTS_LAYER_1
|
||||
A.flags |= TABLEPASS
|
||||
//A.sd_SetLuminosity(3)
|
||||
//A.sd_SetColor(0.7, 0, 0.7)
|
||||
var/i
|
||||
for(i=0, i<20, i++)
|
||||
var/obj/overlay/B = new /obj/overlay(A.loc)
|
||||
B.icon_state = "magicmd"
|
||||
B.icon = 'icons/obj/wizard.dmi'
|
||||
B.name = "trail"
|
||||
B.anchored = 1
|
||||
B.density = 0
|
||||
B.layer = EFFECTS_LAYER_BASE
|
||||
spawn(5)
|
||||
qdel(B)
|
||||
step_to(A,M,0)
|
||||
if (get_dist(A,M) == 0)
|
||||
M.weakened += (5 - (min(mob_count2,4)))
|
||||
boutput(M, text("<span style=\"color:blue\">The magic missile SLAMS into you!</span>"))
|
||||
M.visible_message("<span style=\"color:red\">[M] is struck by a magic missile!</span>")
|
||||
playsound(M.loc, "sound/effects/mag_magmisimpact.ogg", 25, 1, -1)
|
||||
M.TakeDamage("chest", 0, 10, 0, DAMAGE_BURN)
|
||||
random_brute_damage(M, 5)
|
||||
M.lastattacker = holder.owner
|
||||
M.lastattackertime = world.time
|
||||
qdel(A)
|
||||
return
|
||||
sleep(6)
|
||||
qdel(A)
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/targetable/spell/magshield
|
||||
name = "Spell Shield"
|
||||
desc = "Temporarily shield yourself from melee attacks and projectiles. It also absorbs some of the blast of explosions."
|
||||
icon_state = "spellshield"
|
||||
targeted = 0
|
||||
cooldown = 300
|
||||
requires_robes = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
if(holder.owner.spellshield)
|
||||
boutput(holder.owner, "<span style=\"color:red\">You already have a Spell Shield active!</span>")
|
||||
return
|
||||
|
||||
holder.owner.say("XYZZYX")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/MagicshieldLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/image/shield_overlay = null
|
||||
|
||||
holder.owner.spellshield = 1
|
||||
shield_overlay = image('icons/effects/effects.dmi', holder.owner, "enshield", MOB_LAYER+1)
|
||||
holder.owner.underlays += shield_overlay
|
||||
boutput(holder.owner, "<span style=\"color:blue\"><b>You are surrounded by a magical barrier!</b></span>")
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] is encased in a protective shield.</span>")
|
||||
playsound(holder.owner,"sound/effects/MagShieldUp.ogg",50,1)
|
||||
spawn(100)
|
||||
if(holder.owner && holder.owner.spellshield)
|
||||
holder.owner.spellshield = 0
|
||||
holder.owner.underlays -= shield_overlay
|
||||
shield_overlay = null
|
||||
boutput(holder.owner, "<span style=\"color:blue\"><b>Your magical barrier fades away!</b></span>")
|
||||
holder.owner.visible_message("<span style=\"color:red\">The shield protecting [holder.owner] fades away.</span>")
|
||||
playsound(usr,"sound/effects/MagShieldDown.ogg", 50, 1)
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/targetable/spell/mutate
|
||||
name = "Empower"
|
||||
desc = "Temporarily superpowers your body and mind."
|
||||
icon_state = "mutate"
|
||||
targeted = 0
|
||||
cooldown = 400
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("BIRUZ BENNAR")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/MutateLoud.ogg", 50, 0, -1)
|
||||
boutput(holder.owner, "<span style=\"color:blue\">Your mind and muscles are magically empowered!</span>")
|
||||
holder.owner.visible_message("<span style=\"color:red\">[holder.owner] glows with a POWERFUL aura!</span>")
|
||||
|
||||
if (!holder.owner.bioHolder.HasEffect("hulk"))
|
||||
holder.owner.bioHolder.AddEffect("hulk")
|
||||
if (!holder.owner.bioHolder.HasEffect("telekinesis") && holder.owner.wizard_spellpower())
|
||||
holder.owner.bioHolder.AddEffect("telekinesis")
|
||||
var/SPtime = 150
|
||||
if (holder.owner.wizard_spellpower())
|
||||
SPtime = 300
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell doesn't last as long without a staff to focus it!</span>")
|
||||
spawn (SPtime)
|
||||
if (holder.owner.bioHolder.HasEffect("telekinesis"))
|
||||
holder.owner.bioHolder.RemoveEffect("telekinesis")
|
||||
if (holder.owner.bioHolder.HasEffect("hulk"))
|
||||
holder.owner.bioHolder.RemoveEffect("hulk")
|
||||
@@ -0,0 +1,161 @@
|
||||
/datum/targetable/spell/pandemonium
|
||||
name = "Pandemonium"
|
||||
desc = "Calls upon spirits of chaos to summon unpredictable effects."
|
||||
icon_state = "pandemonium"
|
||||
targeted = 0
|
||||
cooldown = 400
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("WATT LEHFUQUE")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/PandemoniumLoud.ogg", 50, 0, -1)
|
||||
|
||||
var/list/available_effects = list("babel", "boost", "roar", "signaljam", "grilles", "meteors")
|
||||
|
||||
var/protectuser = 1
|
||||
if (!holder.owner.wizard_spellpower())
|
||||
boutput(holder.owner, "<span style=\"color:red\">Without your staff to focus your spell, it may backfire!</span>")
|
||||
protectuser = 0
|
||||
|
||||
var/people_in_range = 0
|
||||
for (var/mob/living/carbon/M in range(7, holder.owner))
|
||||
if (M == holder.owner) continue
|
||||
people_in_range++
|
||||
|
||||
if (people_in_range)
|
||||
available_effects += "fireburst"
|
||||
available_effects += "tripballs"
|
||||
available_effects += "flashbang"
|
||||
available_effects += "screech"
|
||||
|
||||
var/string_of_effects = " "
|
||||
for (var/X in available_effects)
|
||||
string_of_effects += "[X] "
|
||||
|
||||
var/mob/living/carbon/human/W = holder.owner
|
||||
|
||||
switch(pick(available_effects))
|
||||
if("fireburst") W.PAND_Fireburst(protectuser)
|
||||
if("babel") W.PAND_Babel(protectuser)
|
||||
if("tripballs") W.PAND_Tripballs(protectuser)
|
||||
if("flashbang") W.PAND_Flashbang(protectuser)
|
||||
if("meteors") W.PAND_Meteors(protectuser)
|
||||
if("screech") W.PAND_Screech(protectuser)
|
||||
if("boost") W.PAND_Boost(protectuser)
|
||||
if("roar") W.PAND_Roar(protectuser)
|
||||
if("signaljam") W.PAND_Signaljam(protectuser)
|
||||
if("grilles") W.PAND_Grilles(protectuser)
|
||||
|
||||
// holy shit someone clean this up and just move it into the main spell proc, this is ridiclous
|
||||
/mob/living/carbon/human/proc/PAND_Fireburst(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> radiates a wave of burning heat!</span>", src), 1)
|
||||
playsound(src, "sound/effects/bamf.ogg", 80, 1)
|
||||
for (var/mob/living/carbon/human/M in range(6, src))
|
||||
if (M == src && protectuser) continue
|
||||
if (iswizard(M) && M.wizard_spellpower()) continue
|
||||
boutput(M, "<span style=\"color:red\">You suddenly burst into flames!</span>")
|
||||
M.update_burning(30)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Babel(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> emits a faint smell of cheese!</span>", src), 1)
|
||||
playsound(src, "sound/misc/superfart.ogg", 80, 1)
|
||||
for (var/mob/living/carbon/human/M in mobs)
|
||||
if (M == src && protectuser) continue
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain")) continue
|
||||
if (iswizard(M) && M.wizard_spellpower()) continue
|
||||
M.bioHolder.AddEffect("accent_swedish", timeleft = 15)
|
||||
M.bioHolder.AddEffect("accent_comic", timeleft = 15)
|
||||
M.bioHolder.AddEffect("accent_elvis", timeleft = 15)
|
||||
M.bioHolder.AddEffect("accent_chav", timeleft = 15)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Tripballs(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> radiates a confusing aura!</span>", src), 1)
|
||||
playsound(src, "sound/effects/bionic_sound.ogg", 80, 1)
|
||||
for (var/mob/living/carbon/human/M in range(25, src))
|
||||
if (M == src && protectuser) continue
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain")) continue
|
||||
if (iswizard(M) && M.wizard_spellpower()) continue
|
||||
boutput(M, "<span style=\"color:red\">You feel extremely strange!</span>")
|
||||
M.reagents.add_reagent("LSD", 20)
|
||||
M.reagents.add_reagent("THC", 20)
|
||||
M.reagents.add_reagent("psilocybin", 20)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Flashbang(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> explodes into a brilliant flash of light!</span>", src), 1)
|
||||
playsound(src.loc, "sound/weapons/flashbang.ogg", 50, 1)
|
||||
for(var/mob/N in AIviewers(src, null))
|
||||
if(get_dist(N, src) <= 6)
|
||||
if(N != src)
|
||||
if (ishuman(N))
|
||||
if (N.bioHolder && N.bioHolder.HasEffect("training_chaplain"))
|
||||
continue
|
||||
if (iswizard(N) && N.wizard_spellpower())
|
||||
continue
|
||||
N.apply_flash(30, 5)
|
||||
if(N.client) shake_camera(N, 6, 4)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Meteors(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> summons meteors!</span>", src), 1)
|
||||
for(var/turf/T in orange(1, src))
|
||||
if(!T.density)
|
||||
var/target_dir = get_dir(src.loc, T)
|
||||
var/turf/U = get_edge_target_turf(src, target_dir)
|
||||
new /obj/newmeteor/small(my_spawn = T, trg = U)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Screech(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> emits a horrible shriek!</span>", src), 1)
|
||||
playsound(src.loc, "sound/effects/screech.ogg", 25, 1, -1)
|
||||
|
||||
for (var/mob/living/H in hearers(src, null))
|
||||
if (H == src && protectuser)
|
||||
continue
|
||||
if (ishuman(H) && H.bioHolder && H.bioHolder.HasEffect("training_chaplain"))
|
||||
H.show_text("You are immune to [src]'s screech!", "blue")
|
||||
continue
|
||||
if (iswizard(H) && H.wizard_spellpower())
|
||||
continue
|
||||
if (isvampire(H) && H.check_vampire_power(3) == 1)
|
||||
H.show_text("You are immune to [src]'s screech!", "blue")
|
||||
continue
|
||||
|
||||
H.apply_sonic_stun(0, 3, 0, 0, 0, 8)
|
||||
|
||||
sonic_attack_environmental_effect(src, 7, list("light", "window", "r_window"))
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Boost(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> glows with magical power!</span>", src), 1)
|
||||
playsound(src.loc, "sound/mksounds/boost.ogg", 25, 1, -1)
|
||||
src.bioHolder.AddEffect("arcane_power", timeleft = 60)
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Roar(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> emits a horrific reverberating roar!</span>", src), 1)
|
||||
world << sound('sound/effects/mag_pandroar.ogg')
|
||||
for (var/mob/living/carbon/human/M in mobs)
|
||||
if (M == src && protectuser) continue
|
||||
if (ishuman(M))
|
||||
if (M.bioHolder.HasEffect("training_chaplain")) continue
|
||||
if (iswizard(M) && M.wizard_spellpower()) continue
|
||||
boutput(M, "<span style=\"color:red\">A horrifying noise stuns you in sheer terror!</span>")
|
||||
M.stunned += 3
|
||||
M.stuttering += 10
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Signaljam(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> emits a wave of electrical interference!</span>", src), 1)
|
||||
playsound(src.loc, "sound/effects/mag_warp.ogg", 25, 1, -1)
|
||||
for (var/mob/living/carbon/human/M in mobs)
|
||||
if (M.ears) boutput(M, "<span style=\"color:red\">Your headset speaker suddenly bursts into weird static!</span>")
|
||||
solar_flare = 1
|
||||
sleep(100)
|
||||
solar_flare = 0
|
||||
|
||||
/mob/living/carbon/human/proc/PAND_Grilles(var/protectuser = 1)
|
||||
for(var/mob/O in AIviewers(src, null)) O.show_message(text("<span style=\"color:red\"><B>[]</B> reshapes the metal around \him!</span>", src), 1)
|
||||
playsound(src.loc, "sound/effects/grillehit.ogg", 25, 1, -1)
|
||||
for(var/turf/simulated/floor/T in view(src,7))
|
||||
if (prob(33)) new /obj/grille/steel(T)
|
||||
@@ -0,0 +1,131 @@
|
||||
/datum/targetable/spell/phaseshift
|
||||
name = "Phase Shift"
|
||||
desc = "Become incorporeal and move through walls."
|
||||
icon_state = "phaseshift"
|
||||
targeted = 0
|
||||
cooldown = 300
|
||||
requires_robes = 1
|
||||
cooldown_staff = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
if (spell_invisibility(holder.owner, 1, 0, 1, 1) != 1) // Dry run. Can we phaseshift?
|
||||
return 1
|
||||
|
||||
holder.owner.say("PHEE CABUE")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/MistFormLoud.ogg", 50, 0, -1)
|
||||
var/SPtime = 35
|
||||
if(holder.owner.wizard_spellpower())
|
||||
SPtime = 50
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell doesn't last as long without a staff to focus it!</span>")
|
||||
playsound(holder.owner.loc, "sound/effects/mag_phase.ogg", 25, 1, -1)
|
||||
spell_invisibility(holder.owner, SPtime, 0, 1)
|
||||
|
||||
// Merged some stuff from wizard and vampire phaseshift for easy of use (Convair880).
|
||||
/proc/spell_invisibility(var/mob/H, var/time, var/check_for_watchers = 0, var/stop_burning = 0, var/dry_run_only = 0)
|
||||
if (!H || !ismob(H))
|
||||
return
|
||||
if (!isturf(H.loc))
|
||||
H.show_text("You can't seem to turn incorporeal here.", "red")
|
||||
return
|
||||
if (H.stat || H.paralysis > 0)
|
||||
H.show_text("You can't turn incorporeal when you are incapacitated.", "red")
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(H)
|
||||
if (T && isrestrictedz(T.z))
|
||||
H.show_text("You can't seem to turn incorporeal here.", "red")
|
||||
return
|
||||
|
||||
if (check_for_watchers == 1)
|
||||
if (H.client)
|
||||
for (var/mob/living/L in view(H.client.view, H))
|
||||
if (L.stat == 0 && L.sight_check(1) && L.ckey != H.ckey)
|
||||
H.show_text("You can only use that when nobody can see you!", "red")
|
||||
return
|
||||
|
||||
if (dry_run_only)
|
||||
return 1 // Return 1 if we got this far in the test run.
|
||||
|
||||
if (stop_burning == 1)
|
||||
var/mob/living/carbon/human/HH = H
|
||||
if (istype(HH) && HH.burning)
|
||||
boutput(HH, "<span style=\"color:blue\">The flames sputter out as you phase shift.</span>")
|
||||
HH.set_burning(0)
|
||||
|
||||
spawn(0)
|
||||
var/mobloc = get_turf(H.loc)
|
||||
var/obj/dummy/spell_invis/holder = new /obj/dummy/spell_invis( mobloc )
|
||||
var/atom/movable/overlay/animation = new /atom/movable/overlay( mobloc )
|
||||
animation.name = "water"
|
||||
animation.density = 0
|
||||
animation.anchored = 1
|
||||
animation.icon = 'icons/mob/mob.dmi'
|
||||
animation.icon_state = "liquify"
|
||||
animation.layer = EFFECTS_LAYER_BASE
|
||||
animation.master = holder
|
||||
flick("liquify",animation)
|
||||
H.set_loc(holder)
|
||||
var/datum/effects/system/steam_spread/steam = unpool(/datum/effects/system/steam_spread)
|
||||
steam.set_up(10, 0, mobloc)
|
||||
steam.start()
|
||||
sleep(time)
|
||||
mobloc = get_turf(H.loc)
|
||||
animation.set_loc(mobloc)
|
||||
steam.location = mobloc
|
||||
steam.start()
|
||||
H.canmove = 0
|
||||
sleep(20)
|
||||
flick("reappear",animation)
|
||||
sleep(5)
|
||||
H.set_loc(mobloc)
|
||||
H.canmove = 1
|
||||
qdel(animation)
|
||||
for (var/obj/junk_to_dump in holder.contents)
|
||||
junk_to_dump.set_loc(mobloc)
|
||||
|
||||
qdel(holder)
|
||||
|
||||
/obj/dummy/spell_invis
|
||||
name = "water"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "nothing"
|
||||
invisibility = 101
|
||||
var/canmove = 1
|
||||
density = 0
|
||||
anchored = 1
|
||||
|
||||
/obj/dummy/spell_invis/relaymove(var/mob/user, direction)
|
||||
if (!src.canmove) return
|
||||
switch(direction)
|
||||
if(NORTH)
|
||||
src.y++
|
||||
if(SOUTH)
|
||||
src.y--
|
||||
if(EAST)
|
||||
src.x++
|
||||
if(WEST)
|
||||
src.x--
|
||||
if(NORTHEAST)
|
||||
src.y++
|
||||
src.x++
|
||||
if(NORTHWEST)
|
||||
src.y++
|
||||
src.x--
|
||||
if(SOUTHEAST)
|
||||
src.y--
|
||||
src.x++
|
||||
if(SOUTHWEST)
|
||||
src.y--
|
||||
src.x--
|
||||
src.canmove = 0
|
||||
spawn(2) src.canmove = 1
|
||||
|
||||
/obj/dummy/spell_invis/ex_act(blah)
|
||||
return
|
||||
|
||||
/obj/dummy/spell_invis/bullet_act(blah,blah)
|
||||
return
|
||||
@@ -0,0 +1,32 @@
|
||||
/datum/targetable/spell/rathens
|
||||
name = "Rathen's Secret"
|
||||
desc = "Summons a powerful shockwave around you that tears the arses and limbs off of enemies."
|
||||
icon_state = "arsenath"
|
||||
targeted = 0
|
||||
cooldown = 500
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("ARSE NATH!")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/RathensSecretLoud.ogg", 50, 0, -1)
|
||||
|
||||
playsound(holder.owner, "sound/misc/superfart.ogg", 25, 1)
|
||||
|
||||
for (var/mob/*living/carbon/human*//H in oview(holder.owner))
|
||||
if (H.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(usr, "<span style=\"color:red\">[H]'s butt has divine protection from magic.</span>")
|
||||
H.visible_message("<span style=\"color:red\">The spell fails to work on [H]!</span>")
|
||||
continue
|
||||
if (iswizard(H) && H.wizard_spellpower())
|
||||
H.visible_message("<span style=\"color:red\">[H] magically farts the spell away!</span>")
|
||||
playsound(H, "sound/vox/Poo.ogg", 25, 1)
|
||||
continue
|
||||
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
|
||||
smoke.set_up(5, 0, H:loc)
|
||||
smoke.attach(H)
|
||||
smoke.start()
|
||||
ass_explosion(H, 1, 7)
|
||||
// See bigfart.dm for the ass_explosion() proc. The third value represents the probability of limb loss in percent.
|
||||
@@ -0,0 +1,65 @@
|
||||
/datum/targetable/spell/shock
|
||||
name = "Shocking Touch"
|
||||
desc = "Shocks the victim with electrical power, which can arc to nearby people and stun them. Takes a few seconds to cast."
|
||||
icon_state = "grasp"
|
||||
targeted = 1
|
||||
max_range = 2
|
||||
cooldown = 800
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
sticky = 1
|
||||
var/wattage = 100000
|
||||
var/burn_damage = 100
|
||||
var/target_damage_modifier = 1.95
|
||||
var/arc_range = 3
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.visible_message("<span style=\"color:red\"><b>[holder.owner] begins to cast a spell on [target]!</b></span>")
|
||||
playsound(holder.owner.loc, "sound/effects/elec_bzzz.ogg", 25, 1, -1)
|
||||
if (do_mob(holder.owner, target, 10))
|
||||
holder.owner.say("EI NATH")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/ShockingGraspLoud.ogg", 50, 0, -1)
|
||||
playsound(holder.owner.loc, "sound/effects/elec_bigzap.ogg", 25, 1, -1)
|
||||
|
||||
if (ishuman(target))
|
||||
if (target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[target] has divine protection from magic.</span>")
|
||||
target.visible_message("<span style=\"color:red\">The electric charge courses through [target] harmlessly!</span>")
|
||||
return
|
||||
else if (iswizard(target) && target.wizard_spellpower())
|
||||
target.visible_message("<span style=\"color:red\">The electric charge somehow completely misses [target]!</span>")
|
||||
return
|
||||
|
||||
if (holder.owner.wizard_spellpower())
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
//target.elecgib()
|
||||
arcFlash(holder.owner, target, 0) // we just want the effect, the damage is taken care of below
|
||||
target.TakeDamage("chest", 0, burn_damage / target_damage_modifier, 0, DAMAGE_BURN)
|
||||
var/count = 0
|
||||
for (var/mob/living/L in oview(src.arc_range, target))
|
||||
if (iswizard(L))
|
||||
continue
|
||||
count++
|
||||
for (var/mob/living/L in oview(src.arc_range, target))
|
||||
if (iswizard(L))
|
||||
continue
|
||||
arcFlash(target, L, max(src.wattage / count, 1)) // adds some randomness to the damage
|
||||
target.TakeDamage("chest", 0, burn_damage / count, 0, DAMAGE_BURN)
|
||||
else
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
target.visible_message("<span style=\"color:red\">[target] is severely burned by an electrical charge!</span>")
|
||||
target.lastattacker = holder.owner
|
||||
target.lastattackertime = world.time
|
||||
target.TakeDamage("chest", 0, 40, 0, DAMAGE_BURN)
|
||||
target.stunned += 6
|
||||
target.weakened += 6
|
||||
target.stuttering += 10
|
||||
else
|
||||
return 1 // no cooldown if it fails
|
||||
@@ -0,0 +1,69 @@
|
||||
/datum/targetable/spell/shockwave
|
||||
name = "Shockwave"
|
||||
desc = "Violently throws nearby targets away from the caster."
|
||||
icon_state = "shockwave"
|
||||
targeted = 0
|
||||
cooldown = 400
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
|
||||
cast()
|
||||
if(!holder)
|
||||
return
|
||||
holder.owner.say("ERATH QUUK")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/EarthquakeLoud.ogg", 50, 0, -1)
|
||||
|
||||
playsound(holder.owner.loc, "sound/effects/exlow.ogg", 25, 1, -1)
|
||||
|
||||
new/obj/effects/shockwave(holder.owner.loc)
|
||||
|
||||
var/list/range1 = orange(1, holder.owner.loc)
|
||||
var/list/range2 = orange(2, holder.owner.loc)
|
||||
var/list/range3 = orange(3, holder.owner.loc)
|
||||
|
||||
var/list/affected = list()
|
||||
get_edge_target_turf(src, holder.owner.dir)
|
||||
for(var/atom/A in range1)
|
||||
if(affected.Find(A)) continue
|
||||
affected += A
|
||||
//animate_shockwave(A)
|
||||
if(hasvar(A, "weakened")) A:weakened += 3
|
||||
if(istype(A, /atom/movable))
|
||||
if(!isturf(A) && hasvar(A, "anchored") && !A:anchored)
|
||||
spawn(0) A:throw_at(get_edge_cheap(A, get_dir(holder.owner, A)), 30, 1)
|
||||
sleep(3)
|
||||
for(var/atom/A in range1 ^ range2)
|
||||
if(affected.Find(A)) continue
|
||||
affected += A
|
||||
//animate_shockwave(A)
|
||||
if(hasvar(A, "weakened")) A:weakened += 3
|
||||
if(istype(A, /atom/movable))
|
||||
if(!isturf(A) && hasvar(A, "anchored") && !A:anchored)
|
||||
spawn(0) A:throw_at(get_edge_cheap(A, get_dir(holder.owner, A)), 30, 1)
|
||||
sleep(3)
|
||||
for(var/atom/A in range2 ^ range3)
|
||||
if(affected.Find(A)) continue
|
||||
affected += A
|
||||
//animate_shockwave(A)
|
||||
if(hasvar(A, "weakened")) A:weakened += 3
|
||||
if(istype(A, /atom/movable))
|
||||
if(!isturf(A) && hasvar(A, "anchored") && !A:anchored)
|
||||
spawn(0) A:throw_at(get_edge_cheap(A, get_dir(holder.owner, A)), 30, 1)
|
||||
|
||||
/obj/effects/shockwave
|
||||
name = "shockwave"
|
||||
desc = ""
|
||||
anchored = 1
|
||||
layer = EFFECTS_LAYER_1
|
||||
density = 0
|
||||
opacity = 0
|
||||
icon = 'icons/effects/224x224.dmi'
|
||||
icon_state = "shockwave"
|
||||
pixel_y = -96
|
||||
pixel_x = -96
|
||||
|
||||
New()
|
||||
src.Scale(0,0)
|
||||
animate(src, matrix(1.4, MATRIX_SCALE), time = 6, color = "#ffdddd", easing = LINEAR_EASING)
|
||||
animate(time = 2, alpha = 0)
|
||||
spawn(8) qdel(src)
|
||||
@@ -0,0 +1,85 @@
|
||||
// Simple buff for the staff. Maybe it's less of a wasted spell slot now (Convair880).
|
||||
/datum/targetable/spell/summon_staff
|
||||
name = "Summon Staff of Cthulhu"
|
||||
desc = "Returns the staff to your active hand."
|
||||
icon_state = "staff"
|
||||
targeted = 0
|
||||
cooldown = 600
|
||||
requires_robes = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
// Ability holder only checks for M.stat and wizard power, we need more than that here.
|
||||
if (M.stunned > 0 || M.weakened > 0 || M.paralysis > 0 || M.stat != 0 || M.restrained())
|
||||
boutput(M, __red("Not when you're incapacitated or restrained."))
|
||||
return 1
|
||||
|
||||
M.say("KOMH HEIRE")
|
||||
//playsound(M.loc, "sound/voice/wizard/[not_done_yet].ogg", 50, 0, -1)
|
||||
|
||||
var/list/staves = list()
|
||||
var/we_hold_it = 0
|
||||
for (var/obj/item/staff/cthulhu/S in world)
|
||||
if (M.mind && M.mind.key == S.wizard_key)
|
||||
if (S == M.find_in_hand(S))
|
||||
we_hold_it = 1
|
||||
continue
|
||||
if (!(S in staves))
|
||||
staves["[S.name] #[staves.len + 1] [ismob(S.loc) ? "carried by [S.loc.name]" : "at [get_area(S)]"]"] += S
|
||||
|
||||
switch (staves.len)
|
||||
if (-INFINITY to 0)
|
||||
if (we_hold_it != 0)
|
||||
boutput(M, __red("You're already holding your staff."))
|
||||
return 1 // No cooldown.
|
||||
else
|
||||
boutput(M, __red("You were unable to summon your staff."))
|
||||
return 0
|
||||
|
||||
if (1)
|
||||
var/obj/item/staff/cthulhu/S2
|
||||
for (var/C in staves)
|
||||
S2 = staves[C]
|
||||
break
|
||||
|
||||
if (!S2 || !istype(S2))
|
||||
boutput(M, __red("You were unable to summon your staff."))
|
||||
return 0
|
||||
|
||||
S2.send_staff_to_target_mob(M)
|
||||
|
||||
// There could be multiple, I suppose.
|
||||
if (2 to INFINITY)
|
||||
var/t1 = input("Please select a staff to summon", "Target Selection", null, null) as null|anything in staves
|
||||
if (!t1)
|
||||
return 1
|
||||
|
||||
var/obj/item/staff/cthulhu/S3 = staves[t1]
|
||||
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
if (!S3 || !istype(S3))
|
||||
boutput(M, __red("You were unable to summon your staff."))
|
||||
return 0
|
||||
if (!isliving(M) || !M.mind || !iswizard(M))
|
||||
boutput(M, __red("You seem to have lost all magical abilities."))
|
||||
return 0
|
||||
if (M.wizard_castcheck() == 0)
|
||||
return 0 // Has own user feedback.
|
||||
if (M.stunned > 0 || M.weakened > 0 || M.paralysis > 0 || M.stat != 0 || M.restrained())
|
||||
boutput(M, __red("Not when you're incapacitated or restrained."))
|
||||
return 0
|
||||
if (M.mind.key != S3.wizard_key)
|
||||
boutput(M, __red("You were unable to summon your staff."))
|
||||
return 0
|
||||
|
||||
S3.send_staff_to_target_mob(M)
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,153 @@
|
||||
/datum/targetable/spell/teleport
|
||||
name = "Teleport"
|
||||
desc = "Teleports you to an area of your choice after a short delay."
|
||||
icon_state = "phaseshift"
|
||||
targeted = 0
|
||||
cooldown = 450
|
||||
requires_robes = 1
|
||||
cooldown_staff = 1
|
||||
restricted_area_check = 1
|
||||
|
||||
cast()
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
if (holder.owner && ismob(holder.owner) && holder.owner.teleportscroll(0, 3) == 1)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
// These two procs were so similar that I combined them (Convair880).
|
||||
/mob/proc/teleportscroll(var/effect = 0, var/perform_check = 0, var/obj/item_to_check = null)
|
||||
if (src.paralysis > 0 || src.stat != 0)
|
||||
boutput(src, "<span style=\"color:red\">Not when you're incapacitated.</span>")
|
||||
return 0
|
||||
|
||||
if (!isturf(src.loc)) // Teleport doesn't go along well with doppelgaenger or phaseshift.
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if (!T || !isturf(T))
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
if (isrestrictedz(T.z))
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
|
||||
var/A
|
||||
A = input("Area to jump to", "Teleportation", A) in teleareas
|
||||
var/area/thearea = teleareas[A]
|
||||
|
||||
if (!thearea || !istype(thearea))
|
||||
src.show_text("Invalid selection.", "red")
|
||||
return 0
|
||||
|
||||
// You can keep the selection window open, so we have to do the checks again (individual item/spell procs handle the first batch).
|
||||
switch (perform_check)
|
||||
if (1)
|
||||
var/obj/item/teleportation_scroll/scroll_check = item_to_check
|
||||
if (!scroll_check || !istype(scroll_check))
|
||||
src.show_text("The scroll appears to have been destroyed.", "red")
|
||||
return 0
|
||||
if (!iswizard(src))
|
||||
boutput(src, "<span style=\"color:red\">The scroll is illegible!</span>")
|
||||
return 0
|
||||
if (scroll_check.uses < 1)
|
||||
src.show_text("The scroll is depleted!", "src")
|
||||
return 0
|
||||
if (scroll_check.loc != src && scroll_check.loc != src.back) // Pocket or backpack.
|
||||
src.show_text("You reach for the scroll, but it's just too far away.", "red")
|
||||
return 0
|
||||
|
||||
if (2)
|
||||
var/obj/machinery/computer/pod/comp_check = item_to_check
|
||||
if (!comp_check || !istype(comp_check))
|
||||
src.show_text("The computer appears to have been destroyed.", "red")
|
||||
return 0
|
||||
if (comp_check.stat & (NOPOWER|BROKEN))
|
||||
src.show_text("[comp_check] is out of order.", "red")
|
||||
return 0
|
||||
if (get_dist(src, comp_check) > 1)
|
||||
src.show_text("[comp_check] is too far away.", "red")
|
||||
return 0
|
||||
|
||||
if (3)
|
||||
if (!iswizard(src))
|
||||
boutput(src, "<span style=\"color:red\">You seem to have lost all magical abilities.</span>")
|
||||
return 0
|
||||
if (src.wizard_castcheck() == 0)
|
||||
return 0 // Has own user feedback.
|
||||
|
||||
if (src.paralysis > 0 || src.stat != 0)
|
||||
boutput(src, "<span style=\"color:red\">Not when you're incapacitated.</span>")
|
||||
return 0
|
||||
|
||||
if (!isturf(src.loc))
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
|
||||
var/turf/T2 = get_turf(src)
|
||||
if (!T2 || !isturf(T2))
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
if (isrestrictedz(T2.z))
|
||||
boutput(src, "<span style=\"color:red\">You can't seem to teleport from here.</span>")
|
||||
return 0
|
||||
|
||||
switch (perform_check)
|
||||
if (1)
|
||||
src.visible_message("<span style=\"color:red\"><b>[src] magically disappears!</b></span>")
|
||||
|
||||
if (2)
|
||||
src.visible_message("<span style=\"color:red\"><b>[src]</b> presses a button and teleports away.</span>")
|
||||
|
||||
if (3) // Spell-specific stuff.
|
||||
src.say("SCYAR NILA [uppertext(A)]")
|
||||
playsound(src.loc, "sound/voice/wizard/TeleportLoud.ogg", 50, 0, -1)
|
||||
src.visible_message("<span style=\"color:red\"><b>[src] begins to fade away!</b></span>")
|
||||
animate_teleport_wiz(src)
|
||||
sleep(40) // Animation.
|
||||
|
||||
var/mob/living/carbon/human/H = src
|
||||
if (istype(H) && H.burning)
|
||||
boutput(H, "<span style=\"color:blue\">The flames sputter out as you phase shift.</span>")
|
||||
H.set_burning(0)
|
||||
|
||||
playsound(src.loc, "sound/effects/mag_teleport.ogg", 25, 1, -1)
|
||||
|
||||
var/list/L = list()
|
||||
for (var/turf/T3 in get_area_turfs(thearea.type))
|
||||
if (!T3.density)
|
||||
var/clear = 1
|
||||
for (var/obj/O in T3)
|
||||
if (O.density)
|
||||
clear = 0
|
||||
break
|
||||
if (clear)
|
||||
L += T3
|
||||
|
||||
if (effect)
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(5, 1, src.loc)
|
||||
|
||||
if (perform_check == 3)
|
||||
src.set_loc(pick(L))
|
||||
s.start() // Effect second because we had sound effects etc at the old loc.
|
||||
else
|
||||
s.start()
|
||||
src.set_loc(pick(L))
|
||||
|
||||
else
|
||||
var/datum/effects/system/harmless_smoke_spread/smoke = new /datum/effects/system/harmless_smoke_spread()
|
||||
smoke.set_up(5, 0, src.loc)
|
||||
smoke.attach(src)
|
||||
|
||||
if (perform_check == 3)
|
||||
src.set_loc(pick(L))
|
||||
smoke.start()
|
||||
else
|
||||
smoke.start()
|
||||
src.set_loc(pick(L))
|
||||
|
||||
return 1
|
||||
@@ -0,0 +1,46 @@
|
||||
/datum/targetable/spell/warp
|
||||
name = "Warp"
|
||||
desc = "Teleports a foe away."
|
||||
icon_state = "warp"
|
||||
targeted = 1
|
||||
cooldown = 100
|
||||
requires_robes = 1
|
||||
offensive = 1
|
||||
restricted_area_check = 1
|
||||
sticky = 1
|
||||
|
||||
cast(mob/target)
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
holder.owner.say("GHEIT AUT")
|
||||
playsound(holder.owner.loc, "sound/voice/wizard/WarpLoud.ogg", 50, 0, -1)
|
||||
|
||||
if (target.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(holder.owner, "<span style=\"color:red\">[target] has divine protection from magic.</span>")
|
||||
playsound(target.loc, "sound/effects/mag_warp.ogg", 25, 1, -1)
|
||||
target.visible_message("<span style=\"color:red\">The spell fails to work on [target]!</span>")
|
||||
return
|
||||
|
||||
if (iswizard(target) && target.wizard_spellpower())
|
||||
target.visible_message("<span style=\"color:red\">The spell fails to work on [target]!</span>")
|
||||
playsound(target.loc, "sound/effects/mag_warp.ogg", 25, 1, -1)
|
||||
return
|
||||
|
||||
var/telerange = 10
|
||||
if (holder.owner.wizard_spellpower())
|
||||
telerange = 25
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Your spell is weak without a staff to focus it!</span>")
|
||||
var/datum/effects/system/spark_spread/s = unpool(/datum/effects/system/spark_spread)
|
||||
s.set_up(4, 1, target)
|
||||
s.start()
|
||||
var/list/randomturfs = new/list()
|
||||
for(var/turf/T in orange(target, telerange))
|
||||
if(istype(T, /turf/space) || T.density) continue
|
||||
randomturfs.Add(T)
|
||||
boutput(target, "<span style=\"color:blue\">You are caught in a magical warp field!</span>")
|
||||
animate_blink(target)
|
||||
target.visible_message("<span style=\"color:red\">[target] is warped away!</span>")
|
||||
playsound(target.loc, "sound/effects/mag_warp.ogg", 25, 1, -1)
|
||||
target.set_loc(pick(randomturfs))
|
||||
@@ -0,0 +1,599 @@
|
||||
/datum/abilityHolder/wraith
|
||||
topBarRendered = 1
|
||||
pointName = "Wraith Points"
|
||||
var/corpsecount = 0
|
||||
|
||||
/obj/screen/ability/topBar/wraith
|
||||
tens_offset_x = 19
|
||||
tens_offset_y = 7
|
||||
secs_offset_x = 23
|
||||
secs_offset_y = 7
|
||||
|
||||
//clicked(parameters)
|
||||
//if (!istype(usr, /mob/wraith))
|
||||
// return
|
||||
|
||||
//var/mob/wraith/user = usr
|
||||
|
||||
//if (!istype(user) || !istype(owner))
|
||||
// return
|
||||
|
||||
//..()
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility
|
||||
icon = 'icons/mob/wraith_ui.dmi'
|
||||
icon_state = "template"
|
||||
cooldown = 0
|
||||
last_cast = 0
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
preferred_holder_type = /datum/abilityHolder/wraith
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/topBar/wraith/B = new /obj/screen/ability/topBar/wraith(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
|
||||
cast(atom/target)
|
||||
if (!holder || !holder.owner)
|
||||
return 1
|
||||
//if (!istype(holder.owner, /mob/wraith))
|
||||
// boutput(holder.owner, "<span style=\"color:red\">Yo, you're not a wraith, stop that. (like how the hell did you get this. report this to a coder asap)</span>")
|
||||
// return 1
|
||||
return 0
|
||||
|
||||
doCooldown()
|
||||
if (!holder)
|
||||
return
|
||||
last_cast = world.time + cooldown
|
||||
holder.updateButtons()
|
||||
spawn(cooldown + 5)
|
||||
holder.updateButtons()
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility/help
|
||||
name = "Toggle Help Mode"
|
||||
desc = "Enter or exit help mode."
|
||||
icon_state = "help0"
|
||||
targeted = 0
|
||||
cooldown = 0
|
||||
helpable = 0
|
||||
special_screen_loc = "SOUTH,EAST"
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
if (holder.help_mode)
|
||||
holder.help_mode = 0
|
||||
else
|
||||
holder.help_mode = 1
|
||||
boutput(holder.owner, "<span style=\"color:blue\"><strong>Help Mode has been activated To disable it, click on this button again.</strong></span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">Hold down Shift, Ctrl or Alt while clicking the button to set it to that key.</span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">You will then be able to use it freely by holding that button and left-clicking a tile.</span>")
|
||||
boutput(holder.owner, "<span style=\"color:blue\">Alternatively, you can click with your middle mouse button to use the ability on your current tile.</span>")
|
||||
src.object.icon_state = "help[holder.help_mode]"
|
||||
holder.updateButtons()
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility/absorbCorpse
|
||||
name = "Absorb Corpse"
|
||||
icon_state = "absorbcorpse"
|
||||
desc = "Steal life essence from a corpse. You cannot use this on a skeleton!"
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 20
|
||||
cooldown = 450 //Starts at 45 seconds and scales upward exponentially
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
if (!T)
|
||||
T = get_turf(holder.owner)
|
||||
|
||||
//Find a suitable corpse
|
||||
var/error = 0
|
||||
var/mob/living/carbon/human/M
|
||||
if (isturf(T))
|
||||
for (var/mob/living/carbon/human/target in T.contents)
|
||||
if (target.stat == 2)
|
||||
error = 1
|
||||
if (target:decomp_stage != 4)
|
||||
M = target
|
||||
break
|
||||
else if (ishuman(T))
|
||||
M = T
|
||||
if (M.stat != 2)
|
||||
boutput(holder.owner, "<span style=\"color:red\">The living consciousness controlling this body shields it from being absorbed.</span>")
|
||||
return 1
|
||||
else if (M.decomp_stage == 4)
|
||||
M = null
|
||||
error = 1
|
||||
else
|
||||
M = T
|
||||
else
|
||||
boutput(holder.owner, "<span style=\"color:red\">Absorbing [src] does not satisfy your ethereal taste.</span>")
|
||||
return 1
|
||||
|
||||
if (!M && !error)
|
||||
boutput(holder.owner, "<span style=\"color:red\">There are no usable corpses here!</span>")
|
||||
return 1
|
||||
if (!M && error)
|
||||
boutput(holder.owner, "<span style=\"color:red\">[pick("This body is too decrepit to be of any use.", "This corpse has already been run through the wringer.", "There's nothing useful left.", "This corpse is worthless now.")]</span>")
|
||||
return 1
|
||||
|
||||
logTheThing("combat", usr, null, "absorbs the corpse of [key_name(M)] as a wraith.")
|
||||
|
||||
//Make the corpse all grody and skeleton-y
|
||||
M.decomp_stage = 4
|
||||
if (M.organHolder && M.organHolder.brain)
|
||||
qdel(M.organHolder.brain)
|
||||
M.set_face_icon_dirty()
|
||||
M.set_body_icon_dirty()
|
||||
particleMaster.SpawnSystem(new /datum/particleSystem/localSmoke("#000000", 5, locate(M.x, M.y, M.z)))
|
||||
|
||||
holder.regenRate *= 2.0
|
||||
holder.owner:onAbsorb(M)
|
||||
//Messages for everyone!
|
||||
boutput(holder.owner, "<span style=\"color:red\"><strong>[pick("You draw the essence of death out of [M]'s corpse!", "You drain the last scraps of life out of [M]'s corpse!")]</strong></span>")
|
||||
for (var/mob/living/V in viewers(7, holder.owner))
|
||||
boutput(V, "<span style=\"color:red\"><strong>[pick("Black smoke rises from [M]'s corpse! Freaky!", "[M]'s corpse suddenly rots to nothing but bone in moments!")]</strong></span>")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
doCooldown() //This makes it so wraith early game is much faster but hits a wall of high absorb cooldowns after ~5 corpses
|
||||
if (!holder) //so wraiths don't hit scientific notation rates of regen without playing perfectly for a million years
|
||||
return
|
||||
var/datum/abilityHolder/wraith/W = holder
|
||||
if (istype(W))
|
||||
if (W.corpsecount == 0)
|
||||
cooldown = 450
|
||||
W.corpsecount += 1
|
||||
else
|
||||
cooldown += W.corpsecount * 150
|
||||
W.corpsecount += 1
|
||||
last_cast = world.time + cooldown
|
||||
holder.updateButtons()
|
||||
spawn(cooldown + 5)
|
||||
holder.updateButtons()
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility/possessObject
|
||||
name = "Possess Object"
|
||||
icon_state = "possessobject"
|
||||
desc = "Possess and control an everyday object. Freakout level: high."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 300
|
||||
cooldown = 1500 //Tweaked this down from 3 minutes to 2 1/2, let's see if that ruins anything
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
if (src.holder.owner.density)
|
||||
boutput(usr, "<span style=\"color:red\">You cannot force your consciousness into a body while corporeal.</span>")
|
||||
return 1
|
||||
|
||||
if (!istype(T, /obj/item) || istype(T, /obj/item/storage/bible))
|
||||
boutput(holder.owner, "<span style=\"color:red\">You cannot possess this!</span>")
|
||||
return 1
|
||||
|
||||
boutput(holder.owner, "<span style=\"color:red\"><strong>[pick("You extend your will into [T].", "You force [T] to do your bidding.")]</strong></span>")
|
||||
var/mob/living/object/O = new/mob/living/object(T, holder.owner)
|
||||
|
||||
spawn (450)
|
||||
if (O)
|
||||
boutput(O, "<span style=\"color:red\">You feel your control of this vessel slipping away!</span>")
|
||||
spawn (600) //time limit on possession: 1 minute
|
||||
if (O)
|
||||
boutput(O, "<span style=\"color:red\"><strong>Your control is wrested away! The item is no longer yours.</strong></span>")
|
||||
O.death(0)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility/makeRevenant
|
||||
name = "Raise Revenant"
|
||||
icon_state = "revenant"
|
||||
desc = "Take control of an intact corpse as a powerful Revenant! You will not be able to absorb this corpse later. As a revenant, you gain increased point generation, but your revenant abilities cost much more points than normal."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 1000
|
||||
cooldown = 5000 //5 minutes
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
if (src.holder.owner.density)
|
||||
boutput(usr, "<span style=\"color:red\">You cannot force your consciousness into a body while corporeal.</span>")
|
||||
return 1
|
||||
|
||||
//If you targeted a turf for some reason, find a corpse on it
|
||||
if (istype(T, /turf))
|
||||
for (var/mob/living/carbon/human/target in T.contents)
|
||||
if (target.stat == 2 && target:decomp_stage != 4)
|
||||
T = target
|
||||
break
|
||||
|
||||
if (ishuman(T))
|
||||
var/mob/wraith/W = holder.owner
|
||||
return W.makeRevenant(T)
|
||||
//return 0
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">There are no corpses here to possess!</span>")
|
||||
return 1
|
||||
|
||||
/datum/targetable/wraithAbility/decay
|
||||
name = "Decay"
|
||||
icon_state = "decay"
|
||||
desc = "Cause a human to lose stamina, or an object to malfunction."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 30
|
||||
cooldown = 600 //1 minute
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
//If you targeted a turf for some reason, find a valid target on it
|
||||
var/atom/target = null
|
||||
if (istype(T, /turf))
|
||||
for (var/mob/living/carbon/human/M in T.contents)
|
||||
if (M.stat != 2)
|
||||
target = M
|
||||
break
|
||||
if (!target)
|
||||
for (var/obj/O in T.contents)
|
||||
target = O //todo: emaggable check
|
||||
break
|
||||
else
|
||||
target = T
|
||||
|
||||
if (ishuman(T))
|
||||
var/mob/living/carbon/H = T
|
||||
if (H.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(usr, "<span style=\"color:red\">Some mysterious force protects [T] from your influence.</span>")
|
||||
return 1
|
||||
else
|
||||
boutput(usr, "<span style=\"color:blue\">[pick("You sap [T]'s energy.", "You suck the breath out of [T].")]</span>")
|
||||
boutput(T, "<span style=\"color:red\">You feel really tired all of a sudden!</span>")
|
||||
T:emote("pale")
|
||||
T:stamina -= 100
|
||||
return 0
|
||||
else if (isobj(T))
|
||||
var/obj/O = T
|
||||
// go to jail, do not pass src, do not collect pushed messages
|
||||
if (O.emag_act(null, null))
|
||||
boutput(usr, "<span style=\"color:blue\">You alter the energy of [O].</span>")
|
||||
return 0
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">You fail to alter the energy of the [O].</span>")
|
||||
return 1
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">There is nothing to decay here!</span>")
|
||||
return 1
|
||||
|
||||
/datum/targetable/wraithAbility/command
|
||||
name = "Command"
|
||||
icon_state = "command"
|
||||
desc = "Command a few objects to hurl themselves at the target location."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 50
|
||||
cooldown = 200 // 20 seconds
|
||||
|
||||
cast(atom/T)
|
||||
var/list/thrown = list()
|
||||
var/current_prob = 100
|
||||
if (ishuman(T))
|
||||
var/mob/living/carbon/H = T
|
||||
if (H.bioHolder.HasEffect("training_chaplain"))
|
||||
boutput(usr, "<span style=\"color:red\">Some mysterious force protects [T] from your influence.</span>")
|
||||
return 1
|
||||
else
|
||||
T:stunned = max(max(T:weakened, T:stunned), 3)
|
||||
T:lying = 0
|
||||
T:weakened = 0
|
||||
T:show_message("<span style=\"color:red\">A ghostly force compels you to be still on your feet.</span>")
|
||||
for (var/obj/O in view(7, holder.owner))
|
||||
if (!O.anchored && isturf(O.loc))
|
||||
if (prob(current_prob))
|
||||
current_prob *= 0.35 // very steep. probably grabs 3 or 4 objects per cast -- much less effective than revenant command
|
||||
thrown += O
|
||||
animate_float(O)
|
||||
spawn(10)
|
||||
for (var/obj/O in thrown)
|
||||
O.throw_at(T, 32, 2)
|
||||
|
||||
return 0
|
||||
|
||||
/datum/targetable/wraithAbility/raiseSkeleton
|
||||
name = "Raise Skeleton"
|
||||
icon_state = "skeleton"
|
||||
desc = "Raise a skeletonized dead body as an indurable skeletal servant."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 150
|
||||
cooldown = 600 // 1 minute
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
//If you targeted a turf for some reason, find a corpse on it
|
||||
if (istype(T, /turf))
|
||||
for (var/mob/living/carbon/human/target in T.contents)
|
||||
if (target.stat == 2 && target:decomp_stage == 4)
|
||||
T = target
|
||||
break
|
||||
|
||||
if (ishuman(T))
|
||||
if (T:stat != 2 || T:decomp_stage != 4)
|
||||
boutput(usr, "<span style=\"color:red\">That body refuses to submit its skeleton to your will.</span>")
|
||||
return 1
|
||||
var/personname = T:real_name
|
||||
var/obj/critter/wraithskeleton/S = new /obj/critter/wraithskeleton(get_turf(T))
|
||||
S.name = "[personname]'s skeleton"
|
||||
S.health = 1
|
||||
T:gib()
|
||||
return 0
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">There are no skeletonized corpses here to raise!</span>")
|
||||
return 1
|
||||
|
||||
/datum/targetable/wraithAbility/animateObject
|
||||
name = "Animate Object"
|
||||
icon_state = "animobject"
|
||||
desc = "Animate an inanimate object to attack nearby humans."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 100
|
||||
cooldown = 300 //30 seconds
|
||||
|
||||
cast(atom/T)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/obj/O = T
|
||||
//If you targeted a turf for some reason, find an object on it
|
||||
if (istype(T, /turf))
|
||||
for (var/obj/target in T.contents)
|
||||
if (istype(target, /obj/critter) || istype(target, /obj/machinery/bot) || istype(target, /obj/decal) || target.anchored || target.invisibility)
|
||||
continue
|
||||
O = target
|
||||
break
|
||||
|
||||
if (istype(O))
|
||||
if(istype(O, /obj/critter) || istype(O, /obj/machinery/bot) || istype(O, /obj/decal) || O.anchored || O.invisibility)
|
||||
boutput(usr, "<span style=\"color:red\">That is not a valid target for animation!</span>")
|
||||
return 1
|
||||
O.visible_message("<span style=\"color:red\">The [O] comes to life!</span>")
|
||||
var/obj/critter/livingobj/L = new/obj/critter/livingobj(O.loc)
|
||||
O.loc = L
|
||||
L.name = "Living [O.name]"
|
||||
L.desc = "[O.desc]. It appears to be alive!"
|
||||
L.overlays += O
|
||||
L.health = rand(10, 50)
|
||||
L.atck_dmg = rand(5, 20)
|
||||
L.defensive = 1
|
||||
L.aggressive = 1
|
||||
L.atkcarbon = 1
|
||||
L.atksilicon = 1
|
||||
L.opensdoors = 1
|
||||
L.stunprob = 15
|
||||
L.original_object = O
|
||||
animate_levitate(L, -1, 30)
|
||||
return 0
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">There is no object here to animate!</span>")
|
||||
return 1
|
||||
|
||||
/datum/targetable/wraithAbility/haunt
|
||||
name = "Haunt"
|
||||
icon_state = "haunt"
|
||||
desc = "Become corporeal for 30 seconds. During this time, you gain additional biopoints, depending on the amount of humans in your vicinity. You cannot use this ability while already corporeal."
|
||||
targeted = 0
|
||||
pointCost = 0
|
||||
cooldown = 600 //1 minute
|
||||
|
||||
cast()
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
var/mob/wraith/W = src.holder.owner
|
||||
return W.haunt()
|
||||
|
||||
/obj/poltergeistMarker
|
||||
name = "nope"
|
||||
desc = "nope"
|
||||
invisibility = 101
|
||||
anchored = 1
|
||||
density = 0
|
||||
opacity = 0
|
||||
|
||||
|
||||
/datum/targetable/wraithAbility/poltergeist
|
||||
name = "Poltergeist - Mark Location"
|
||||
icon_state = "poltergeist"
|
||||
desc = "Cause freaky, weird, creepy or spooky stuff to happen in an area around you. Use this ability to mark your current tile as the origin of these events, then activate it by using this ability again."
|
||||
targeted = 0
|
||||
pointCost = 20
|
||||
cooldown = 0
|
||||
special_screen_loc="NORTH,EAST"
|
||||
|
||||
var/datum/radio_frequency/pda_connection
|
||||
var/obj/poltergeistMarker/marker = new /obj/poltergeistMarker()
|
||||
var/status = 0
|
||||
var/casting = 0
|
||||
var/static/list/effects = list("Flip light switches" = 1, "Burn out lights" = 2, "Create smoke" = 3, "Create ectoplasm" = 4, "Sap APC" = 5, "Haunt PDAs" = 6, "Open doors, lockers, crates" = 7, "Random" = 8)
|
||||
|
||||
|
||||
New()
|
||||
..()
|
||||
pda_connection = radio_controller.return_frequency("1149")
|
||||
|
||||
proc/haunt_pda(var/obj/item/device/pda2/pda)
|
||||
if (!pda_connection)
|
||||
return
|
||||
var/message = pick("boo", "git spooked", "BOOM", "there's a skeleton inside of you", "DEHUMANIZE YOURSELF AND FACE TO BLOODSHED", "ICARUS HAS FOUND YOU!!!!! RUN WHILE YOU CAN!!!!!!!!!!!")
|
||||
|
||||
var/datum/signal/signal = get_free_signal()
|
||||
signal.source = src.holder.owner
|
||||
signal.transmission_method = TRANSMISSION_RADIO
|
||||
signal.data["command"] = "text_message"
|
||||
signal.data["sender_name"] = holder.owner.name
|
||||
signal.data["message"] = "[message]" // (?)
|
||||
signal.data["sender"] = "00000000" // surely this isn't going to be a problem
|
||||
signal.data["address_1"] = pda.net_id
|
||||
|
||||
|
||||
pda_connection.post_signal(src, signal)
|
||||
|
||||
cast()
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
if (status == 0)
|
||||
marker.loc = get_turf(holder.owner)
|
||||
boutput(usr, "<span style=\"color:blue\">You prepare the area.</span>")
|
||||
return 0
|
||||
else
|
||||
if (casting)
|
||||
return
|
||||
casting = 1
|
||||
var/effect = input("Which effect?", "Effect", "Random") in effects
|
||||
if (effect == "Random")
|
||||
effect = rand(1, 7)
|
||||
else
|
||||
effect = effects[effect]
|
||||
switch (effect)
|
||||
if (1)
|
||||
boutput(usr, "<span style=\"color:blue\">You flip some light switches near the designated location!!</span>")
|
||||
for (var/obj/machinery/light_switch/L in range(10, marker))
|
||||
L.attack_hand(holder.owner)
|
||||
return 0
|
||||
if (2)
|
||||
boutput(usr, "<span style=\"color:blue\">You cause a few lights to burn out near the designated location!.</span>")
|
||||
var/c_prob = 100
|
||||
for (var/obj/machinery/light/L in range(10, marker))
|
||||
if (L.status == 2 || L.status == 1)
|
||||
continue
|
||||
if (prob(c_prob))
|
||||
L.broken()
|
||||
c_prob *= 0.5
|
||||
return 0
|
||||
if (3)
|
||||
boutput(usr, "<span style=\"color:blue\">Smoke rises in the designated location.</span>")
|
||||
var/turf/trgloc = get_turf(marker)
|
||||
var/list/affected = block(locate(trgloc.x - 3,trgloc.y - 3,trgloc.z), locate(trgloc.x + 3,trgloc.y + 3,trgloc.z))
|
||||
if(!affected.len) return
|
||||
var/list/centerview = view(world.view, trgloc)
|
||||
for(var/atom/A in affected)
|
||||
if(!(A in centerview)) continue
|
||||
if (A == holder.owner) continue
|
||||
var/obj/smokeDummy/D = new(A)
|
||||
spawn(150) qdel(D)
|
||||
particleMaster.SpawnSystem(new/datum/particleSystem/areaSmoke("#ffffff", 150, trgloc))
|
||||
return 0
|
||||
if (4)
|
||||
boutput(usr, "<span style=\"color:blue\">Matter from your realm appears near the designated location!</span>")
|
||||
var/count = rand(5,9)
|
||||
var/turf/trgloc = get_turf(marker)
|
||||
var/list/affected = block(locate(trgloc.x - 8,trgloc.y - 8,trgloc.z), locate(trgloc.x + 8,trgloc.y + 8,trgloc.z))
|
||||
for (var/i = 0, i < count, i++)
|
||||
new/obj/item/reagent_containers/food/snacks/ectoplasm(pick(affected))
|
||||
return 0
|
||||
if (5)
|
||||
var/sapped_amt = src.holder.regenRate * 100
|
||||
var/obj/machinery/power/apc/apc = locate() in get_area(marker)
|
||||
if (!apc)
|
||||
boutput(usr, "<span style=\"color:red\">Power sap failed: local APC not found.</span>")
|
||||
return 0
|
||||
boutput(usr, "<span style=\"color:blue\">You sap the power of the chamber's power source.</span>")
|
||||
var/obj/item/cell/cell = apc.cell
|
||||
if (cell)
|
||||
cell.use(sapped_amt)
|
||||
return 0
|
||||
if (6)
|
||||
boutput(usr, "<span style=\"color:blue\">Mysterious messages haunt PDAs near the designated location!</span>")
|
||||
for (var/mob/living/L in range(10, marker))
|
||||
var/obj/item/device/pda2/pda = locate() in L
|
||||
if (pda)
|
||||
src.haunt_pda(pda)
|
||||
for (var/obj/item/device/pda2/pda in range(10, marker))
|
||||
src.haunt_pda(pda)
|
||||
if (7)
|
||||
boutput(usr, "<span style=\"color:blue\">Crates, lockers and doors mysteriously open and close in the designated area!</span>")
|
||||
var/c_prob = 100
|
||||
for(var/obj/machinery/door/G in range(10, marker))
|
||||
if (prob(c_prob))
|
||||
c_prob *= 0.4
|
||||
spawn(1)
|
||||
if (G.density)
|
||||
G.open()
|
||||
else
|
||||
G.close()
|
||||
c_prob = 100
|
||||
for(var/obj/storage/F in range(10, marker))
|
||||
if (prob(c_prob))
|
||||
c_prob *= 0.4
|
||||
spawn(1)
|
||||
if (F.open)
|
||||
F.close()
|
||||
else
|
||||
F.open()
|
||||
|
||||
return 0
|
||||
|
||||
afterCast()
|
||||
if (status == 0)
|
||||
name = "Poltergeist - Cast"
|
||||
pointCost = 0
|
||||
cooldown = 300
|
||||
status = 1
|
||||
icon_state = "poltergeist1"
|
||||
else
|
||||
name = "Poltergeist - Mark Location"
|
||||
casting = 0
|
||||
pointCost = 20
|
||||
cooldown = 0
|
||||
status = 0
|
||||
icon_state = "poltergeist"
|
||||
|
||||
/datum/targetable/wraithAbility/whisper
|
||||
name = "Whisper"
|
||||
icon_state = "whisper"
|
||||
desc = "Send an ethereal message to a living being."
|
||||
targeted = 1
|
||||
target_anything = 1
|
||||
pointCost = 10
|
||||
cooldown = 200 //20 seconds
|
||||
|
||||
proc/ghostify_message(var/message)
|
||||
return message
|
||||
|
||||
cast(atom/target)
|
||||
if (..())
|
||||
return 1
|
||||
|
||||
if (ishuman(target))
|
||||
if (target:stat == 2)
|
||||
boutput(usr, "<span style=\"color:red\">They can hear you just fine without the use of your abilities.</span>")
|
||||
return 1
|
||||
else
|
||||
var/message = input("What would you like to whisper to [target]?", "Whisper", "") as text
|
||||
logTheThing("say", usr, target, "WRAITH WHISPER TO %target%: [message]")
|
||||
message = ghostify_message(trim(copytext(sanitize(message), 1, 255)))
|
||||
boutput(usr, "<b>You whisper to [target]:</b> [message]")
|
||||
boutput(target, "<b>A netherworldly voice whispers into your ears... </b> [message]")
|
||||
else
|
||||
boutput(usr, "<span style=\"color:red\">It would be futile to attempt to force your voice to the consciousness of that.</span>")
|
||||
return 1
|
||||
@@ -0,0 +1,258 @@
|
||||
// Converted everything related to wrestlers from client procs to ability holders and used
|
||||
// the opportunity to do some clean-up as well (Convair880).
|
||||
|
||||
//////////////////////////////////////////// Setup //////////////////////////////////////////////////
|
||||
|
||||
/mob/proc/make_wrestler(var/make_inherent = 0, var/belt_check = 0, var/remove_powers = 0)
|
||||
if (ishuman(src) || iscritter(src))
|
||||
if (iscritter(src))
|
||||
var/mob/living/critter/C = src
|
||||
|
||||
if (remove_powers == 1)
|
||||
var/datum/abilityHolder/wrestler/A = C.get_ability_holder(/datum/abilityHolder/wrestler)
|
||||
if (A && istype(A))
|
||||
C.remove_ability_holder(/datum/abilityHolder/wrestler)
|
||||
else
|
||||
C.abilityHolder.removeAbility(/datum/targetable/wrestler/kick)
|
||||
C.abilityHolder.removeAbility(/datum/targetable/wrestler/strike)
|
||||
C.abilityHolder.removeAbility(/datum/targetable/wrestler/drop)
|
||||
C.abilityHolder.removeAbility(/datum/targetable/wrestler/throw)
|
||||
C.abilityHolder.removeAbility(/datum/targetable/wrestler/slam)
|
||||
|
||||
return
|
||||
|
||||
else
|
||||
if (belt_check == 1) // They don't have belts.
|
||||
return
|
||||
|
||||
if (isnull(C.abilityHolder)) // But they do have a critter AH by default...or should.
|
||||
var/datum/abilityHolder/wrestler/A2 = C.add_ability_holder(/datum/abilityHolder/wrestler)
|
||||
if (!A2 || !istype(A2, /datum/abilityHolder/))
|
||||
return
|
||||
|
||||
C.abilityHolder.addAbility(/datum/targetable/wrestler/kick)
|
||||
C.abilityHolder.addAbility(/datum/targetable/wrestler/strike)
|
||||
C.abilityHolder.addAbility(/datum/targetable/wrestler/drop)
|
||||
C.abilityHolder.addAbility(/datum/targetable/wrestler/throw)
|
||||
C.abilityHolder.addAbility(/datum/targetable/wrestler/slam)
|
||||
|
||||
if (ishuman(src))
|
||||
var/mob/living/carbon/human/H = src
|
||||
|
||||
if (remove_powers == 1)
|
||||
var/datum/abilityHolder/wrestler/A3 = H.get_ability_holder(/datum/abilityHolder/wrestler)
|
||||
if (A3 && istype(A3))
|
||||
if (belt_check == 1 && A3.is_inherent == 1) // Wrestler/omnitraitor vs wrestling belt.
|
||||
return
|
||||
H.remove_ability_holder(/datum/abilityHolder/wrestler)
|
||||
else
|
||||
if (!isnull(H.abilityHolder))
|
||||
H.abilityHolder.removeAbility(/datum/targetable/wrestler/kick)
|
||||
H.abilityHolder.removeAbility(/datum/targetable/wrestler/strike)
|
||||
H.abilityHolder.removeAbility(/datum/targetable/wrestler/drop)
|
||||
H.abilityHolder.removeAbility(/datum/targetable/wrestler/throw)
|
||||
H.abilityHolder.removeAbility(/datum/targetable/wrestler/slam)
|
||||
|
||||
return
|
||||
|
||||
else
|
||||
if (belt_check == 1 && !(H.belt && istype(H.belt, /obj/item/storage/belt/wrestling)))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/wrestler/A4 = H.get_ability_holder(/datum/abilityHolder/wrestler)
|
||||
if (A4 && istype(A4))
|
||||
return
|
||||
|
||||
var/datum/abilityHolder/wrestler/A5 = H.add_ability_holder(/datum/abilityHolder/wrestler)
|
||||
A5.addAbility(/datum/targetable/wrestler/kick)
|
||||
A5.addAbility(/datum/targetable/wrestler/strike)
|
||||
A5.addAbility(/datum/targetable/wrestler/drop)
|
||||
A5.addAbility(/datum/targetable/wrestler/throw)
|
||||
A5.addAbility(/datum/targetable/wrestler/slam)
|
||||
|
||||
if (make_inherent == 1)
|
||||
A5.is_inherent = 1
|
||||
|
||||
if (belt_check != 1 && (src.mind && src.mind.special_role != "omnitraitor"))
|
||||
src << browse(grabResource("html/traitorTips/wrestlerTips.html"),"window=antagTips;titlebar=1;size=600x400;can_minimize=0;can_resize=0")
|
||||
|
||||
else return
|
||||
|
||||
//////////////////////////////////////////// Ability holder /////////////////////////////////////////
|
||||
|
||||
/obj/screen/ability/wrestler
|
||||
clicked(params)
|
||||
var/datum/targetable/wrestler/spell = owner
|
||||
if (!istype(spell))
|
||||
return
|
||||
if (!spell.holder)
|
||||
return
|
||||
if (!isturf(owner.holder.owner.loc))
|
||||
boutput(owner.holder.owner, "<span style=\"color:red\">You can't use this ability here.</span>")
|
||||
return
|
||||
if (spell.targeted && usr:targeting_spell == owner)
|
||||
usr:targeting_spell = null
|
||||
usr.update_cursor()
|
||||
return
|
||||
|
||||
var/use_targeted = src.do_target_selection_check()
|
||||
if (use_targeted == 2)
|
||||
return
|
||||
if (spell.targeted || use_targeted == 1)
|
||||
if (world.time < spell.last_cast)
|
||||
return
|
||||
owner.holder.owner.targeting_spell = owner
|
||||
owner.holder.owner.update_cursor()
|
||||
else
|
||||
spawn
|
||||
spell.handleCast()
|
||||
return
|
||||
|
||||
/datum/abilityHolder/wrestler
|
||||
usesPoints = 0
|
||||
regenRate = 0
|
||||
tabName = "Wrestler"
|
||||
notEnoughPointsMessage = "<span style=\"color:red\">You aren't strong enough to use this ability.</span>"
|
||||
var/is_inherent = 0 // Are we a wrestler as opposed to somebody with a wrestling belt?
|
||||
|
||||
/////////////////////////////////////////////// Wrestler spell parent ////////////////////////////
|
||||
|
||||
/datum/targetable/wrestler
|
||||
icon = 'icons/mob/critter_ui.dmi'
|
||||
icon_state = "template" // No custom sprites yet.
|
||||
cooldown = 0
|
||||
start_on_cooldown = 1 // So you can't bypass the cooldown by taking off your belt and re-equipping it.
|
||||
last_cast = 0
|
||||
pointCost = 0
|
||||
preferred_holder_type = /datum/abilityHolder/wrestler
|
||||
var/when_stunned = 0 // 0: Never | 1: Ignore mob.stunned and mob.weakened | 2: Ignore all incapacitation vars
|
||||
var/not_when_handcuffed = 0
|
||||
|
||||
New()
|
||||
var/obj/screen/ability/wrestler/B = new /obj/screen/ability/wrestler(null)
|
||||
B.icon = src.icon
|
||||
B.icon_state = src.icon_state
|
||||
B.owner = src
|
||||
B.name = src.name
|
||||
B.desc = src.desc
|
||||
src.object = B
|
||||
return
|
||||
|
||||
updateObject()
|
||||
..()
|
||||
if (!src.object)
|
||||
src.object = new /obj/screen/ability/wrestler()
|
||||
object.icon = src.icon
|
||||
object.owner = src
|
||||
if (src.last_cast > world.time)
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt] ([round((src.last_cast-world.time)/10)])"
|
||||
object.icon_state = src.icon_state + "_cd"
|
||||
else
|
||||
var/pttxt = ""
|
||||
if (pointCost)
|
||||
pttxt = " \[[pointCost]\]"
|
||||
object.name = "[src.name][pttxt]"
|
||||
object.icon_state = src.icon_state
|
||||
return
|
||||
|
||||
proc/incapacitation_check(var/stunned_only_is_okay = 0)
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
if (!M || !ismob(M))
|
||||
return 0
|
||||
|
||||
switch (stunned_only_is_okay)
|
||||
if (0)
|
||||
if (M.stat != 0 || M.stunned > 0 || M.paralysis > 0 || M.weakened > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
if (1)
|
||||
if (M.stat != 0 || M.paralysis > 0)
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
else
|
||||
return 1
|
||||
|
||||
castcheck()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
var/datum/abilityHolder/wrestler/H = holder
|
||||
|
||||
if (!M)
|
||||
return 0
|
||||
|
||||
// The HUD autoequip code doesn't call unequipped() when it should, naturally.
|
||||
if (ishuman(M) && (istype(H) && H.is_inherent != 1))
|
||||
var/mob/living/carbon/human/HH = M
|
||||
if (!(HH.belt && istype(HH.belt, /obj/item/storage/belt/wrestling)))
|
||||
boutput(HH, __red("You have to wear the wrestling belt for this."))
|
||||
HH.make_wrestler(0, 1, 1)
|
||||
return 0
|
||||
|
||||
if (!(ishuman(M) || iscritter(M))) // Not all critters have arms to grab people with, but whatever.
|
||||
boutput(M, __red("You cannot use any powers in your current form."))
|
||||
return 0
|
||||
|
||||
if (M.transforming)
|
||||
boutput(M, __red("You can't use any powers right now."))
|
||||
return 0
|
||||
|
||||
if (incapacitation_check(src.when_stunned) != 1)
|
||||
boutput(M, __red("You can't use this ability while incapacitated!"))
|
||||
return 0
|
||||
|
||||
if (src.not_when_handcuffed == 1 && M.restrained())
|
||||
boutput(M, __red("You can't use this ability when restrained!"))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
cast(atom/target)
|
||||
. = ..()
|
||||
actions.interrupt(holder.owner, INTERRUPT_ACT)
|
||||
return 0
|
||||
|
||||
proc/calculate_cooldown()
|
||||
if (!holder)
|
||||
return 0
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !istype(M))
|
||||
return 0
|
||||
|
||||
var/CD = src.cooldown
|
||||
var/ST_mod_max = M.get_stam_mod_max()
|
||||
var/ST_mod_regen = M.get_stam_mod_regen()
|
||||
|
||||
// Balanced for 200/12 and 200/13 drugs (e.g. epinephrine or meth), so stamina regeneration
|
||||
// buffs are prioritized over total stamina modifiers.
|
||||
var/R = src.cooldown - (((ST_mod_max / 3 ) + (ST_mod_regen * 2)) * 10)
|
||||
if (R > (src.cooldown * 2.5))
|
||||
R = src.cooldown * 2.5 // Chems with severe stamina penalty exist, so this should be capped.
|
||||
CD = max((src.cooldown / 2.5), R) // About the same minimum as the old wrestling belt procs.
|
||||
|
||||
//DEBUG("Default CD: [src.cooldown]. Modifier: [R]. Actual CD: [CD].")
|
||||
return CD
|
||||
|
||||
doCooldown()
|
||||
src.last_cast = world.time + calculate_cooldown()
|
||||
|
||||
if (!src.holder.owner || !ismob(src.holder.owner))
|
||||
return
|
||||
|
||||
// Why isn't this in afterCast()? Well, failed attempts to use an abililty call it too.
|
||||
spawn (rand(200, 900))
|
||||
if (src.holder && src.holder.owner && ismob(src.holder.owner))
|
||||
src.holder.owner.emote("flex")
|
||||
|
||||
return
|
||||
@@ -0,0 +1,114 @@
|
||||
/datum/targetable/wrestler/drop
|
||||
name = "Drop (prone)"
|
||||
desc = "Smash down onto on an opponent."
|
||||
targeted = 1
|
||||
target_anything = 0
|
||||
target_nodamage_check = 1
|
||||
target_selection_check = 1
|
||||
max_range = 1
|
||||
cooldown = 350
|
||||
start_on_cooldown = 1
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target)
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to wrestle yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
if (!target.lying)
|
||||
boutput(M, __red("You can use this move on prone opponents only!"))
|
||||
return 1
|
||||
|
||||
var/obj/surface = null
|
||||
var/turf/ST = null
|
||||
var/falling = 0
|
||||
|
||||
for (var/obj/O in oview(1, M))
|
||||
if (O.density == 1 || istype(O, /obj/stool))
|
||||
if (O == M) continue
|
||||
if (O == target) continue
|
||||
if (O.opacity) continue
|
||||
if (istype(O, /obj/window) || istype(O, /obj/grille))
|
||||
continue
|
||||
else
|
||||
surface = O
|
||||
ST = get_turf(O)
|
||||
break
|
||||
|
||||
if (surface && (ST && isturf(ST)))
|
||||
M.set_loc(ST)
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] climbs onto [surface]!</b></span>")
|
||||
M.pixel_y = 10
|
||||
falling = 1
|
||||
sleep (10)
|
||||
|
||||
if (M && target)
|
||||
// These are necessary because of the sleep call.
|
||||
if (src.castcheck() != 1)
|
||||
M.pixel_y = 0
|
||||
return 0
|
||||
|
||||
if ((falling == 0 && get_dist(M, target) > src.max_range) || (falling == 1 && get_dist(M, target) > (src.max_range + 1))) // We climbed onto stuff.
|
||||
M.pixel_y = 0
|
||||
if (falling == 1)
|
||||
M.visible_message("<span style=\"color:red\"><B>...and dives head-first into the ground, ouch!</b></span>")
|
||||
random_brute_damage(M, 15)
|
||||
M.weakened += 3
|
||||
boutput(M, __red("[target] is too far away!"))
|
||||
return 0
|
||||
|
||||
if (!isturf(M.loc) || !isturf(target.loc))
|
||||
M.pixel_y = 0
|
||||
boutput(M, __red("You can't drop onto [target] from here!"))
|
||||
return 0
|
||||
|
||||
spawn (0)
|
||||
if (M)
|
||||
animate(M, transform = matrix(90, MATRIX_ROTATE), time = 1, loop = 0)
|
||||
sleep (10)
|
||||
if (M)
|
||||
animate(transform = null, time = 1, loop = 0)
|
||||
|
||||
M.set_loc(target.loc)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] [pick_string("wrestling_belt.txt", "drop")] [target]!</B></span>")
|
||||
playsound(M.loc, "swing_hit", 50, 1)
|
||||
M.emote("scream")
|
||||
|
||||
if (falling == 1)
|
||||
if (prob(33) || target.stat == 2)
|
||||
target.ex_act(3)
|
||||
else
|
||||
random_brute_damage(target, 25)
|
||||
else
|
||||
random_brute_damage(target, 15)
|
||||
|
||||
target.weakened++
|
||||
target.stunned += 2
|
||||
|
||||
M.pixel_y = 0
|
||||
logTheThing("combat", M, target, "uses the drop wrestling move on %target% at [log_loc(M)].")
|
||||
|
||||
else
|
||||
if (M)
|
||||
M.pixel_y = 0
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/datum/targetable/wrestler/kick
|
||||
name = "Kick"
|
||||
desc = "A powerful kick, sends people flying away from you. Also useful for escaping from bad situations."
|
||||
targeted = 1
|
||||
target_anything = 0
|
||||
target_nodamage_check = 1
|
||||
target_selection_check = 1
|
||||
max_range = 1
|
||||
cooldown = 300
|
||||
start_on_cooldown = 1
|
||||
pointCost = 0
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 0
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target)
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to wrestle yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
M.emote("scream")
|
||||
M.emote("flip")
|
||||
M.dir = turn(M.dir, 90)
|
||||
|
||||
for (var/mob/C in oviewers(M))
|
||||
shake_camera(C, 8, 3)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M.name] [pick_string("wrestling_belt.txt", "kick")]-kicks [target]!</B></span>")
|
||||
random_brute_damage(target, 15)
|
||||
playsound(M.loc, "swing_hit", 60, 1)
|
||||
|
||||
var/turf/T = get_edge_target_turf(M, get_dir(M, get_step_away(target, M)))
|
||||
if (T && isturf(T))
|
||||
spawn(0)
|
||||
target.throw_at(T, 3, 2)
|
||||
target.weakened++
|
||||
target.stunned += 2
|
||||
|
||||
logTheThing("combat", M, target, "uses the kick wrestling move on %target% at [log_loc(M)].")
|
||||
return 0
|
||||
@@ -0,0 +1,172 @@
|
||||
/datum/targetable/wrestler/slam
|
||||
name = "Slam (grab)"
|
||||
desc = "Slam a grappled opponent into the floor."
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
target_nodamage_check = 0
|
||||
target_selection_check = 0
|
||||
max_range = 0
|
||||
cooldown = 350
|
||||
start_on_cooldown = 1
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
var/obj/item/grab/G = src.grab_check(null, 1, 1)
|
||||
if (!G || !istype(G))
|
||||
return 1
|
||||
|
||||
var/mob/living/HH = G.affecting
|
||||
HH.set_loc(M.loc)
|
||||
M.dir = get_dir(M, HH)
|
||||
HH.dir = get_dir(HH, M)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] lifts [HH] up!</B></span>")
|
||||
|
||||
spawn (0)
|
||||
if (HH)
|
||||
animate(HH, transform = matrix(180, MATRIX_ROTATE), time = 1, loop = 0)
|
||||
sleep (15)
|
||||
if (HH)
|
||||
animate(transform = null, time = 1, loop = 0)
|
||||
|
||||
var/GT = G.state // Can't include a possibly non-existent item in the loop before we can run the check.
|
||||
for (var/i = 0, i < (GT * 3), i++)
|
||||
if (M && HH)
|
||||
M.pixel_y += 3
|
||||
HH.pixel_y += 3
|
||||
M.dir = turn(M.dir, 90)
|
||||
HH.dir = turn(HH.dir, 90)
|
||||
|
||||
switch (M.dir)
|
||||
if (NORTH)
|
||||
HH.pixel_x = M.pixel_x
|
||||
if (SOUTH)
|
||||
HH.pixel_x = M.pixel_x
|
||||
if (EAST)
|
||||
HH.pixel_x = M.pixel_x - 8
|
||||
if (WEST)
|
||||
HH.pixel_x = M.pixel_x + 8
|
||||
|
||||
// These are necessary because of the sleep call.
|
||||
if (!G || !istype(G) || G.state < 1)
|
||||
boutput(M, __red("You can't slam the target without a firm grab!"))
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
return 0
|
||||
|
||||
if (src.castcheck() != 1)
|
||||
qdel(G)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
return 0
|
||||
|
||||
if (get_dist(M, HH) > 1)
|
||||
boutput(M, __red("[target] is too far away!"))
|
||||
qdel(G)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
return 0
|
||||
|
||||
if (!isturf(M.loc) || !isturf(HH.loc))
|
||||
boutput(M, __red("You can't slam [target] here!"))
|
||||
qdel(G)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
return 0
|
||||
else
|
||||
if (M)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
if (HH)
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
return 0
|
||||
|
||||
sleep (1)
|
||||
|
||||
if (M && HH)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
|
||||
// These are necessary because of the sleep call.
|
||||
if (!G || !istype(G) || G.state < 1)
|
||||
boutput(M, __red("You can't slam the target without a firm grab!"))
|
||||
return 0
|
||||
|
||||
if (src.castcheck() != 1)
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (get_dist(M, HH) > 1)
|
||||
boutput(M, __red("[HH] is too far away!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (!isturf(M.loc) || !isturf(HH.loc))
|
||||
boutput(M, __red("You can't slam [HH] here!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
HH.set_loc(M.loc)
|
||||
|
||||
var/fluff = pick_string("wrestling_belt.txt", "slam")
|
||||
switch (G.state)
|
||||
if (2)
|
||||
fluff = "turbo [fluff]"
|
||||
if (3)
|
||||
fluff = "atomic [fluff]"
|
||||
playsound(M.loc, "sound/effects/explosionfar.ogg", 60, 1)
|
||||
|
||||
playsound(M.loc, "sound/effects/fleshbr1.ogg", 75, 1)
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] [fluff] [HH]!</B></span>")
|
||||
|
||||
if (HH.stat != 2)
|
||||
HH.emote("scream")
|
||||
HH.weakened += 2
|
||||
HH.stunned += 2
|
||||
|
||||
switch (G.state)
|
||||
if (2)
|
||||
random_brute_damage(HH, 25)
|
||||
if (3)
|
||||
HH.ex_act(3)
|
||||
else
|
||||
random_brute_damage(HH, 15)
|
||||
else
|
||||
HH.ex_act(3)
|
||||
|
||||
qdel(G)
|
||||
logTheThing("combat", M, HH, "uses the slam wrestling move on %target% at [log_loc(M)].")
|
||||
|
||||
else
|
||||
if (M)
|
||||
M.pixel_x = 0
|
||||
M.pixel_y = 0
|
||||
if (HH)
|
||||
HH.pixel_x = 0
|
||||
HH.pixel_y = 0
|
||||
|
||||
if (G && istype(G)) // Target was gibbed before we could slam them, who knows.
|
||||
qdel(G)
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,57 @@
|
||||
/datum/targetable/wrestler/strike
|
||||
name = "Strike"
|
||||
desc = "Hit a neaby opponent with a quick attack."
|
||||
targeted = 1
|
||||
target_anything = 0
|
||||
target_nodamage_check = 1
|
||||
target_selection_check = 1
|
||||
max_range = 1
|
||||
cooldown = 250
|
||||
start_on_cooldown = 1
|
||||
pointCost = 0
|
||||
when_stunned = 1
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M || !target)
|
||||
return 1
|
||||
|
||||
if (M == target)
|
||||
boutput(M, __red("Why would you want to wrestle yourself?"))
|
||||
return 1
|
||||
|
||||
if (get_dist(M, target) > src.max_range)
|
||||
boutput(M, __red("[target] is too far away."))
|
||||
return 1
|
||||
|
||||
var/turf/T = get_turf(M)
|
||||
if (T && isturf(T) && target && isturf(target.loc))
|
||||
playsound(M.loc, "swing_hit", 50, 1)
|
||||
|
||||
spawn (0)
|
||||
for (var/i = 0, i < 4, i++)
|
||||
M.dir = turn(M.dir, 90)
|
||||
|
||||
M.set_loc(target.loc)
|
||||
spawn (4)
|
||||
if (M && (T && isturf(T) && get_dist(M, T) <= 1))
|
||||
M.set_loc(T)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><b>[M] [pick_string("wrestling_belt.txt", "strike")] [target]!</b></span>")
|
||||
random_brute_damage(target, 15)
|
||||
playsound(M.loc, "sound/effects/fleshbr1.ogg", 75, 1)
|
||||
|
||||
target.paralysis++
|
||||
target.change_misstep_chance(25)
|
||||
|
||||
logTheThing("combat", M, target, "uses the strike wrestling move on %target% at [log_loc(M)].")
|
||||
|
||||
else
|
||||
boutput(M, __red("You can't wrestle the target here!"))
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,121 @@
|
||||
/datum/targetable/wrestler/throw
|
||||
name = "Throw (grab)"
|
||||
desc = "Spin a grabbed opponent around and throw them."
|
||||
targeted = 0
|
||||
target_anything = 0
|
||||
target_nodamage_check = 0
|
||||
target_selection_check = 0
|
||||
max_range = 0
|
||||
cooldown = 300
|
||||
start_on_cooldown = 1
|
||||
pointCost = 0
|
||||
when_stunned = 0
|
||||
not_when_handcuffed = 1
|
||||
|
||||
cast(mob/target)
|
||||
if (!holder)
|
||||
return 1
|
||||
|
||||
var/mob/living/M = holder.owner
|
||||
|
||||
if (!M)
|
||||
return 1
|
||||
|
||||
var/obj/item/grab/G = src.grab_check(null, 1, 1)
|
||||
if (!G || !istype(G))
|
||||
return 1
|
||||
|
||||
var/mob/living/HH = G.affecting
|
||||
HH.set_loc(M.loc)
|
||||
HH.dir = get_dir(HH, M)
|
||||
|
||||
HH.stunned = 4
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] starts spinning around with [HH]!</B></span>")
|
||||
M.emote("scream")
|
||||
|
||||
for (var/i = 0, i < 20, i++)
|
||||
var/delay = 5
|
||||
switch (i)
|
||||
if (17 to INFINITY)
|
||||
delay = 0.25
|
||||
if (14 to 16)
|
||||
delay = 0.5
|
||||
if (9 to 13)
|
||||
delay = 1
|
||||
if (5 to 8)
|
||||
delay = 2
|
||||
if (0 to 4)
|
||||
delay = 3
|
||||
|
||||
if (M && HH)
|
||||
// These are necessary because of the sleep call.
|
||||
if (!G || !istype(G) || G.state < 1)
|
||||
boutput(M, __red("You can't throw the target without a firm grab!"))
|
||||
return 0
|
||||
|
||||
if (src.castcheck() != 1)
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (get_dist(M, HH) > 1)
|
||||
boutput(M, __red("[HH] is too far away!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (!isturf(M.loc) || !isturf(HH.loc))
|
||||
boutput(M, __red("You can't throw [HH] from here!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
M.dir = turn(M.dir, 90)
|
||||
var/turf/T = get_step(M, M.dir)
|
||||
var/turf/S = HH.loc
|
||||
if ((S && isturf(S) && S.Exit(HH)) && (T && isturf(T) && T.Enter(HH)))
|
||||
HH.set_loc(T)
|
||||
HH.dir = get_dir(HH, M)
|
||||
else
|
||||
return 0
|
||||
|
||||
sleep (delay)
|
||||
|
||||
if (M && HH)
|
||||
// These are necessary because of the sleep call.
|
||||
if (!G || !istype(G) || G.state < 1)
|
||||
boutput(M, __red("You can't throw the target without a firm grab!"))
|
||||
return 0
|
||||
|
||||
if (src.castcheck() != 1)
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (get_dist(M, HH) > 1)
|
||||
boutput(M, __red("[HH] is too far away!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
if (!isturf(M.loc) || !isturf(HH.loc))
|
||||
boutput(M, __red("You can't throw [HH] from here!"))
|
||||
qdel(G)
|
||||
return 0
|
||||
|
||||
HH.set_loc(M.loc) // Maybe this will help with the wallthrowing bug.
|
||||
qdel(G)
|
||||
|
||||
M.visible_message("<span style=\"color:red\"><B>[M] [pick_string("wrestling_belt.txt", "throw")] [HH]!</B></span>")
|
||||
playsound(M.loc, "swing_hit", 50, 1)
|
||||
|
||||
var/turf/T = get_edge_target_turf(M, M.dir)
|
||||
if (T && isturf(T))
|
||||
spawn(0)
|
||||
if (HH.stat != 2)
|
||||
HH.emote("scream")
|
||||
HH.throw_at(T, 10, 4)
|
||||
HH.weakened += 2
|
||||
HH.change_misstep_chance(33)
|
||||
|
||||
logTheThing("combat", M, HH, "uses the throw wrestling move on %target% at [log_loc(M)].")
|
||||
|
||||
if (G && istype(G)) // Target was gibbed before we could throw them, who knows.
|
||||
qdel(G)
|
||||
|
||||
return 0
|
||||
Reference in New Issue
Block a user