Files
Stephen 036a29b765 all mbc code from jan 1st to 2/16/2020
includes a lot of small things + fixes.
big ones are Simple Lights, New Grab stuff, Disorient Eye/Ear protection armor values, access reprogrammer and deconstruction device

also added flabo to the list of contributors, we missed her on the first go!

check changelog for full list, i merged that too
2020-02-17 01:36:01 -05:00

1052 lines
30 KiB
Plaintext

/datum/abilityHolder
var/help_mode = 0
var/list/abilities = list()
var/list/suspended = list()
var/locked = 0
var/topBarRendered = 1
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
var/x_occupied = 0
var/y_occupied = 0
var/datum/abilityHolder/composite_owner = 0
var/any_abilities_displayed = 0
var/cast_while_dead = 0
// cirr's effort to make these work like normal huds, take 1
var/datum/hud/hud
var/next_update = 0
New(var/mob/M)
owner = M
hud = new()
if(owner)
owner.attach_hud(hud)
disposing()
for (var/obj/screen/S in hud.objects)
if (hasvar(S, "master") && S:master == src)
S:master = null
if (owner)
owner.detach_hud(hud)
hud.clear_master()
hud.mobs -= src
if (owner)
owner.huds -= hud
owner = null
..()
proc/onLife(var/mult = 1)
.= 0
proc/updateButtons(var/called_by_owner = 0, var/start_x = 1, var/start_y = 0)
//MBC : sorry, but for things to work correctly in the case of composite with multiple holders (omnitraitor) we need to update via the composite holder only
//note that the composite holder reads in x_occupied and y_occupied. that's important for positioning over multiple holders.
if (composite_owner && !called_by_owner)
composite_owner.updateButtons()
return
x_occupied = 0
y_occupied = 0
any_abilities_displayed = 0
if (src.topBarRendered && src.rendered)
if(!src.owner || !src.owner.client)
return
if (!called_by_owner)
for(var/obj/screen/ability/A in src.hud)
src.hud -= A
if (ishuman(owner))
var/mob/living/carbon/human/H = owner
// are we viewing genetic powers instead of our item abilities and other abilities?
// if so, don't bother adding buttons back to hud, keep it empty
if (H.hud.current_ability_set == 2)
return
var/pos_x = start_x
var/pos_y = start_y
for(var/datum/targetable/B in src.abilities)
if (!istype(B.object, /obj/screen/ability/topBar))
continue
if (!B.display_available())
B.object.screen_loc = null
continue
any_abilities_displayed = 1
var/obj/screen/ability/topBar/button = B.object
button.update_on_hud(pos_x,pos_y)
button.icon = B.icon
button.icon_state = B.icon_state
if (!B.special_screen_loc)
pos_x++
if(pos_x > 15)
pos_x = 1
pos_y++
x_occupied = pos_x
y_occupied = pos_y
return
// legacy stat panel button rendering
// hopefully one day we can just axe this completely
// wouldn't that be a fuckin' dream come true
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(var/mult = 1)
lastBonus = bonus
points += bonus
points += regenRate * mult
bonus = 0
proc/transferOwnership(var/newbody)
if(owner)
owner.detach_hud(hud)
owner = newbody
if(owner)
owner.attach_hud(hud)
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 (!rendered)
return
if (topBarRendered) //Topbar shows abilities, just display the toplevel info in Status
statpanel("Status")
onAbilityStat()
stat(null, " ")
else
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, params)
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, params)
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, params)
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
proc/onAbilityHolderInstanceAdd()
return 0
proc/remove_unlocks()
return 0
proc/set_loc_callback(var/newloc)
.=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")
disposing()
owner = null
..()
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_DBG(0)
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)
if (src && src.owner && usr.client.tooltipHolder && control == "mapwindow.map")
var/theme = src.owner.theme
usr.client.tooltipHolder.showHover(src, list(
"params" = params,
"title" = src.name,
"content" = (src.desc ? src.desc : null),
"theme" = theme
))
MouseExited()
if (usr.client.tooltipHolder)
usr.client.tooltipHolder.hideHover()
/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 = 19
var/tens_offset_y = 7
var/secs_offset_x = 23
var/secs_offset_y = 7
//mbc : used for updates called without positioning - just use last poistion
var/last_x = 0
var/last_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
if (isnull(darkener)) // fuck. -drsingh
darkener = image('icons/mob/spell_buttons.dmi',"darkener")
darkener.alpha = 100
SPAWN_DBG(0)
if(owner)
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 && owner.pointCost)
name += "<br> Cost: [owner.pointCost] [owner.holder.pointName]"
if (owner.cooldown)
name += "<br> Cooldown: [owner.cooldown / 10] s"
src.name = name
var/datum/hud/abilityHud
if(owner.holder)
// for nice, well-behaved abilities that live in a holder
abilityHud = owner.holder.hud
else
// for fucking deviant genetics abilities that know neither ethics nor morality
if(ishuman(M)) // they better fucking be human i'm not dealing with other things getting bioeffects
var/mob/living/carbon/human/H = M
abilityHud = H.hud
abilityHud.add_object(src)
abilityHud.remove_object(src.cd_tens)
abilityHud.remove_object(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]"
abilityHud.add_object(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]"
abilityHud.add_object(src.cd_secs)
last_x = pos_x
last_y = pos_y
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 = 10
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.
interrupt_action_bars = 1 //if 1, we will interrupt any action bars running with the INTERRUPT_ACT flag
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"
theme = null // for wire's tooltips, it's about time this got varized
New()
..()
if (src.icon && src.icon_state)
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
disposing()
if (object && object.owner == src)
object.owner = null
..()
proc
handleCast(atom/target, params)
Z_LOG_DEBUG("abilities", "handle shit [target] / [params]")
var/result = tryCast(target, params)
if (result && result != 999)
last_cast = 0 // reset cooldown
else if (result != 999)
doCooldown()
afterCast()
holder.updateButtons()
cast(atom/target)
if(interrupt_action_bars) actions.interrupt(holder.owner, INTERRUPT_ACT)
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, params)
Z_LOG_DEBUG("abilities", "fucking christ [target] / [params]")
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 (!holder.cast_while_dead && isdead(holder.owner))
boutput(holder.owner, "<span style=\"color:red\">You cannot cast this ability while you are dead.</span>")
src.holder.locked = 0
return 999
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, params)
src.holder.locked = 0
if (!.)
holder.deductPoints(pointCost)
updateObject()
return
doCooldown()
src.last_cast = world.time + src.cooldown
SPAWN_DBG(src.cooldown + 5)
holder.updateButtons()
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
display_available()
.= (src.icon && src.icon_state)
flip_callback()
.= 0
/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 = 1
topBarRendered = 1
disposing()
for (var/datum/abilityHolder/H in holders)
H.owner = null
..()
proc/addHolder(holderType)
for (var/datum/abilityHolder/H in holders)
if (H.type == holderType)
return
holders += new holderType(owner)
holders[holders.len].composite_owner = src
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
N.onAbilityHolderInstanceAdd()
updateButtons()
proc/removeHolder(holderType)
for (var/datum/abilityHolder/H in holders)
if (H.type == holderType)
H.composite_owner = 0
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(var/called_by_owner = 0, var/start_x = 1, var/start_y = 0)
if (src.topBarRendered && src.rendered && src.hud)
for(var/obj/screen/ability/A in src.hud)
src.hud -= A
x_occupied = 1
y_occupied = 0
any_abilities_displayed = 0
for (var/datum/abilityHolder/H in holders)
if (H.topBarRendered || H.rendered)
H.updateButtons(called_by_owner = 1, start_x = x_occupied, start_y = y_occupied)
x_occupied = H.x_occupied
y_occupied = H.y_occupied
any_abilities_displayed = any_abilities_displayed || H.any_abilities_displayed
addBonus(var/value)
for (var/datum/abilityHolder/H in holders)
H.addBonus(value)
generatePoints(var/mult = 1)
for (var/datum/abilityHolder/H in holders)
H.generatePoints(mult)
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 A
var/datum/abilityHolder/X = holders[1]
A.holder = X
X.abilities += A
//X.updateButtons()
A.onAttach(X)
src.updateButtons()
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(src.owner)
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
remove_unlocks()
for (var/datum/abilityHolder/H in holders)
H.remove_unlocks()
onLife(var/mult = 1)
for (var/datum/abilityHolder/H in holders)
H.onLife(mult)
set_loc_callback(var/newloc)
for (var/datum/abilityHolder/H in holders)
H.set_loc_callback(newloc)