Initial Commit

This commit is contained in:
Tobba
2016-03-06 20:52:14 +01:00
commit b181d0b552
4325 changed files with 579453 additions and 0 deletions
+874
View File
@@ -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
+193
View File
@@ -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
+194
View File
@@ -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)
+61
View File
@@ -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)
+112
View File
@@ -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
+89
View File
@@ -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)
+33
View File
@@ -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
+80
View File
@@ -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
+72
View File
@@ -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
+75
View File
@@ -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
+33
View File
@@ -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
+44
View File
@@ -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
+127
View File
@@ -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
+52
View File
@@ -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
+32
View File
@@ -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
+27
View File
@@ -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
+111
View File
@@ -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()
+166
View File
@@ -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
+47
View File
@@ -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
+74
View File
@@ -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
+59
View File
@@ -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!"))
+355
View File
@@ -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
+519
View File
@@ -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
+383
View File
@@ -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
+194
View File
@@ -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!"))
+48
View File
@@ -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
+46
View File
@@ -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
+377
View File
@@ -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
+242
View File
@@ -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)
+57
View File
@@ -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)
+67
View File
@@ -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>")
+102
View File
@@ -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
+82
View File
@@ -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)
+55
View File
@@ -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
+62
View File
@@ -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)
+168
View File
@@ -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
+49
View File
@@ -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
+34
View File
@@ -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)
+34
View File
@@ -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)
+31
View File
@@ -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")
+161
View File
@@ -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)
+131
View File
@@ -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
+32
View File
@@ -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.
+65
View File
@@ -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
+69
View File
@@ -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
+153
View File
@@ -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
+46
View File
@@ -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))
+599
View File
@@ -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
+258
View File
@@ -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
+114
View File
@@ -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
+51
View File
@@ -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
+172
View File
@@ -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
+57
View File
@@ -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
+121
View File
@@ -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