mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Merge branch 'master' of https://github.com/PolarisSS13/Polaris into item_state
Conflicts: code/modules/clothing/head/jobs.dm icons/mob/items/lefthand.dmi icons/mob/items/righthand.dmi icons/mob/suit.dmi icons/mob/uniform.dmi icons/obj/items.dmi
This commit is contained in:
@@ -68,10 +68,12 @@ var/list/be_special_flags = list(
|
||||
#define MODE_COMMANDO "commando"
|
||||
#define MODE_DEATHSQUAD "deathsquad"
|
||||
#define MODE_ERT "ert"
|
||||
#define MODE_TRADE "trader"
|
||||
#define MODE_MERCENARY "mercenary"
|
||||
#define MODE_NINJA "ninja"
|
||||
#define MODE_RAIDER "raider"
|
||||
#define MODE_WIZARD "wizard"
|
||||
#define MODE_TECHNOMANCER "technomancer"
|
||||
#define MODE_CHANGELING "changeling"
|
||||
#define MODE_CULTIST "cultist"
|
||||
#define MODE_HIGHLANDER "highlander"
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects,
|
||||
// being unable to hear people due to being in a box within a bag.
|
||||
|
||||
/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1)
|
||||
/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1, var/ignore_show_messages = 0)
|
||||
|
||||
if(!recursion_limit)
|
||||
return L
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
else if(istype(I,/obj/))
|
||||
var/obj/check_obj = I
|
||||
if(check_obj.show_messages)
|
||||
if(ignore_show_messages || check_obj.show_messages)
|
||||
if(!sight_check || isInSight(I, O))
|
||||
L |= recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects)
|
||||
if(include_objects)
|
||||
|
||||
@@ -119,27 +119,12 @@ proc/age2agedescription(age)
|
||||
if(70 to INFINITY) return "elderly"
|
||||
else return "unknown"
|
||||
|
||||
proc/RoundHealth(health)
|
||||
switch(health)
|
||||
if(100 to INFINITY)
|
||||
return "health100"
|
||||
if(70 to 100)
|
||||
return "health80"
|
||||
if(50 to 70)
|
||||
return "health60"
|
||||
if(30 to 50)
|
||||
return "health40"
|
||||
if(18 to 30)
|
||||
return "health25"
|
||||
if(5 to 18)
|
||||
return "health10"
|
||||
if(1 to 5)
|
||||
return "health1"
|
||||
if(-99 to 0)
|
||||
return "health0"
|
||||
else
|
||||
return "health-100"
|
||||
return "0"
|
||||
/proc/RoundHealth(health)
|
||||
var/list/icon_states = icon_states('icons/mob/hud_med.dmi')
|
||||
for(var/icon_state in icon_states)
|
||||
if(health >= text2num(icon_state))
|
||||
return icon_state
|
||||
return icon_states[icon_states.len] // If we had no match, return the last element
|
||||
|
||||
/*
|
||||
Proc for attack log creation, because really why not
|
||||
|
||||
@@ -103,6 +103,9 @@
|
||||
#define ui_alien_health "EAST-1:28,CENTER-1:13" //aliens have the health display where humans have the pressure damage indicator.
|
||||
|
||||
#define ui_ling_chemical_display "EAST-1:28,CENTER-3:15"
|
||||
#define ui_wiz_energy_display "EAST-1:28,CENTER-3:15"
|
||||
//#define ui_wiz_instability_display "EAST-2:28,CENTER-3:15"
|
||||
#define ui_wiz_instability_display "EAST-1:28,NORTH-2:27"
|
||||
|
||||
//Pop-up inventory
|
||||
#define ui_shoes "WEST+1:8,SOUTH:5"
|
||||
|
||||
378
code/_onclick/hud/ability_screen_objects.dm
Normal file
378
code/_onclick/hud/ability_screen_objects.dm
Normal file
@@ -0,0 +1,378 @@
|
||||
/obj/screen/movable/ability_master
|
||||
name = "Abilities"
|
||||
icon = 'icons/mob/screen_spells.dmi'
|
||||
icon_state = "grey_spell_ready"
|
||||
var/list/obj/screen/ability/ability_objects = list()
|
||||
var/showing = 0 // If we're 'open' or not.
|
||||
|
||||
var/open_state = "master_open" // What the button looks like when it's 'open', showing the other buttons.
|
||||
var/closed_state = "master_closed" // Button when it's 'closed', hiding everything else.
|
||||
|
||||
screen_loc = ui_spell_master // TODO: Rename
|
||||
|
||||
var/mob/my_mob = null // The mob that possesses this hud object.
|
||||
|
||||
/obj/screen/movable/ability_master/New(owner)
|
||||
if(owner)
|
||||
my_mob = owner
|
||||
update_abilities(0, owner)
|
||||
else
|
||||
message_admins("ERROR: ability_master's New() was not given an owner argument. This is a bug.")
|
||||
|
||||
/obj/screen/movable/ability_master/Destroy()
|
||||
..()
|
||||
//Get rid of the ability objects.
|
||||
remove_all_abilities()
|
||||
ability_objects.Cut()
|
||||
|
||||
// After that, remove ourselves from the mob seeing us, so we can qdel cleanly.
|
||||
if(my_mob)
|
||||
my_mob.ability_master = null
|
||||
if(my_mob.client && my_mob.client.screen)
|
||||
my_mob.client.screen -= src
|
||||
my_mob = null
|
||||
|
||||
/obj/screen/movable/ability_master/ResetVars()
|
||||
..("ability_objects", args)
|
||||
remove_all_abilities()
|
||||
// ability_objects = list()
|
||||
|
||||
/obj/screen/movable/ability_master/MouseDrop()
|
||||
if(showing)
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/screen/movable/ability_master/Click()
|
||||
if(!ability_objects.len) // If we're empty for some reason.
|
||||
// qdel(src)
|
||||
return
|
||||
|
||||
toggle_open()
|
||||
|
||||
/obj/screen/movable/ability_master/proc/toggle_open(var/forced_state = 0)
|
||||
if(showing && (forced_state != 2)) // We are closing the ability master, hide the abilities.
|
||||
for(var/obj/screen/ability/O in ability_objects)
|
||||
if(my_mob && my_mob.client)
|
||||
my_mob.client.screen -= O
|
||||
// O.handle_icon_updates = 0
|
||||
showing = 0
|
||||
overlays.len = 0
|
||||
overlays.Add(closed_state)
|
||||
else if(forced_state != 1) // We're opening it, show the icons.
|
||||
open_ability_master()
|
||||
update_abilities(1)
|
||||
showing = 1
|
||||
overlays.len = 0
|
||||
overlays.Add(open_state)
|
||||
update_icon()
|
||||
|
||||
/obj/screen/movable/ability_master/proc/open_ability_master()
|
||||
var/list/screen_loc_xy = splittext(screen_loc,",")
|
||||
|
||||
//Create list of X offsets
|
||||
var/list/screen_loc_X = splittext(screen_loc_xy[1],":")
|
||||
var/x_position = decode_screen_X(screen_loc_X[1])
|
||||
var/x_pix = screen_loc_X[2]
|
||||
|
||||
//Create list of Y offsets
|
||||
var/list/screen_loc_Y = splittext(screen_loc_xy[2],":")
|
||||
var/y_position = decode_screen_Y(screen_loc_Y[1])
|
||||
var/y_pix = screen_loc_Y[2]
|
||||
|
||||
for(var/i = 1; i <= ability_objects.len; i++)
|
||||
var/obj/screen/ability/A = ability_objects[i]
|
||||
var/xpos = x_position + (x_position < 8 ? 1 : -1)*(i%7)
|
||||
var/ypos = y_position + (y_position < 8 ? round(i/7) : -round(i/7))
|
||||
A.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]"
|
||||
if(my_mob && my_mob.client)
|
||||
my_mob.client.screen += A
|
||||
// A.handle_icon_updates = 1
|
||||
|
||||
/obj/screen/movable/ability_master/proc/update_abilities(forced = 0, mob/user)
|
||||
update_icon()
|
||||
if(user && user.client)
|
||||
if(!(src in user.client.screen))
|
||||
user.client.screen += src
|
||||
var/i = 1
|
||||
for(var/obj/screen/ability/ability in ability_objects)
|
||||
ability.update_icon(forced)
|
||||
ability.maptext = "[i]" // Slot number
|
||||
i++
|
||||
|
||||
/obj/screen/movable/ability_master/update_icon()
|
||||
if(ability_objects.len)
|
||||
invisibility = 0
|
||||
else
|
||||
invisibility = 101
|
||||
|
||||
/obj/screen/movable/ability_master/proc/add_ability(var/name_given)
|
||||
if(!name) return
|
||||
|
||||
// if(spell.connected_button) //we have one already, for some reason
|
||||
// if(spell.connected_button in spell_objects)
|
||||
// return
|
||||
// else
|
||||
// spell_objects.Add(spell.connected_button)
|
||||
// if(spell_holder.client)
|
||||
// toggle_open(2)
|
||||
// return
|
||||
|
||||
// if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one
|
||||
// return
|
||||
|
||||
var/obj/screen/ability/new_button = new /obj/screen/ability
|
||||
new_button.ability_master = src
|
||||
// new_button.spell = spell
|
||||
|
||||
// spell.connected_button = newscreen
|
||||
|
||||
// if(!spell.override_base) //if it's not set, we do basic checks
|
||||
// if(spell.spell_flags & CONSTRUCT_CHECK)
|
||||
// newscreen.spell_base = "const" //construct spells
|
||||
// else
|
||||
// newscreen.spell_base = "wiz" //wizard spells
|
||||
// else
|
||||
// newscreen.spell_base = spell.override_base
|
||||
new_button.name = name_given
|
||||
new_button.ability_icon_state = name_given
|
||||
new_button.update_icon(1)
|
||||
ability_objects.Add(new_button)
|
||||
if(my_mob.client)
|
||||
toggle_open(2) //forces the icons to refresh on screen
|
||||
|
||||
/obj/screen/movable/ability_master/proc/remove_ability(var/obj/screen/ability/ability)
|
||||
if(!ability)
|
||||
return
|
||||
ability_objects.Remove(ability)
|
||||
qdel(ability)
|
||||
|
||||
|
||||
if(ability_objects.len)
|
||||
toggle_open(showing + 1)
|
||||
update_icon()
|
||||
// else
|
||||
// qdel(src)
|
||||
|
||||
/obj/screen/movable/ability_master/proc/remove_all_abilities()
|
||||
for(var/obj/screen/ability/A in ability_objects)
|
||||
remove_ability(A)
|
||||
|
||||
/obj/screen/movable/ability_master/proc/get_ability_by_name(name_to_search)
|
||||
for(var/obj/screen/ability/A in ability_objects)
|
||||
if(A.name == name_to_search)
|
||||
return A
|
||||
return null
|
||||
|
||||
/obj/screen/movable/ability_master/proc/get_ability_by_proc_ref(proc_ref)
|
||||
for(var/obj/screen/ability/verb_based/V in ability_objects)
|
||||
if(V.verb_to_call == proc_ref)
|
||||
return V
|
||||
return null
|
||||
|
||||
/obj/screen/movable/ability_master/proc/get_ability_by_instance(var/obj/instance/)
|
||||
for(var/obj/screen/ability/obj_based/O in ability_objects)
|
||||
if(O.object == instance)
|
||||
return O
|
||||
return null
|
||||
|
||||
/mob/Login()
|
||||
..()
|
||||
if(ability_master)
|
||||
ability_master.toggle_open(1)
|
||||
client.screen -= ability_master
|
||||
|
||||
/mob/New()
|
||||
..()
|
||||
ability_master = new /obj/screen/movable/ability_master(src)
|
||||
|
||||
///////////ACTUAL ABILITIES////////////
|
||||
//This is what you click to do things//
|
||||
///////////////////////////////////////
|
||||
/obj/screen/ability
|
||||
icon = 'icons/mob/screen_spells.dmi'
|
||||
icon_state = "grey_spell_base"
|
||||
maptext_x = 3
|
||||
var/background_base_state = "grey"
|
||||
var/ability_icon_state = null
|
||||
|
||||
// var/spell/spell = null
|
||||
var/obj/screen/movable/ability_master/ability_master
|
||||
|
||||
// var/icon/last_charged_icon
|
||||
|
||||
/obj/screen/ability/Destroy()
|
||||
if(ability_master)
|
||||
ability_master.ability_objects -= src
|
||||
if(ability_master.my_mob && ability_master.my_mob.client)
|
||||
ability_master.my_mob.client.screen -= src
|
||||
if(ability_master && !ability_master.ability_objects.len)
|
||||
ability_master.update_icon()
|
||||
// qdel(ability_master)
|
||||
ability_master = null
|
||||
..()
|
||||
|
||||
/obj/screen/ability/update_icon()
|
||||
// if(!spell)
|
||||
// qdel(src)
|
||||
// return
|
||||
|
||||
// if((last_charge == spell.charge_counter || !handle_icon_updates) && !forced_update)
|
||||
// return //nothing to see here
|
||||
|
||||
// overlays -= spell.hud_state
|
||||
|
||||
// if(spell.charge_type == Sp_RECHARGE || spell.charge_type == Sp_CHARGES)
|
||||
// if(spell.charge_counter < spell.charge_max)
|
||||
// icon_state = "[background_base_state]_spell_base"
|
||||
// if(spell.charge_counter > 0)
|
||||
// var/icon/partial_charge = icon(src.icon, "[spell_base]_spell_ready")
|
||||
// partial_charge.Crop(1, 1, partial_charge.Width(), round(partial_charge.Height() * spell.charge_counter / spell.charge_max))
|
||||
// overlays += partial_charge
|
||||
// if(last_charged_icon)
|
||||
// overlays -= last_charged_icon
|
||||
// last_charged_icon = partial_charge
|
||||
// else if(last_charged_icon)
|
||||
// overlays -= last_charged_icon
|
||||
// last_charged_icon = null
|
||||
// else
|
||||
// icon_state = "[spell_base]_spell_ready"
|
||||
// if(last_charged_icon)
|
||||
// overlays -= last_charged_icon
|
||||
// else
|
||||
// icon_state = "[spell_base]_spell_ready"
|
||||
overlays.Cut()
|
||||
icon_state = "[background_base_state]_spell_base"
|
||||
|
||||
overlays += ability_icon_state
|
||||
|
||||
// last_charge = spell.charge_counter
|
||||
|
||||
// overlays -= "silence"
|
||||
// if(spell.silenced)
|
||||
// overlays += "silence"
|
||||
|
||||
/obj/screen/ability/Click()
|
||||
if(!usr)
|
||||
// qdel(src)
|
||||
return
|
||||
|
||||
// spell.perform(usr)
|
||||
activate()
|
||||
|
||||
// Makes the ability be triggered. The subclasses of this are responsible for carrying it out in whatever way it needs to.
|
||||
/obj/screen/ability/proc/activate()
|
||||
world << "[src] had activate() called."
|
||||
return
|
||||
|
||||
// This checks if the ability can be used.
|
||||
/obj/screen/ability/proc/can_activate()
|
||||
return 1
|
||||
|
||||
/client/verb/activate_ability(var/slot as num)
|
||||
set name = ".activate_ability"
|
||||
// set hidden = 1
|
||||
if(!mob)
|
||||
return // Paranoid.
|
||||
if(isnull(slot) || !isnum(slot))
|
||||
src << "<span class='warning'>.activate_ability requires a number as input, corrisponding to the slot you wish to use.</span>"
|
||||
return // Bad input.
|
||||
if(!mob.ability_master)
|
||||
return // No abilities.
|
||||
if(slot > mob.ability_master.ability_objects.len || slot <= 0)
|
||||
return // Out of bounds.
|
||||
var/obj/screen/ability/A = mob.ability_master.ability_objects[slot]
|
||||
A.activate()
|
||||
|
||||
//////////Verb Abilities//////////
|
||||
//Buttons to trigger verbs/procs//
|
||||
//////////////////////////////////
|
||||
|
||||
/obj/screen/ability/verb_based
|
||||
var/verb_to_call = null
|
||||
var/object_used = null
|
||||
var/arguments_to_use = list()
|
||||
|
||||
/obj/screen/ability/verb_based/activate()
|
||||
if(object_used && verb_to_call)
|
||||
call(object_used,verb_to_call)(arguments_to_use)
|
||||
// call(object_used,verb_to_call)(arguments_to_use)
|
||||
// world << "Attempted to call([object_used],[verb_to_call])([arguments_to_use])"
|
||||
// if(hascall(object_used, verb_to_call))
|
||||
// call(object_used,verb_to_call)(arguments_to_use)
|
||||
// else
|
||||
// message_admins("ERROR: activate() on [ability_master.my_mob]'s [src] failed the hascall([object_used],[verb_to_call]) check.")
|
||||
|
||||
/obj/screen/movable/ability_master/proc/add_verb_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments)
|
||||
if(!object_given)
|
||||
message_admins("ERROR: add_verb_ability() was not given an object in its arguments.")
|
||||
if(!verb_given)
|
||||
message_admins("ERROR: add_verb_ability() was not given a verb/proc in its arguments.")
|
||||
if(get_ability_by_proc_ref(verb_given))
|
||||
return // Duplicate
|
||||
var/obj/screen/ability/verb_based/A = new /obj/screen/ability/verb_based()
|
||||
A.ability_master = src
|
||||
A.object_used = object_given
|
||||
A.verb_to_call = verb_given
|
||||
A.ability_icon_state = ability_icon_given
|
||||
A.name = name_given
|
||||
if(arguments)
|
||||
A.arguments_to_use = arguments
|
||||
ability_objects.Add(A)
|
||||
if(my_mob.client)
|
||||
toggle_open(2) //forces the icons to refresh on screen
|
||||
|
||||
//Changeling Abilities
|
||||
/obj/screen/ability/verb_based/changeling
|
||||
icon_state = "ling_spell_base"
|
||||
background_base_state = "ling"
|
||||
|
||||
/obj/screen/movable/ability_master/proc/add_ling_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments)
|
||||
if(!object_given)
|
||||
message_admins("ERROR: add_ling_ability() was not given an object in its arguments.")
|
||||
if(!verb_given)
|
||||
message_admins("ERROR: add_ling_ability() was not given a verb/proc in its arguments.")
|
||||
if(get_ability_by_proc_ref(verb_given))
|
||||
return // Duplicate
|
||||
var/obj/screen/ability/verb_based/changeling/A = new /obj/screen/ability/verb_based/changeling()
|
||||
A.ability_master = src
|
||||
A.object_used = object_given
|
||||
A.verb_to_call = verb_given
|
||||
A.ability_icon_state = ability_icon_given
|
||||
A.name = name_given
|
||||
if(arguments)
|
||||
A.arguments_to_use = arguments
|
||||
ability_objects.Add(A)
|
||||
if(my_mob.client)
|
||||
toggle_open(2) //forces the icons to refresh on screen
|
||||
|
||||
|
||||
/////////Obj Abilities////////
|
||||
//Buttons to trigger objects//
|
||||
//////////////////////////////
|
||||
|
||||
/obj/screen/ability/obj_based
|
||||
var/obj/object = null
|
||||
|
||||
/obj/screen/ability/obj_based/activate()
|
||||
if(object)
|
||||
object.Click()
|
||||
|
||||
// Technomancer
|
||||
/obj/screen/ability/obj_based/technomancer
|
||||
icon_state = "wiz_spell_base"
|
||||
background_base_state = "wiz"
|
||||
|
||||
/obj/screen/movable/ability_master/proc/add_technomancer_ability(var/obj/object_given, var/ability_icon_given)
|
||||
if(!object_given)
|
||||
message_admins("ERROR: add_technomancer_ability() was not given an object in its arguments.")
|
||||
if(get_ability_by_instance(object_given))
|
||||
return // Duplicate
|
||||
var/obj/screen/ability/obj_based/technomancer/A = new /obj/screen/ability/obj_based/technomancer()
|
||||
A.ability_master = src
|
||||
A.object = object_given
|
||||
A.ability_icon_state = ability_icon_given
|
||||
A.name = object_given.name
|
||||
ability_objects.Add(A)
|
||||
if(my_mob.client)
|
||||
toggle_open(2) //forces the icons to refresh on screen
|
||||
@@ -133,6 +133,8 @@ var/list/global_huds = list(
|
||||
var/hotkey_ui_hidden = 0 //This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons)
|
||||
|
||||
var/obj/screen/lingchemdisplay
|
||||
var/obj/screen/wiz_instability_display
|
||||
var/obj/screen/wiz_energy_display
|
||||
var/obj/screen/blobpwrdisplay
|
||||
var/obj/screen/blobhealthdisplay
|
||||
var/obj/screen/r_hand_hud_object
|
||||
@@ -159,6 +161,8 @@ datum/hud/New(mob/owner)
|
||||
disarm_intent = null
|
||||
help_intent = null
|
||||
lingchemdisplay = null
|
||||
wiz_instability_display = null
|
||||
wiz_energy_display = null
|
||||
blobpwrdisplay = null
|
||||
blobhealthdisplay = null
|
||||
r_hand_hud_object = null
|
||||
|
||||
@@ -303,6 +303,17 @@
|
||||
mymob.ling_chem_display.icon_state = "ling_chems"
|
||||
hud_elements |= mymob.ling_chem_display
|
||||
|
||||
mymob.wiz_instability_display = new /obj/screen/wizard/instability()
|
||||
mymob.wiz_instability_display.screen_loc = ui_wiz_instability_display
|
||||
mymob.wiz_instability_display.icon_state = "wiz_instability_none"
|
||||
hud_elements |= mymob.wiz_instability_display
|
||||
|
||||
mymob.wiz_energy_display = new/obj/screen/wizard/energy()
|
||||
mymob.wiz_energy_display.screen_loc = ui_wiz_energy_display
|
||||
mymob.wiz_energy_display.icon_state = "wiz_energy"
|
||||
hud_elements |= mymob.wiz_energy_display
|
||||
|
||||
|
||||
mymob.pain = new /obj/screen( null )
|
||||
|
||||
mymob.zone_sel = new /obj/screen/zone_sel( null )
|
||||
@@ -371,3 +382,15 @@
|
||||
/obj/screen/ling/chems
|
||||
name = "chemical storage"
|
||||
icon_state = "power_display"
|
||||
|
||||
/obj/screen/wizard
|
||||
invisibility = 101
|
||||
|
||||
/obj/screen/wizard/instability
|
||||
name = "instability"
|
||||
icon_state = "instability-1"
|
||||
invisibility = 0
|
||||
|
||||
/obj/screen/wizard/energy
|
||||
name = "energy"
|
||||
icon_state = "wiz_energy"
|
||||
@@ -9,40 +9,35 @@
|
||||
|
||||
/datum/supply_packs/materials/metal50
|
||||
name = "50 metal sheets"
|
||||
contains = list(/obj/item/stack/material/steel)
|
||||
amount = 50
|
||||
contains = list(/obj/item/stack/material/steel/fifty)
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Metal sheets crate"
|
||||
|
||||
/datum/supply_packs/materials/glass50
|
||||
name = "50 glass sheets"
|
||||
contains = list(/obj/item/stack/material/glass)
|
||||
amount = 50
|
||||
contains = list(/obj/item/stack/material/glass/fifty)
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Glass sheets crate"
|
||||
|
||||
/datum/supply_packs/materials/wood50
|
||||
name = "50 wooden planks"
|
||||
contains = list(/obj/item/stack/material/wood)
|
||||
amount = 50
|
||||
contains = list(/obj/item/stack/material/wood/fifty)
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Wooden planks crate"
|
||||
|
||||
/datum/supply_packs/materials/plastic50
|
||||
name = "50 plastic sheets"
|
||||
contains = list(/obj/item/stack/material/plastic)
|
||||
amount = 50
|
||||
contains = list(/obj/item/stack/material/plastic/fifty)
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Plastic sheets crate"
|
||||
|
||||
/datum/supply_packs/materials/cardboard_sheets
|
||||
contains = list(/obj/item/stack/material/cardboard)
|
||||
contains = list(/obj/item/stack/material/cardboard/fifty)
|
||||
name = "50 cardboard sheets"
|
||||
amount = 50
|
||||
cost = 10
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Cardboard sheets crate"
|
||||
@@ -53,10 +48,9 @@
|
||||
containername = "Imported carpet crate"
|
||||
cost = 15
|
||||
contains = list(
|
||||
/obj/item/stack/tile/carpet,
|
||||
/obj/item/stack/tile/carpet/blue
|
||||
/obj/item/stack/tile/carpet/fifty,
|
||||
/obj/item/stack/tile/carpet/blue/fifty
|
||||
)
|
||||
amount = 50
|
||||
|
||||
|
||||
/datum/supply_packs/misc/linoleum
|
||||
@@ -64,5 +58,4 @@
|
||||
containertype = /obj/structure/closet/crate
|
||||
containername = "Linoleum crate"
|
||||
cost = 15
|
||||
contains = list(/obj/item/stack/tile/linoleum)
|
||||
amount = 50
|
||||
contains = list(/obj/item/stack/tile/linoleum/fifty)
|
||||
@@ -28,7 +28,6 @@ var/list/all_supply_groups = list("Atmospherics",
|
||||
var/name = null
|
||||
var/list/contains = list()
|
||||
var/manifest = ""
|
||||
var/amount = null
|
||||
var/cost = null
|
||||
var/containertype = null
|
||||
var/containername = null
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
Use the regular_hud_updates() proc before process_med_hud(mob) or process_sec_hud(mob) so
|
||||
the HUD updates properly! */
|
||||
|
||||
// hud overlay image type, used for clearing client.images precisely
|
||||
/image/hud_overlay
|
||||
|
||||
//Medical HUD outputs. Called by the Life() proc of the mob using it, usually.
|
||||
proc/process_med_hud(var/mob/M, var/local_scanner, var/mob/Alt)
|
||||
if(!can_process_hud(M))
|
||||
@@ -63,8 +66,7 @@ proc/can_process_hud(var/mob/M)
|
||||
//Deletes the current HUD images so they can be refreshed with new ones.
|
||||
mob/proc/handle_regular_hud_updates() //Used in the life.dm of mobs that can use HUDs.
|
||||
if(client)
|
||||
for(var/image/hud in client.images)
|
||||
if(copytext(hud.icon_state,1,4) == "hud")
|
||||
for(var/image/hud_overlay/hud in client.images)
|
||||
client.images -= hud
|
||||
med_hud_users -= src
|
||||
sec_hud_users -= src
|
||||
|
||||
@@ -104,6 +104,9 @@
|
||||
if(ghosts_only && !istype(player.current, /mob/observer/dead))
|
||||
candidates -= player
|
||||
log_debug("[key_name(player)] is not eligible to become a [role_text]: Only ghosts may join as this role! They have been removed from the draft.")
|
||||
else if(istype(player.current, /mob/living/voice))
|
||||
candidates -= player
|
||||
log_debug("[key_name(player)] is not eligible to become a [role_text]: They are only a communicator voice. They have been removed from the draft.")
|
||||
else if(player.special_role)
|
||||
candidates -= player
|
||||
log_debug("[key_name(player)] is not eligible to become a [role_text]: They already have a special role ([player.special_role])! They have been removed from the draft.")
|
||||
|
||||
79
code/game/antagonist/outsider/technomancer.dm
Normal file
79
code/game/antagonist/outsider/technomancer.dm
Normal file
@@ -0,0 +1,79 @@
|
||||
var/datum/antagonist/technomancer/technomancers
|
||||
|
||||
/datum/antagonist/technomancer
|
||||
id = MODE_TECHNOMANCER
|
||||
role_type = BE_WIZARD
|
||||
role_text = "Technomancer"
|
||||
role_text_plural = "Technomancers"
|
||||
bantype = "wizard"
|
||||
landmark_id = "wizard"
|
||||
welcome_text = "You will need to purchase <b>functions</b> and perhaps some <b>equipment</b> from the various machines around your \
|
||||
base. Choose your technological arsenal carefully. Remember that without the <b>core</b> on your back, your functions are \
|
||||
powerless, and therefore you will be as well.<br>\
|
||||
In your pockets you will find a one-time use teleport device. Use it to leave the base and go to the colony, when you are ready."
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_CLEAR_EQUIPMENT | ANTAG_CHOOSE_NAME | ANTAG_SET_APPEARANCE | ANTAG_VOTABLE
|
||||
antaghud_indicator = "hudwizard"
|
||||
|
||||
hard_cap = 1
|
||||
hard_cap_round = 3
|
||||
initial_spawn_req = 1
|
||||
initial_spawn_target = 1
|
||||
|
||||
id_type = /obj/item/weapon/card/id/syndicate
|
||||
|
||||
/datum/antagonist/technomancer/New()
|
||||
..()
|
||||
technomancers = src
|
||||
|
||||
/datum/antagonist/technomancer/update_antag_mob(var/datum/mind/technomancer)
|
||||
..()
|
||||
technomancer.store_memory("<B>Remember:</B> Do not forget to purchase the functions and equipment you need.")
|
||||
technomancer.current.real_name = "[pick(wizard_first)] [pick(wizard_second)]"
|
||||
technomancer.current.name = technomancer.current.real_name
|
||||
|
||||
/datum/antagonist/technomancer/equip(var/mob/living/carbon/human/technomancer_mob)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/under/technomancer/master(technomancer_mob), slot_w_uniform)
|
||||
create_id("Technomagus", technomancer_mob)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/weapon/disposable_teleporter/free(technomancer_mob), slot_r_store)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/weapon/technomancer_catalog(technomancer_mob), slot_l_store)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/device/radio/headset(technomancer_mob), slot_l_ear)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/weapon/technomancer_core(technomancer_mob), slot_back)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/device/flashlight(technomancer_mob), slot_belt)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(technomancer_mob), slot_shoes)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/head/technomancer/master(technomancer_mob), slot_head)
|
||||
technomancer_mob.update_icons()
|
||||
return 1
|
||||
|
||||
/datum/antagonist/technomancer/proc/equip_apprentice(var/mob/living/carbon/human/technomancer_mob)
|
||||
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/under/technomancer/apprentice(technomancer_mob), slot_w_uniform)
|
||||
create_id("Techno-apprentice", technomancer_mob)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/weapon/disposable_teleporter/free(technomancer_mob), slot_r_store)
|
||||
|
||||
var/obj/item/weapon/technomancer_catalog/apprentice/catalog = new /obj/item/weapon/technomancer_catalog/apprentice()
|
||||
catalog.bind_to_owner(technomancer_mob)
|
||||
technomancer_mob.equip_to_slot_or_del(catalog, slot_l_store)
|
||||
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/device/radio/headset(technomancer_mob), slot_l_ear)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/weapon/technomancer_core(technomancer_mob), slot_back)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/device/flashlight(technomancer_mob), slot_belt)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(technomancer_mob), slot_shoes)
|
||||
technomancer_mob.equip_to_slot_or_del(new /obj/item/clothing/head/technomancer/apprentice(technomancer_mob), slot_head)
|
||||
technomancer_mob.update_icons()
|
||||
return 1
|
||||
|
||||
/datum/antagonist/technomancer/check_victory()
|
||||
var/survivor
|
||||
for(var/datum/mind/player in current_antagonists)
|
||||
if(!player.current || player.current.stat == DEAD)
|
||||
continue
|
||||
survivor = 1
|
||||
break
|
||||
if(!survivor)
|
||||
feedback_set_details("round_end_result","loss - technomancer killed")
|
||||
world << "<span class='danger'><font size = 3>The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been \
|
||||
killed!</font></span>"
|
||||
65
code/game/antagonist/outsider/trader.dm
Normal file
65
code/game/antagonist/outsider/trader.dm
Normal file
@@ -0,0 +1,65 @@
|
||||
var/datum/antagonist/trader/traders
|
||||
|
||||
/datum/antagonist/trader
|
||||
id = MODE_TRADE
|
||||
role_type = BE_OPERATIVE
|
||||
role_text = "Trader"
|
||||
role_text_plural = "Traders"
|
||||
welcome_text = "As a crewmember of the Beruang, you answer to your captain and international laws of space."
|
||||
antag_text = "You are an <b>non-antagonist</b> visitor! Within the rules, \
|
||||
try to provide interesting interaction for the crew. \
|
||||
Try to make sure other players have <i>fun</i>! If you are confused or at a loss, always adminhelp, \
|
||||
and before taking extreme actions, please try to also contact the administration! \
|
||||
Think through your actions and make the roleplay immersive! <b>Please remember all \
|
||||
rules apply to you.</b>"
|
||||
leader_welcome_text = "As Captain of the Beruang, you have control over your crew and cargo. It may be worth briefly discussing a consistent shared backstory with your crew."
|
||||
landmark_id = "Trader"
|
||||
|
||||
id_type = /obj/item/weapon/card/id/external
|
||||
|
||||
flags = ANTAG_OVERRIDE_JOB | ANTAG_SET_APPEARANCE | ANTAG_HAS_LEADER | ANTAG_CHOOSE_NAME
|
||||
|
||||
hard_cap = 5
|
||||
hard_cap_round = 7
|
||||
initial_spawn_req = 5
|
||||
initial_spawn_target = 7
|
||||
|
||||
/datum/antagonist/trader/create_default(var/mob/source)
|
||||
var/mob/living/carbon/human/M = ..()
|
||||
if(istype(M)) M.age = rand(25,45)
|
||||
|
||||
/datum/antagonist/trader/New()
|
||||
..()
|
||||
traders = src
|
||||
|
||||
/datum/antagonist/trader/greet(var/datum/mind/player)
|
||||
if(!..())
|
||||
return
|
||||
player.current << "The Beruang is an independent cargo hauler, unless you decide otherwise. You're on your way to [station_name()]."
|
||||
player.current << "You may want to discuss a collective story with the rest of your crew. More members may be joining, so don't move out straight away!"
|
||||
|
||||
/datum/antagonist/trader/equip(var/mob/living/carbon/human/player)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/under/rank/cargotech(src), slot_w_uniform)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(src), slot_shoes)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/brown(src), slot_gloves)
|
||||
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses(src), slot_glasses)
|
||||
|
||||
create_radio(PUB_FREQ, player) //Assume they tune their headsets into the station's public radio as they approach
|
||||
|
||||
var/obj/item/weapon/card/id/id = create_id("Trader", player, equip = 0)
|
||||
id.name = "[player.real_name]'s Passport"
|
||||
id.assignment = "Trader"
|
||||
id.access |= access_trader
|
||||
var/obj/item/weapon/storage/wallet/W = new(player)
|
||||
W.handle_item_insertion(id)
|
||||
player.equip_to_slot_or_del(W, slot_wear_id)
|
||||
spawn_money(rand(50,150)*10,W)
|
||||
|
||||
return 1
|
||||
|
||||
/datum/antagonist/trader/update_access(var/mob/living/player)
|
||||
for(var/obj/item/weapon/storage/wallet/W in player.contents)
|
||||
for(var/obj/item/weapon/card/id/id in W.contents)
|
||||
id.name = "[player.real_name]'s Passport"
|
||||
id.registered_name = player.real_name
|
||||
W.name = "[initial(W.name)] ([id.name])"
|
||||
@@ -230,6 +230,11 @@ its easier to just keep the beam vertical.
|
||||
/atom/proc/melt()
|
||||
return
|
||||
|
||||
// Previously this was defined both on /obj/ and /turf/ seperately. And that's bad.
|
||||
/atom/proc/update_icon()
|
||||
return
|
||||
|
||||
|
||||
/atom/proc/hitby(atom/movable/AM as mob|obj)
|
||||
if (density)
|
||||
AM.throwing = 0
|
||||
|
||||
@@ -79,6 +79,16 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
|
||||
if(lesser_form && !P.allowduringlesserform) continue
|
||||
if(!(P in src.verbs))
|
||||
src.verbs += P.verbpath
|
||||
if(P.make_hud_button)
|
||||
if(!src.ability_master)
|
||||
src.ability_master = new /obj/screen/movable/ability_master(src)
|
||||
src.ability_master.add_ling_ability(
|
||||
object_given = src,
|
||||
verb_given = P.verbpath,
|
||||
name_given = P.name,
|
||||
ability_icon_given = P.ability_icon_state,
|
||||
arguments = list()
|
||||
)
|
||||
|
||||
for(var/language in languages)
|
||||
mind.changeling.absorbed_languages |= language
|
||||
@@ -96,6 +106,9 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
|
||||
for(var/datum/power/changeling/P in mind.changeling.purchased_powers)
|
||||
if(P.isVerb)
|
||||
verbs -= P.verbpath
|
||||
var/obj/screen/ability/verb_based/changeling/C = ability_master.get_ability_by_proc_ref(P.verbpath)
|
||||
if(C)
|
||||
ability_master.remove_ability(C)
|
||||
|
||||
|
||||
//Helper proc. Does all the checks and stuff for us to avoid copypasta
|
||||
|
||||
@@ -12,6 +12,8 @@ var/list/datum/power/changeling/powerinstances = list()
|
||||
var/enhancedtext = ""
|
||||
var/isVerb = 1 // Is it an active power, or passive?
|
||||
var/verbpath // Path to a verb that contains the effects.
|
||||
var/make_hud_button = 1 // Is this ability significant enough to dedicate screen space for a HUD button?
|
||||
var/ability_icon_state = null // icon_state for icons for the ability HUD. Must be in screen_spells.dmi.
|
||||
|
||||
/datum/power/changeling
|
||||
var/allowduringlesserform = 0
|
||||
@@ -340,6 +342,17 @@ var/list/datum/power/changeling/powerinstances = list()
|
||||
if(Thepower.genomecost > 0)
|
||||
purchased_powers_history.Add("[Pname] ([Thepower.genomecost] points)")
|
||||
|
||||
if(Thepower.make_hud_button && Thepower.isVerb)
|
||||
if(!M.current.ability_master)
|
||||
M.current.ability_master = new /obj/screen/movable/ability_master(M.current)
|
||||
M.current.ability_master.add_ling_ability(
|
||||
object_given = M.current,
|
||||
verb_given = Thepower.verbpath,
|
||||
name_given = Thepower.name,
|
||||
ability_icon_given = Thepower.ability_icon_state,
|
||||
arguments = list()
|
||||
)
|
||||
|
||||
if(!Thepower.isVerb && Thepower.verbpath)
|
||||
call(M.current, Thepower.verbpath)()
|
||||
else if(remake_verbs)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
force = 40
|
||||
sharp = 1
|
||||
edge = 1
|
||||
pry = 1
|
||||
anchored = 1
|
||||
throwforce = 0 //Just to be on the safe side
|
||||
throw_range = 0
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
desc = "We can channel a DNA into the airwaves, allowing our fellow changelings to absorb it and transform into it as if they acquired the DNA themselves."
|
||||
helptext = "Allows other changelings to absorb the DNA you channel from the airwaves. Will not help them towards their absorb objectives."
|
||||
genomecost = 0
|
||||
make_hud_button = 0
|
||||
verbpath = /mob/proc/changeling_hiveupload
|
||||
|
||||
/datum/power/changeling/hive_download
|
||||
@@ -12,6 +13,7 @@
|
||||
desc = "We can absorb a single DNA from the airwaves, allowing us to use more disguises with help from our fellow changelings."
|
||||
helptext = "Allows you to absorb a single DNA and use it. Does not count towards your absorb objective."
|
||||
genomecost = 0
|
||||
make_hud_button = 0
|
||||
verbpath = /mob/proc/changeling_hivedownload
|
||||
|
||||
// HIVE MIND UPLOAD/DOWNLOAD DNA
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
C.sdisabilities = 0
|
||||
C.disabilities = 0
|
||||
C.reagents.clear_reagents()
|
||||
C.ingested.clear_reagents()
|
||||
|
||||
var/heal_amount = 5
|
||||
if(src.mind.changeling.recursive_enhancement)
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
C.mind.changeling.purchased_powers -= C
|
||||
feedback_add_details("changeling_powers","CR")
|
||||
C.stat = CONSCIOUS
|
||||
C.timeofdeath = null
|
||||
src.verbs -= /mob/proc/changeling_revive
|
||||
// re-add our changeling powers
|
||||
C.make_changeling()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/game_mode/conflux
|
||||
name = "Wizard & Cult"
|
||||
name = "Technomancer & Cult"
|
||||
round_description = "A space wizard and a cult have invaded the station!"
|
||||
extended_round_description = "Cultists and wizards spawn during this round."
|
||||
config_tag = "conflux"
|
||||
@@ -7,6 +7,6 @@
|
||||
required_players_secret = 15
|
||||
required_enemies = 5
|
||||
end_on_antag_death = 1
|
||||
antag_tags = list(MODE_WIZARD, MODE_CULTIST)
|
||||
antag_tags = list(MODE_TECHNOMANCER, MODE_CULTIST)
|
||||
require_all_templates = 1
|
||||
votable = 0
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/game_mode/lizard
|
||||
name = "Wizard & Changelings"
|
||||
name = "Technomancer & Changelings"
|
||||
round_description = "A space wizard and changelings have invaded the station!"
|
||||
extended_round_description = "Changelings and a wizard spawn during this round."
|
||||
config_tag = "lizard"
|
||||
@@ -7,5 +7,6 @@
|
||||
required_players_secret = 8
|
||||
required_enemies = 3
|
||||
end_on_antag_death = 0
|
||||
antag_tags = list(MODE_WIZARD, MODE_CHANGELING)
|
||||
antag_tags = list(MODE_TECHNOMANCER, MODE_CHANGELING)
|
||||
require_all_templates = 1
|
||||
votable = 0
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/game_mode/mercwiz
|
||||
name = "Mercenaries & Wizard"
|
||||
name = "Mercenaries & Technomancer"
|
||||
round_description = "A mercenary team and a wizard have invaded the station!"
|
||||
extended_round_description = "Mercenaries and wizard spawn during this round."
|
||||
config_tag = "mercwiz"
|
||||
@@ -7,5 +7,6 @@
|
||||
required_players_secret = 15 //I don't think we can have it lower and not need an ERT every round.
|
||||
required_enemies = 7
|
||||
end_on_antag_death = 0
|
||||
antag_tags = list(MODE_MERCENARY, MODE_WIZARD)
|
||||
antag_tags = list(MODE_MERCENARY, MODE_TECHNOMANCER)
|
||||
require_all_templates = 1
|
||||
votable = 0
|
||||
@@ -1,5 +1,5 @@
|
||||
/datum/game_mode/visitors
|
||||
name = "Wizard & Ninja"
|
||||
name = "Technomancer & Ninja"
|
||||
round_description = "A space wizard and a ninja have invaded the station!"
|
||||
extended_round_description = "A ninja and wizard spawn during this round."
|
||||
config_tag = "visitors"
|
||||
@@ -7,5 +7,6 @@
|
||||
required_players_secret = 8
|
||||
required_enemies = 2
|
||||
end_on_antag_death = 0
|
||||
antag_tags = list(MODE_WIZARD, MODE_NINJA)
|
||||
antag_tags = list(MODE_TECHNOMANCER, MODE_NINJA)
|
||||
require_all_templates = 1
|
||||
votable = 0
|
||||
123
code/game/gamemodes/technomancer/assistance/assistance.dm
Normal file
123
code/game/gamemodes/technomancer/assistance/assistance.dm
Normal file
@@ -0,0 +1,123 @@
|
||||
/datum/technomancer/assistance
|
||||
var/one_use_only = 0
|
||||
|
||||
/datum/technomancer/assistance/apprentice
|
||||
name = "Friendly Apprentice"
|
||||
desc = "A one-time use teleporter that sends a less powerful manipulator of space to you, who will do their best to protect \
|
||||
and serve you. They get their own catalog and can buy spells for themselves, however they have a smaller pool to buy with. \
|
||||
If you are unable to receive an apprentice, the teleporter can be refunded like most equipment by sliding it into the \
|
||||
catalog. Note that apprentices cannot purchase more apprentices."
|
||||
cost = 300
|
||||
obj_path = /obj/item/weapon/antag_spawner/technomancer_apprentice
|
||||
|
||||
/obj/item/weapon/antag_spawner
|
||||
w_class = 1
|
||||
var/used = 0
|
||||
|
||||
/obj/item/weapon/antag_spawner/proc/spawn_antag(client/C, turf/T)
|
||||
return
|
||||
|
||||
/obj/item/weapon/antag_spawner/proc/equip_antag(mob/target)
|
||||
return
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice
|
||||
name = "apprentice teleporter"
|
||||
desc = "A teleportation device, which will bring a less potent manipulator of space to you."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "oldshieldoff"
|
||||
var/searching = 0
|
||||
var/datum/effect/effect/system/spark_spread/sparks
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/New()
|
||||
..()
|
||||
sparks = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
sparks.set_up(5, 0, src)
|
||||
sparks.attach(loc)
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/Destroy()
|
||||
qdel(sparks)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/attack_self(mob/user)
|
||||
user << "<span class='notice'>Teleporter attempting to lock on to your apprentice.</span>"
|
||||
searching = 1
|
||||
icon_state = "oldshieldon"
|
||||
for(var/mob/observer/dead/O in player_list)
|
||||
if(!O.MayRespawn())
|
||||
continue
|
||||
if(jobban_isbanned(O, "Syndicate") || jobban_isbanned(O, "wizard"))
|
||||
continue
|
||||
if(O.client)
|
||||
if(O.client.prefs.be_special & BE_WIZARD)
|
||||
question(O.client)
|
||||
spawn(1 MINUTE)
|
||||
searching = 0
|
||||
if(!used)
|
||||
icon_state = "oldshieldoff"
|
||||
user << "<span class='warning'>The teleporter failed to find your apprentice. Perhaps you could try again later?</span>"
|
||||
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/proc/question(var/client/C)
|
||||
spawn(0)
|
||||
if(!C)
|
||||
return
|
||||
var/response = alert(C, "Someone is requesting a Technomancer apprentice Would you like to play as one?",
|
||||
"Apprentice request","Yes", "No")
|
||||
if(response == "Yes")
|
||||
response = alert(C, "Are you sure you want to play as an apprentice?", "Apprentice request", "Yes", "No")
|
||||
if(!C || used || !searching)
|
||||
return
|
||||
if(response == "Yes")
|
||||
spawn_antag(C, get_turf(src))
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/spawn_antag(client/C, turf/T)
|
||||
sparks.start()
|
||||
var/mob/living/carbon/human/H = new/mob/living/carbon/human(T)
|
||||
C.prefs.copy_to(H)
|
||||
H.key = C.key
|
||||
|
||||
H << "<b>You are the Technomancer's apprentice! Your goal is to assist them in their mission at the [station_name()].</b>"
|
||||
H << "<b>Your service has not gone unrewarded, however. Studying under them, you have learned how to use a Manipulation Core \
|
||||
of your own. You also have a Catelog, to purchase your own functions and equipment as you see fit.</b>"
|
||||
H << "<b>It would be wise to speak to your master, and learn what their plans are for today.</b>"
|
||||
|
||||
spawn(1)
|
||||
technomancers.add_antagonist(H.mind, 0, 1, 0, 0, 0)
|
||||
equip_antag(H)
|
||||
used = 1
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/item/weapon/antag_spawner/technomancer_apprentice/equip_antag(mob/technomancer_mob)
|
||||
var/datum/antagonist/technomancer/antag_datum = all_antag_types[MODE_TECHNOMANCER]
|
||||
antag_datum.equip_apprentice(technomancer_mob)
|
||||
|
||||
|
||||
/*
|
||||
// For when no one wants to play support.
|
||||
/datum/technomancer/assistance/golem
|
||||
name = "Friendly GOLEM unit"
|
||||
desc = "Teleports a specially designed synthetic unit to you, which is very durable, has an advanced AI, and can also use \
|
||||
functions. It knows Shield, Targeted Blink, Beam, Flame Tongue, Mend Wounds, and Mend Burns. It also has a large storage \
|
||||
capacity for energy, and due to it's synthetic nature, instability is less of an issue for them."
|
||||
cost = 350
|
||||
obj_path = null //TODO
|
||||
one_use_only = 1
|
||||
|
||||
/datum/technomancer/assistance/ninja
|
||||
name = "Neutral Cyberassassin"
|
||||
desc = "Someone almost as enigmatic as you will also arrive at your destination, with their own goals and motivations. \
|
||||
This could prove to be a problem if they decide to go against you, so this is only recommended as a challenge."
|
||||
cost = 100
|
||||
obj_path = null //TODO
|
||||
one_use_only = 1
|
||||
|
||||
// Hardmode.
|
||||
/datum/technomancer/assistance/enemy_technomancer
|
||||
name = "Enemy Technomancer"
|
||||
desc = "Another manipulator of space will arrive on the colony in addition to you, most likely wanting to oppose you in \
|
||||
some form, if you purchase this. This is only recommended as a challenge."
|
||||
cost = 100
|
||||
obj_path = null //TODO
|
||||
one_use_only = 1
|
||||
*/
|
||||
72
code/game/gamemodes/technomancer/assistance/golem.dm
Normal file
72
code/game/gamemodes/technomancer/assistance/golem.dm
Normal file
@@ -0,0 +1,72 @@
|
||||
//An AI-controlled 'companion' for the Technomancer. It's tough, strong, and can also use spells.
|
||||
/mob/living/simple_animal/hostile/technomancer_golem
|
||||
name = "G.O.L.E.M."
|
||||
desc = "A rather unusual looking synthetic."
|
||||
icon = 'icons/mob/robots.dmi'
|
||||
icon_state = "Security"
|
||||
health = 250
|
||||
maxHealth = 250
|
||||
stop_automated_movement = 1
|
||||
wander = 0
|
||||
response_help = "pets"
|
||||
response_disarm = "pushes away"
|
||||
response_harm = "punches"
|
||||
harm_intent_damage = 3
|
||||
|
||||
heat_damage_per_tick = 0
|
||||
cold_damage_per_tick = 0
|
||||
|
||||
min_oxy = 0
|
||||
max_oxy = 0
|
||||
min_tox = 0
|
||||
max_tox = 0
|
||||
min_co2 = 0
|
||||
max_co2 = 0
|
||||
min_n2 = 0
|
||||
max_n2 = 0
|
||||
unsuitable_atoms_damage = 0
|
||||
speed = 0
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
attacktext = "pummeled"
|
||||
attack_sound = null
|
||||
friendly = "hugs"
|
||||
resistance = 0
|
||||
|
||||
var/obj/item/weapon/technomancer_core/core = null
|
||||
var/obj/item/weapon/spell/active_spell = null
|
||||
var/mob/living/master = null
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/New()
|
||||
..()
|
||||
core = new core(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/Destroy()
|
||||
qdel(core)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/proc/bind_to_mob(mob/user)
|
||||
if(!user || master)
|
||||
return
|
||||
master = user
|
||||
name = "[master]'s [initial(name)]"
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/examine(mob/user)
|
||||
..()
|
||||
if(user.mind && technomancers.is_antagonist(user.mind))
|
||||
user << "Your pride and joy. It's a very special synthetic robot, capable of using functions similar to you, and you built it \
|
||||
yourself! It'll always stand by your side, ready to help you out. You have no idea what GOLEM stands for, however..."
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/Life()
|
||||
handle_ai()
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/proc/handle_ai()
|
||||
if(!master)
|
||||
return
|
||||
if(get_dist(src, master) > 6 || src.z != master.z)
|
||||
recall_to_master()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/technomancer_golem/proc/recall_to_master()
|
||||
return
|
||||
271
code/game/gamemodes/technomancer/catalog.dm
Normal file
271
code/game/gamemodes/technomancer/catalog.dm
Normal file
@@ -0,0 +1,271 @@
|
||||
var/list/all_technomancer_spells = typesof(/datum/technomancer/spell) - /datum/technomancer/spell
|
||||
var/list/all_technomancer_equipment = typesof(/datum/technomancer/equipment) - /datum/technomancer/equipment
|
||||
var/list/all_technomancer_consumables = typesof(/datum/technomancer/consumable) - /datum/technomancer/consumable
|
||||
var/list/all_technomancer_assistance = typesof(/datum/technomancer/assistance) - /datum/technomancer/assistance
|
||||
var/list/all_technomancer_presets = typesof(/datum/technomancer/presets) - /datum/technomancer/presets
|
||||
|
||||
/datum/technomancer
|
||||
var/name = "technomancer thing"
|
||||
var/desc = "If you can see this, something broke."
|
||||
var/enhancement_desc = "No effect."
|
||||
var/cost = 100
|
||||
var/hidden = 0
|
||||
var/obj_path = null
|
||||
var/ability_icon_state = null
|
||||
|
||||
/obj/item/weapon/technomancer_catalog
|
||||
name = "catalog"
|
||||
desc = "A \"book\" featuring a holographic display, metal cover, and miniaturized teleportation device, allowing the user to \
|
||||
requisition various things from.. where ever they came from."
|
||||
icon = 'icons/obj/storage.dmi'
|
||||
icon_state ="scientology" //placeholder
|
||||
w_class = 2
|
||||
var/budget = 1000
|
||||
var/max_budget = 1000
|
||||
var/mob/living/carbon/human/owner = null
|
||||
var/list/spell_instances = list()
|
||||
var/list/equipment_instances = list()
|
||||
var/list/consumable_instances = list()
|
||||
var/list/assistance_instances = list()
|
||||
var/list/preset_instances = list()
|
||||
var/tab = 0
|
||||
var/show_scepter_text = 0
|
||||
|
||||
/obj/item/weapon/technomancer_catalog/apprentice
|
||||
name = "apprentice's catelog"
|
||||
budget = 700
|
||||
max_budget = 700
|
||||
|
||||
/obj/item/weapon/technomancer_catalog/master //for badmins, I suppose
|
||||
name = "master's catelog"
|
||||
budget = 2000
|
||||
max_budget = 2000
|
||||
|
||||
|
||||
// Proc: bind_to_owner()
|
||||
// Parameters: 1 (new_owner - mob that the book is trying to bind to)
|
||||
// Description: Links the catelog to hopefully the technomancer, so that only they can access it.
|
||||
/obj/item/weapon/technomancer_catalog/proc/bind_to_owner(var/mob/living/carbon/human/new_owner)
|
||||
if(!owner && technomancers.is_antagonist(new_owner.mind))
|
||||
owner = new_owner
|
||||
|
||||
// Proc: New()
|
||||
// Parameters: 0
|
||||
// Description: Sets up the catelog, as shown below.
|
||||
/obj/item/weapon/technomancer_catalog/New()
|
||||
..()
|
||||
set_up()
|
||||
|
||||
// Proc: set_up()
|
||||
// Parameters: 0
|
||||
// Description: Instantiates all the catelog datums for everything that can be bought.
|
||||
/obj/item/weapon/technomancer_catalog/proc/set_up()
|
||||
if(!spell_instances.len)
|
||||
for(var/S in all_technomancer_spells)
|
||||
spell_instances += new S()
|
||||
if(!equipment_instances.len)
|
||||
for(var/E in all_technomancer_equipment)
|
||||
equipment_instances += new E()
|
||||
if(!consumable_instances.len)
|
||||
for(var/C in all_technomancer_consumables)
|
||||
consumable_instances += new C()
|
||||
if(!assistance_instances.len)
|
||||
for(var/A in all_technomancer_assistance)
|
||||
assistance_instances += new A()
|
||||
if(!preset_instances.len)
|
||||
for(var/P in all_technomancer_presets)
|
||||
preset_instances += new P()
|
||||
|
||||
/obj/item/weapon/technomancer_catalog/apprentice/set_up()
|
||||
..()
|
||||
for(var/datum/technomancer/assistance/apprentice/A in assistance_instances)
|
||||
assistance_instances.Remove(A)
|
||||
|
||||
// Proc: attack_self()
|
||||
// Parameters: 1 (user - the mob clicking on the catelog)
|
||||
// Description: Shows an HTML window, to buy equipment and spells, if the user is the legitimate owner. Otherwise it cannot be used.
|
||||
/obj/item/weapon/technomancer_catalog/attack_self(mob/user)
|
||||
if(!user)
|
||||
return 0
|
||||
if(owner && user != owner)
|
||||
user << "<span class='danger'>\The [src] knows that you're not the original owner, and has locked you out of it!</span>"
|
||||
return 0
|
||||
else if(!owner)
|
||||
bind_to_owner(user)
|
||||
|
||||
switch(tab)
|
||||
if(0) //Functions
|
||||
var/dat = ""
|
||||
user.set_machine(src)
|
||||
dat += "<align='center'><b>Functions</b> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=1'>Equipment</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=2'>Consumables</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=3'>Assistance</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=4'>Presets</a></align><br>"
|
||||
dat += "You currently have a budget of <b>[budget]/[max_budget]</b>.<br><br>"
|
||||
dat += "<a href='byond://?src=\ref[src];refund_functions=1'>Refund Functions</a><br><br>"
|
||||
for(var/datum/technomancer/spell in spell_instances)
|
||||
if(spell.hidden)
|
||||
continue
|
||||
dat += "<b>[spell.name]</b><br>"
|
||||
dat += "<i>[spell.desc]</i><br>"
|
||||
if(show_scepter_text)
|
||||
dat += "<span class='info'><i>[spell.enhancement_desc]</i></span>"
|
||||
if(spell.cost <= budget)
|
||||
dat += "<a href='byond://?src=\ref[src];spell_choice=[spell.name]'>Purchase</a> ([spell.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(1) //Equipment
|
||||
var/dat = ""
|
||||
user.set_machine(src)
|
||||
dat += "<align='center'><a href='byond://?src=\ref[src];tab_choice=0'>Functions</a> | "
|
||||
dat += "<b>Equipment</b> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=2'>Consumables</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=3'>Assistance</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=4'>Presets</a></align><br>"
|
||||
dat += "You currently have a budget of <b>[budget]/[max_budget]</b>.<br><br>"
|
||||
for(var/datum/technomancer/equipment/E in equipment_instances)
|
||||
dat += "<b>[E.name]</b><br>"
|
||||
dat += "<i>[E.desc]</i><br>"
|
||||
if(E.cost <= budget)
|
||||
dat += "<a href='byond://?src=\ref[src];item_choice=[E.name]'>Purchase</a> ([E.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(2) //Consumables
|
||||
var/dat = ""
|
||||
user.set_machine(src)
|
||||
dat += "<align='center'><a href='byond://?src=\ref[src];tab_choice=0'>Functions</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=1'>Equipment</a> | "
|
||||
dat += "<b>Consumables</b> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=3'>Assistance</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=4'>Presets</a></align><br>"
|
||||
dat += "You currently have a budget of <b>[budget]/[max_budget]</b>.<br><br>"
|
||||
for(var/datum/technomancer/consumable/C in consumable_instances)
|
||||
dat += "<b>[C.name]</b><br>"
|
||||
dat += "<i>[C.desc]</i><br>"
|
||||
if(C.cost <= budget)
|
||||
dat += "<a href='byond://?src=\ref[src];item_choice=[C.name]'>Purchase</a> ([C.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(3) //Assistance
|
||||
var/dat = ""
|
||||
user.set_machine(src)
|
||||
dat += "<align='center'><a href='byond://?src=\ref[src];tab_choice=0'>Functions</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=1'>Equipment</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=2'>Consumables</a> | "
|
||||
dat += "<b>Assistance</b> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=4'>Presets</a></align><br>"
|
||||
dat += "You currently have a budget of <b>[budget]/[max_budget]</b>.<br><br>"
|
||||
for(var/datum/technomancer/assistance/A in assistance_instances)
|
||||
dat += "<b>[A.name]</b><br>"
|
||||
dat += "<i>[A.desc]</i><br>"
|
||||
if(A.cost <= budget)
|
||||
dat += "<a href='byond://?src=\ref[src];item_choice=[A.name]'>Purchase</a> ([A.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(4) //Presets
|
||||
var/dat = ""
|
||||
user.set_machine(src)
|
||||
dat += "<align='center'><a href='byond://?src=\ref[src];tab_choice=0'>Functions</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=1'>Equipment</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=2'>Consumables</a> | "
|
||||
dat += "<a href='byond://?src=\ref[src];tab_choice=3'>Assistance</a> | "
|
||||
dat += "<b>Presets</b></align><br>"
|
||||
dat += "You currently have a budget of <b>[budget]/[max_budget]</b>.<br><br>"
|
||||
for(var/datum/technomancer/presets/P in preset_instances)
|
||||
dat += "<b>[P.name]</b><br>"
|
||||
dat += "<i>[P.desc]</i><br>"
|
||||
if(P.cost <= budget)
|
||||
dat += "<a href='byond://?src=\ref[src];spell_choice=[P.name]'>Purchase</a> ([P.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
onclose(user, "radio")
|
||||
|
||||
// Proc: Topic()
|
||||
// Parameters: 2 (href - don't know, href_list - the choice that the person using the interface above clicked on.)
|
||||
// Description: Acts upon clicks on links for the catelog, if they are the rightful owner.
|
||||
/obj/item/weapon/technomancer_catalog/Topic(href, href_list)
|
||||
..()
|
||||
var/mob/living/carbon/human/H = usr
|
||||
|
||||
if(H.stat || H.restrained())
|
||||
return
|
||||
if(!istype(H, /mob/living/carbon/human))
|
||||
return 1 //why does this return 1?
|
||||
|
||||
if(H != owner)
|
||||
H << "\The [src] won't allow you to do that, as you don't own \the [src]!"
|
||||
return
|
||||
|
||||
if(loc == H || (in_range(src, H) && istype(loc, /turf)))
|
||||
H.set_machine(src)
|
||||
if(href_list["tab_choice"])
|
||||
tab = text2num(href_list["tab_choice"])
|
||||
if(href_list["spell_choice"])
|
||||
var/datum/technomancer/new_spell = null
|
||||
//Locate the spell.
|
||||
for(var/datum/technomancer/spell/spell in spell_instances)
|
||||
if(spell.name == href_list["spell_choice"])
|
||||
new_spell = spell
|
||||
break
|
||||
|
||||
var/obj/item/weapon/technomancer_core/core = null
|
||||
if(istype(H.back, /obj/item/weapon/technomancer_core))
|
||||
core = H.back
|
||||
|
||||
if(new_spell && core)
|
||||
if(new_spell.cost <= budget)
|
||||
if(!core.has_spell(new_spell))
|
||||
budget -= new_spell.cost
|
||||
H << "<span class='notice'>You have just bought [new_spell.name].</span>"
|
||||
core.add_spell(new_spell.obj_path, new_spell.name, new_spell.ability_icon_state)
|
||||
else //We already own it.
|
||||
H << "<span class='danger'>You already have [new_spell.name]!</span>"
|
||||
return
|
||||
else //Can't afford.
|
||||
H << "<span class='danger'>You can't afford that!</span>"
|
||||
return
|
||||
|
||||
// This needs less copypasta.
|
||||
if(href_list["item_choice"])
|
||||
var/datum/technomancer/desired_object = null
|
||||
for(var/datum/technomancer/O in equipment_instances + consumable_instances + assistance_instances)
|
||||
if(O.name == href_list["item_choice"])
|
||||
desired_object = O
|
||||
break
|
||||
|
||||
if(desired_object)
|
||||
if(desired_object.cost <= budget)
|
||||
budget -= desired_object.cost
|
||||
H << "<span class='notice'>You have just bought \a [desired_object.name].</span>"
|
||||
new desired_object.obj_path(get_turf(H))
|
||||
|
||||
else //Can't afford.
|
||||
H << "<span class='danger'>You can't afford that!</span>"
|
||||
return
|
||||
|
||||
|
||||
if(href_list["refund_functions"])
|
||||
if(H.z != 2)
|
||||
H << "<span class='danger'>You can only refund at your base, it's too late now!</span>"
|
||||
return
|
||||
var/obj/item/weapon/technomancer_core/core = null
|
||||
if(istype(H.back, /obj/item/weapon/technomancer_core))
|
||||
core = H.back
|
||||
if(core)
|
||||
for(var/obj/spellbutton/spell in core.spells)
|
||||
for(var/datum/technomancer/spell/spell_datum in spell_instances)
|
||||
if(spell_datum.obj_path == spell.spellpath && !spell.was_bought_by_preset)
|
||||
budget += spell_datum.cost
|
||||
core.remove_spell(spell)
|
||||
break
|
||||
attack_self(H)
|
||||
42
code/game/gamemodes/technomancer/clothing.dm
Normal file
42
code/game/gamemodes/technomancer/clothing.dm
Normal file
@@ -0,0 +1,42 @@
|
||||
/obj/item/clothing/suit/technomancer
|
||||
name = "chrome manipulation suit"
|
||||
desc = "It's a very shiny and somewhat protective suit, built to help carry cores on the user's back."
|
||||
icon_state = "technomancer_suit"
|
||||
body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS|FEET|HANDS
|
||||
allowed = list(/obj/item/weapon/tank)
|
||||
armor = list(melee = 50, bullet = 20, laser = 30, energy = 30, bomb = 10, bio = 0, rad = 40)
|
||||
siemens_coefficient = 0.75
|
||||
|
||||
/obj/item/clothing/under/technomancer
|
||||
name = "initiate's jumpsuit"
|
||||
desc = "It's a blue colored jumpsuit. There appears to be light-weight armor padding underneath, providing some protection."
|
||||
icon_state = "initiate"
|
||||
armor = list(melee = 10, bullet = 5, laser = 5, energy = 0, bomb = 0, bio = 0, rad = 0)
|
||||
siemens_coefficient = 0.9
|
||||
|
||||
/obj/item/clothing/under/technomancer/apprentice
|
||||
name = "apprentice's jumpsuit"
|
||||
desc = "It's a blue colored jumpsuit with some silver markings. There appears to be light-weight armor padding \
|
||||
underneath, providing some protection."
|
||||
icon_state = "apprentice"
|
||||
|
||||
/obj/item/clothing/under/technomancer/master
|
||||
name = "master's jumpsuit"
|
||||
desc = "It's a blue colored jumpsuit with some gold markings. There appears to be light-weight armor padding \
|
||||
underneath, providing some protection."
|
||||
icon_state = "technomancer"
|
||||
|
||||
/obj/item/clothing/head/technomancer
|
||||
name = "initiate's hat"
|
||||
desc = "It's a somewhat silly looking blue pointed hat."
|
||||
icon_state = "initiate"
|
||||
|
||||
/obj/item/clothing/head/technomancer/apprentice
|
||||
name = "apprentice's hat"
|
||||
desc = "It's a somewhat silly looking blue pointed hat. This one has a silver colored metalic feather strapped to it."
|
||||
icon_state = "apprentice"
|
||||
|
||||
/obj/item/clothing/head/technomancer/master
|
||||
name = "master's hat"
|
||||
desc = "It's a somewhat silly looking blue pointed hat. This one has a gold colored metalic feather strapped to it."
|
||||
icon_state = "technomancer"
|
||||
282
code/game/gamemodes/technomancer/core_obj.dm
Normal file
282
code/game/gamemodes/technomancer/core_obj.dm
Normal file
@@ -0,0 +1,282 @@
|
||||
//The base core object, worn on the wizard's back.
|
||||
/obj/item/weapon/technomancer_core
|
||||
name = "manipulation core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats."
|
||||
icon = 'icons/obj/technomancer.dmi'
|
||||
icon_state = "technomancer_core"
|
||||
item_state = "technomancer_core"
|
||||
w_class = 5
|
||||
slot_flags = SLOT_BACK
|
||||
unacidable = 1
|
||||
origin_tech = list(
|
||||
TECH_MATERIAL = 8, TECH_ENGINEERING = 8, TECH_POWER = 8, TECH_BLUESPACE = 10,
|
||||
TECH_COMBAT = 7, TECH_MAGNET = 9, TECH_DATA = 5
|
||||
)
|
||||
sprite_sheets = list(
|
||||
"Teshari" = 'icons/mob/species/seromi/back.dmi'
|
||||
)
|
||||
var/energy = 10000
|
||||
var/max_energy = 10000
|
||||
var/regen_rate = 50 // 200 seconds to full
|
||||
var/energy_delta = 0 // How much we're gaining (or perhaps losing) every process().
|
||||
var/mob/living/wearer = null // Reference to the mob wearing the core.
|
||||
var/instability_modifer = 1.0 // Multiplier on how much instability is added.
|
||||
var/list/spells = list() // This contains the buttons used to make spells in the user's hand.
|
||||
var/list/appearances = list( // Assoc list containing possible icon_states that the wiz can change the core to.
|
||||
"default" = "technomancer_core",
|
||||
"wizard's cloak" = "wizard_cloak"
|
||||
)
|
||||
|
||||
// Some spell-specific variables go here, since spells themselves are temporary. Cores are more long term and more accessable than \
|
||||
// mind datums. It may also allow creative players to try to pull off a 'soul jar' scenario.
|
||||
var/list/summoned_mobs = list() // Maintained horribly with maintain_summon_list().
|
||||
var/list/wards_in_use = list() // Wards don't count against the cap for other summons.
|
||||
var/max_summons = 10 // Maximum allowed summoned entities. Some cores will have different caps.
|
||||
|
||||
/obj/item/weapon/technomancer_core/New()
|
||||
..()
|
||||
processing_objects |= src
|
||||
|
||||
/obj/item/weapon/technomancer_core/Destroy()
|
||||
dismiss_all_summons()
|
||||
processing_objects.Remove(src)
|
||||
..()
|
||||
|
||||
// Add the spell buttons to the HUD.
|
||||
/obj/item/weapon/technomancer_core/equipped(mob/user)
|
||||
wearer = user
|
||||
for(var/obj/spellbutton/spell in spells)
|
||||
wearer.ability_master.add_technomancer_ability(spell, spell.ability_icon_state)
|
||||
..()
|
||||
|
||||
// Removes the spell buttons from the HUD.
|
||||
/obj/item/weapon/technomancer_core/dropped(mob/user)
|
||||
for(var/obj/screen/ability/obj_based/technomancer/A in wearer.ability_master.ability_objects)
|
||||
wearer.ability_master.remove_ability(A)
|
||||
wearer = null
|
||||
..()
|
||||
|
||||
// 'pay_energy' is too vague of a name for a proc at the mob level.
|
||||
/mob/proc/technomancer_pay_energy(amount)
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/technomancer_pay_energy(amount)
|
||||
if(istype(back, /obj/item/weapon/technomancer_core))
|
||||
var/obj/item/weapon/technomancer_core/TC = back
|
||||
return TC.pay_energy(amount)
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/pay_energy(amount)
|
||||
if(amount <= energy)
|
||||
energy = max(energy - amount, 0)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/give_energy(amount)
|
||||
energy = min(energy + amount, max_energy)
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/technomancer_core/process()
|
||||
var/old_energy = energy
|
||||
regenerate()
|
||||
pay_dues()
|
||||
energy_delta = energy - old_energy
|
||||
if(world.time % 5 == 0) // Maintaining fat lists is expensive, I imagine.
|
||||
maintain_summon_list()
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/regenerate()
|
||||
energy = min(max(energy + regen_rate, 0), max_energy)
|
||||
if(wearer && ishuman(wearer))
|
||||
var/mob/living/carbon/human/H = wearer
|
||||
H.wiz_energy_update_hud()
|
||||
|
||||
// We pay for on-going effects here.
|
||||
/obj/item/weapon/technomancer_core/proc/pay_dues()
|
||||
if(summoned_mobs.len)
|
||||
pay_energy( round(summoned_mobs.len * 5) )
|
||||
|
||||
// Because sometimes our summoned mobs will stop existing and leave a null entry in the list, we need to do cleanup every
|
||||
// so often so .len remains reliable.
|
||||
/obj/item/weapon/technomancer_core/proc/maintain_summon_list()
|
||||
if(!summoned_mobs.len) // No point doing work if there's no work to do.
|
||||
return
|
||||
for(var/A in summoned_mobs)
|
||||
// First, a null check.
|
||||
if(isnull(A))
|
||||
summoned_mobs -= A
|
||||
continue
|
||||
// Now check for dead mobs who shouldn't be on the list.
|
||||
if(istype(A, /mob/living))
|
||||
var/mob/living/L = A
|
||||
if(L.stat == DEAD)
|
||||
summoned_mobs -= L
|
||||
qdel(L)
|
||||
|
||||
// Deletes all the summons and wards from the core, so that Destroy() won't have issues.
|
||||
/obj/item/weapon/technomancer_core/proc/dismiss_all_summons()
|
||||
for(var/mob/living/L in summoned_mobs)
|
||||
summoned_mobs -= L
|
||||
qdel(L)
|
||||
for(var/mob/living/simple_animal/ward/ward in wards_in_use)
|
||||
wards_in_use -= ward
|
||||
qdel(ward)
|
||||
|
||||
// This is what is clicked on to place a spell in the user's hands.
|
||||
/obj/spellbutton
|
||||
name = "generic spellbutton"
|
||||
var/spellpath = null
|
||||
var/obj/item/weapon/technomancer_core/core = null
|
||||
var/was_bought_by_preset = 0 // Relevant for refunding via the spellbook. Presets may be priced differently.
|
||||
var/ability_icon_state = null
|
||||
|
||||
/obj/spellbutton/New(loc, var/path, var/new_name, var/new_icon_state)
|
||||
if(!path || !ispath(path))
|
||||
message_admins("ERROR: /obj/spellbutton/New() was not given a proper path!")
|
||||
qdel(src)
|
||||
src.name = new_name
|
||||
src.spellpath = path
|
||||
src.loc = loc
|
||||
src.core = loc
|
||||
src.ability_icon_state = new_icon_state
|
||||
|
||||
/obj/spellbutton/Click()
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
H.place_spell_in_hand(spellpath)
|
||||
|
||||
/obj/spellbutton/DblClick()
|
||||
return Click()
|
||||
|
||||
/mob/living/carbon/human/Stat()
|
||||
. = ..()
|
||||
|
||||
if(. && istype(back,/obj/item/weapon/technomancer_core))
|
||||
var/obj/item/weapon/technomancer_core/core = back
|
||||
setup_technomancer_stat(core)
|
||||
|
||||
/mob/living/carbon/human/proc/setup_technomancer_stat(var/obj/item/weapon/technomancer_core/core)
|
||||
if(core && statpanel("Spell Core"))
|
||||
var/charge_status = "[core.energy]/[core.max_energy] ([round( (core.energy / core.max_energy) * 100)]%) \
|
||||
([round(core.energy_delta)]/s)"
|
||||
var/instability_status = "[src.instability]"
|
||||
stat("Core charge", charge_status)
|
||||
stat("User instability", instability_status)
|
||||
for(var/obj/spellbutton/button in core.spells)
|
||||
stat(button)
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/add_spell(var/path, var/new_name, var/ability_icon_state)
|
||||
if(!path || !ispath(path))
|
||||
message_admins("ERROR: /obj/item/weapon/technomancer_core/add_spell() was not given a proper path! \
|
||||
The path supplied was [path].")
|
||||
return
|
||||
var/obj/spellbutton/spell = new(src, path, new_name, ability_icon_state)
|
||||
spells.Add(spell)
|
||||
if(wearer)
|
||||
wearer.ability_master.add_technomancer_ability(spell, ability_icon_state)
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/remove_spell(var/obj/spellbutton/spell_to_remove)
|
||||
if(spell_to_remove in spells)
|
||||
spells.Remove(spell_to_remove)
|
||||
if(wearer)
|
||||
var/obj/screen/ability/obj_based/technomancer/A = wearer.ability_master.get_ability_by_instance(spell_to_remove)
|
||||
if(A)
|
||||
wearer.ability_master.remove_ability(A)
|
||||
qdel(spell_to_remove)
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/remove_all_spells()
|
||||
for(var/obj/spellbutton/spell in spells)
|
||||
spells.Remove(spell)
|
||||
qdel(spell)
|
||||
|
||||
/obj/item/weapon/technomancer_core/proc/has_spell(var/datum/technomancer/spell_to_check)
|
||||
for(var/obj/spellbutton/spell in spells)
|
||||
if(spell.spellpath == spell_to_check.obj_path)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/proc/wiz_energy_update_hud()
|
||||
if(client && hud_used)
|
||||
if(istype(back, /obj/item/weapon/technomancer_core)) //I reckon there's a better way of doing this.
|
||||
var/obj/item/weapon/technomancer_core/core = back
|
||||
wiz_energy_display.invisibility = 0
|
||||
var/ratio = core.energy / core.max_energy
|
||||
ratio = max(round(ratio, 0.05) * 100, 5)
|
||||
wiz_energy_display.icon_state = "wiz_energy[ratio]"
|
||||
else
|
||||
wiz_energy_display.invisibility = 101
|
||||
|
||||
//Resonance Aperture
|
||||
|
||||
//Variants which the wizard can buy.
|
||||
|
||||
//High risk, high reward core.
|
||||
/obj/item/weapon/technomancer_core/unstable
|
||||
name = "unstable core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. This one is rather unstable, \
|
||||
and could prove dangerous to the user, as it feeds off unstable energies that can occur with overuse of this machine."
|
||||
energy = 13000
|
||||
max_energy = 13000
|
||||
regen_rate = 35 //~371 seconds to full, 118 seconds to full at 50 instability (rate of 110)
|
||||
|
||||
/obj/item/weapon/technomancer_core/unstable/regenerate()
|
||||
var/instability_bonus = 0
|
||||
if(loc && ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
instability_bonus = H.instability * 1.5
|
||||
energy = min(energy + regen_rate + instability_bonus, max_energy)
|
||||
if(loc && ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
H.wiz_energy_update_hud()
|
||||
|
||||
//Lower capacity but safer core.
|
||||
/obj/item/weapon/technomancer_core/rapid
|
||||
name = "rapid core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. This one has a superior \
|
||||
recharge rate, at the price of storage capacity. It also includes integrated motion assistance, increasing agility somewhat."
|
||||
energy = 7000
|
||||
max_energy = 7000
|
||||
regen_rate = 70 //100 seconds to full
|
||||
slowdown = -1
|
||||
|
||||
//Big batteries but slow regen, buying energy spells is highly recommended.
|
||||
/obj/item/weapon/technomancer_core/bulky
|
||||
name = "bulky core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. This variant is more \
|
||||
cumbersome and bulky, due to the additional energy capacitors installed. It also comes at a price of a subpar fractal \
|
||||
reactor."
|
||||
energy = 20000
|
||||
max_energy = 20000
|
||||
regen_rate = 25 //800 seconds to full
|
||||
slowdown = 1
|
||||
|
||||
// Using this can result in abilities costing less energy. If you're lucky.
|
||||
/obj/item/weapon/technomancer_core/recycling
|
||||
name = "recycling core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. This type tries to recover \
|
||||
some of the energy lost from using functions due to inefficiency."
|
||||
energy = 12000
|
||||
max_energy = 12000
|
||||
regen_rate = 40 //300 seconds to full
|
||||
|
||||
/obj/item/weapon/technomancer_core/recycling/pay_energy(amount)
|
||||
..()
|
||||
if(.)
|
||||
if(prob(30))
|
||||
give_energy(round(amount / 2))
|
||||
if(amount >= 100) // Managing to recover less than half of this isn't worth telling the user about.
|
||||
wearer << "<span class='notice'>\The [src] has recovered [amount/2 >= 1000 ? "a lot of" : "some"] energy.</span>"
|
||||
|
||||
// For those dedicated to summoning hoards of things.
|
||||
/obj/item/weapon/technomancer_core/summoner
|
||||
name = "summoner core"
|
||||
desc = "A bewilderingly complex 'black box' that allows the wearer to accomplish amazing feats. This type is optimized for \
|
||||
plucking hapless creatures and machines from other locations, to do your bidding. The maximum amount of entities that you can \
|
||||
bring over at once is higher with this core, up to 40 entities, and the maintenance cost is significantly lower."
|
||||
energy = 8000
|
||||
max_energy = 8000
|
||||
regen_rate = 35 //228 seconds to full
|
||||
max_summons = 40
|
||||
|
||||
/obj/item/weapon/technomancer_core/summoner/pay_dues()
|
||||
if(summoned_mobs.len)
|
||||
pay_energy( round(summoned_mobs.len) )
|
||||
@@ -0,0 +1,79 @@
|
||||
/datum/technomancer/consumable/disposable_teleporter
|
||||
name = "Disposable Teleporter"
|
||||
desc = "An ultra-safe teleportation device that can directly teleport you to a number of locations at minimal risk, however \
|
||||
it has a limited amount of charges."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/disposable_teleporter
|
||||
|
||||
/obj/item/weapon/disposable_teleporter
|
||||
name = "disposable teleporter"
|
||||
desc = "A very compact personal teleportation device. It's very precise and safe, however it can only be used a few times."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "hand_tele" //temporary
|
||||
var/uses = 3.0
|
||||
w_class = 1
|
||||
item_state = "paper"
|
||||
origin_tech = list(TECH_BLUESPACE = 4, TECH_POWER = 3)
|
||||
|
||||
//This one is what the wizard starts with. The above is a better version that can be purchased.
|
||||
/obj/item/weapon/disposable_teleporter/free
|
||||
name = "complimentary disposable teleporter"
|
||||
desc = "A very compact personal teleportation device. It's very precise and safe, however it can only be used once. This \
|
||||
one has been provided to allow you to leave your hideout."
|
||||
uses = 1
|
||||
|
||||
/obj/item/weapon/disposable_teleporter/examine(mob/user)
|
||||
..()
|
||||
user << "[uses] uses remaining."
|
||||
|
||||
/obj/item/weapon/disposable_teleporter/attack_self(mob/user as mob)
|
||||
if(!uses)
|
||||
user << "<span class='danger'>\The [src] has ran out of uses, and is now useless to you!</span>"
|
||||
return
|
||||
else
|
||||
var/area/A = input(user, "Area to teleport to", "Teleportation") in teleportlocs
|
||||
if(!A)
|
||||
return
|
||||
|
||||
if (user.stat || user.restrained())
|
||||
return
|
||||
|
||||
if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf)))))
|
||||
return
|
||||
|
||||
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
|
||||
sparks.set_up(5, 0, user.loc)
|
||||
sparks.attach(user)
|
||||
sparks.start()
|
||||
|
||||
if(user && user.buckled)
|
||||
user.buckled.unbuckle_mob()
|
||||
|
||||
var/list/targets = list()
|
||||
|
||||
//Copypasta
|
||||
valid_turfs:
|
||||
for(var/turf/simulated/T in A.contents)
|
||||
if(T.density || istype(T, /turf/simulated/mineral)) //Don't blink to vacuum or a wall
|
||||
continue
|
||||
for(var/atom/movable/stuff in T.contents)
|
||||
if(stuff.density)
|
||||
continue valid_turfs
|
||||
targets.Add(T)
|
||||
|
||||
if(!targets.len)
|
||||
user <<"The teleporter matrix was unable to locate a suitable teleport destination, as all the possibilities were nonexistant \
|
||||
or hazardous. Try a different area."
|
||||
return
|
||||
var/turf/simulated/destination = null
|
||||
|
||||
destination = pick(targets)
|
||||
|
||||
if(destination)
|
||||
user.forceMove(destination)
|
||||
user << "<span class='notice'>You are teleported to \the [A].</span>"
|
||||
uses--
|
||||
if(uses <= 0)
|
||||
user << "<span class='danger'>\The [src] has ran out of uses, and disintegrates from your hands, to prevent \
|
||||
reverse engineering by outsiders.</span>"
|
||||
qdel(src)
|
||||
192
code/game/gamemodes/technomancer/devices/hypos.dm
Normal file
192
code/game/gamemodes/technomancer/devices/hypos.dm
Normal file
@@ -0,0 +1,192 @@
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector
|
||||
name = "empty hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/New()
|
||||
..()
|
||||
reagents.remove_reagent("inaprovaline", 5)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/datum/technomancer/consumable/hypo_brute
|
||||
name = "Trauma Hypo"
|
||||
desc = "A extended capacity hypo which can treat blunt trauma."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute
|
||||
|
||||
/datum/technomancer/consumable/hypo_burn
|
||||
name = "Burn Hypo"
|
||||
desc = "A extended capacity hypo which can treat severe burns."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn
|
||||
|
||||
/datum/technomancer/consumable/hypo_tox
|
||||
name = "Toxin Hypo"
|
||||
desc = "A extended capacity hypo which can treat various toxins."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin
|
||||
|
||||
/datum/technomancer/consumable/hypo_oxy
|
||||
name = "Oxy Hypo"
|
||||
desc = "A extended capacity hypo which can treat oxygen deprivation."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy
|
||||
|
||||
/datum/technomancer/consumable/hypo_purity
|
||||
name = "Purity Hypo"
|
||||
desc = "A extended capacity hypo which can remove various inpurities in the system such as viruses, infections, \
|
||||
radiation, and genetic problems."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity
|
||||
|
||||
/datum/technomancer/consumable/hypo_pain
|
||||
name = "Pain Hypo"
|
||||
desc = "A extended capacity hypo which contains potent painkillers."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain
|
||||
|
||||
/datum/technomancer/consumable/hypo_organ
|
||||
name = "Organ Hypo"
|
||||
desc = "A extended capacity hypo which is designed to fix internal organ problems."
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ
|
||||
|
||||
/datum/technomancer/consumable/hypo_combat
|
||||
name = "Combat Hypo"
|
||||
desc = "A extended capacity hypo containing a dangerous cocktail of various combat stims."
|
||||
cost = 75
|
||||
obj_path = /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat
|
||||
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute
|
||||
name = "trauma hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to be used on victims of \
|
||||
moderate blunt trauma."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute/New()
|
||||
..()
|
||||
reagents.add_reagent("bicaridine", 15)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn
|
||||
name = "burn hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to be used on burn victims, \
|
||||
featuring an optimized chemical mixture to allow for rapid healing."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn/New()
|
||||
..()
|
||||
reagents.add_reagent("kelotane", 7.5)
|
||||
reagents.add_reagent("dermaline", 7.5)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin
|
||||
name = "toxin hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to counteract toxins."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin/New()
|
||||
..()
|
||||
reagents.add_reagent("anti_toxin", 15)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy
|
||||
name = "oxy hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to counteract oxygen \
|
||||
deprivation."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy/New()
|
||||
..()
|
||||
reagents.add_reagent("dexalinp", 10)
|
||||
reagents.add_reagent("tricordrazine", 5) //Dex+ ODs above 10, so we add tricord to pad it out somewhat.
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity
|
||||
name = "purity hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This varient excels at \
|
||||
resolving viruses, infections, radiation, and genetic maladies."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity/New()
|
||||
..()
|
||||
reagents.add_reagent("spaceacillin", 9)
|
||||
reagents.add_reagent("arithrazine", 5)
|
||||
reagents.add_reagent("ryetalyn", 1)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain
|
||||
name = "pain hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one contains potent painkillers."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain/New()
|
||||
..()
|
||||
reagents.add_reagent("tramadol", 15)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ
|
||||
name = "organ hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. Organ damage is resolved by this varient."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ/New()
|
||||
..()
|
||||
reagents.add_reagent("alkysine", 1)
|
||||
reagents.add_reagent("imidazoline", 1)
|
||||
reagents.add_reagent("peridaxon", 13)
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat
|
||||
name = "combat hypo"
|
||||
desc = "A refined version of the standard autoinjector, allowing greater capacity. This is a more dangerous and potentially \
|
||||
addictive hypo compared to others, as it contains a potent cocktail of various chemicals to optimize the recipient's combat \
|
||||
ability."
|
||||
icon_state = "autoinjector"
|
||||
amount_per_transfer_from_this = 15
|
||||
volume = 15
|
||||
origin_tech = list(TECH_BIO = 4)
|
||||
|
||||
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat/New()
|
||||
..()
|
||||
reagents.add_reagent("bicaridine", 3)
|
||||
reagents.add_reagent("kelotane", 1.5)
|
||||
reagents.add_reagent("dermaline", 1.5)
|
||||
reagents.add_reagent("oxycodone", 3)
|
||||
reagents.add_reagent("hyperzine", 3)
|
||||
reagents.add_reagent("tricordrazine", 3)
|
||||
update_icon()
|
||||
return
|
||||
84
code/game/gamemodes/technomancer/devices/shield_armor.dm
Normal file
84
code/game/gamemodes/technomancer/devices/shield_armor.dm
Normal file
@@ -0,0 +1,84 @@
|
||||
/datum/technomancer/equipment/shield_armor
|
||||
name = "Personal Shield Projector"
|
||||
desc = "This state-of-the-art technology uses the bleeding edge of energy distribution and field projection \
|
||||
to provide a personal shield around you, which can diffuse laser beams and reduce the velocity of bullets and close quarters \
|
||||
weapons, reducing their potential for harm severely. All of this comes at a cost of of requiring a large amount of energy, \
|
||||
of which your Core can provide. When you are struck by something, the shield will block 75% of the damage, deducting energy \
|
||||
proportional to the amount of force that was inflicted. Armor penetration has no effect on the shield's ability to protect \
|
||||
you from harm, however the shield will fail if the energy supply cannot meet demand."
|
||||
cost = 300
|
||||
obj_path = /obj/item/clothing/suit/armor/shield
|
||||
|
||||
/obj/item/clothing/suit/armor/shield
|
||||
name = "shield projector"
|
||||
desc = "This armor has no inherent ability to absorb shock, as normal armor usually does. Instead, this emits a strong field \
|
||||
around the wearer, designed to protect from most forms of harm, from lasers to bullets to close quarters combat. It appears to \
|
||||
require a very potent supply of an energy of some kind in order to function."
|
||||
icon_state = "reactiveoff" //wip
|
||||
item_state = "reactiveoff"
|
||||
blood_overlay_type = "armor"
|
||||
slowdown = 0
|
||||
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
|
||||
var/active = 0
|
||||
var/damage_to_energy_multiplier = 50.0 //Determines how much energy to charge for blocking, e.g. 20 damage attack = 750 energy cost
|
||||
var/datum/effect/effect/system/spark_spread/spark_system = null
|
||||
var/block_percentage = 75
|
||||
|
||||
/obj/item/clothing/suit/armor/shield/New()
|
||||
..()
|
||||
spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
spark_system.set_up(5, 0, src)
|
||||
|
||||
/obj/item/clothing/suit/armor/shield/Destroy()
|
||||
qdel(spark_system)
|
||||
..()
|
||||
|
||||
/obj/item/clothing/suit/armor/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
//Since this is a pierce of armor that is passive, we do not need to check if the user is incapacitated.
|
||||
if(!active)
|
||||
return 0
|
||||
|
||||
var/modified_block_percentage = block_percentage
|
||||
|
||||
if(issmall(user)) // Smaller shield means better protection.
|
||||
modified_block_percentage += 15
|
||||
|
||||
|
||||
var/damage_blocked = damage * (modified_block_percentage / 100)
|
||||
|
||||
var/damage_to_energy_cost = (damage_to_energy_multiplier * damage_blocked)
|
||||
|
||||
if(!user.technomancer_pay_energy(damage_to_energy_cost))
|
||||
user << "<span class='danger'>Your shield fades due to lack of energy!</span>"
|
||||
active = 0
|
||||
update_icon()
|
||||
return 0
|
||||
|
||||
damage = damage - damage_blocked
|
||||
|
||||
if(istype(damage_source, /obj/item/projectile))
|
||||
var/obj/item/projectile/P = damage_source
|
||||
P.sharp = 0
|
||||
P.edge = 0
|
||||
P.damage = P.damage - damage_blocked
|
||||
|
||||
user.visible_message("<span class='danger'>\The [user]'s [src] absorbs [attack_text]!</span>")
|
||||
user << "<span class='warning'>Your shield has absorbed most of \the [damage_source].</span>"
|
||||
|
||||
spark_system.start()
|
||||
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
|
||||
return 0 // This shield does not block all damage, so returning 0 is needed to tell the game to apply the new damage.
|
||||
|
||||
/obj/item/clothing/suit/armor/shield/attack_self(mob/user)
|
||||
active = !active
|
||||
user << "<span class='notice'>You [active ? "" : "de"]active \the [src].</span>"
|
||||
update_icon()
|
||||
|
||||
/obj/item/clothing/suit/armor/shield/update_icon()
|
||||
if(active)
|
||||
icon_state = "shield_armor"
|
||||
set_light(2, 1, l_color = "#006AFF")
|
||||
else
|
||||
icon_state = "shield_armor_off"
|
||||
set_light(0, 0, l_color = "#000000")
|
||||
return
|
||||
54
code/game/gamemodes/technomancer/devices/tesla_armor.dm
Normal file
54
code/game/gamemodes/technomancer/devices/tesla_armor.dm
Normal file
@@ -0,0 +1,54 @@
|
||||
/datum/technomancer/equipment/tesla_armor
|
||||
name = "Tesla Armor"
|
||||
desc = "This piece of armor offers a retaliation-based defense. When the armor is 'ready', it will completely protect you from \
|
||||
the next attack you suffer, and strike the attacker with a strong bolt of lightning. This effect requires twenty seconds to \
|
||||
recharge. If you are attacked while this is recharging, a weaker lightning bolt is sent out, however you won't be protected from \
|
||||
the person beating you."
|
||||
cost = 250
|
||||
obj_path = /obj/item/clothing/suit/armor/tesla
|
||||
|
||||
/obj/item/clothing/suit/armor/tesla
|
||||
name = "tesla armor"
|
||||
desc = "This rather dangerous looking armor will hopefully shock your enemies, and not you in the process."
|
||||
icon_state = "reactiveoff" //wip
|
||||
item_state = "reactiveoff"
|
||||
blood_overlay_type = "armor"
|
||||
slowdown = 1
|
||||
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
|
||||
var/ready = 1 //Determines if the next attack will be blocked, as well if a strong lightning bolt is sent out at the attacker.
|
||||
var/ready_icon_state = "reactive" //also wip
|
||||
var/cooldown_to_charge = 20 SECONDS
|
||||
|
||||
/obj/item/clothing/suit/armor/tesla/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
//First, some retaliation.
|
||||
if(attacker && attacker != user)
|
||||
if(get_dist(user, attacker) <= 3) //Anyone farther away than three tiles is too far to shoot lightning at.
|
||||
if(ready)
|
||||
shoot_lightning(attacker, 40)
|
||||
else
|
||||
shoot_lightning(attacker, 15)
|
||||
|
||||
//Deal with protecting our wearer now.
|
||||
if(ready)
|
||||
ready = 0
|
||||
spawn(cooldown_to_charge)
|
||||
ready = 1
|
||||
update_icon()
|
||||
user << "<span class='notice'>\The [src] is ready to protect you once more.</span>"
|
||||
visible_message("<span class='danger'>\The [user]'s [src.name] blocks [attack_text]!</span>")
|
||||
update_icon()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/clothing/suit/armor/tesla/update_icon()
|
||||
..()
|
||||
if(ready)
|
||||
icon_state = ready_icon_state
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/obj/item/clothing/suit/armor/tesla/proc/shoot_lightning(var/mob/target, var/power)
|
||||
var/obj/item/projectile/beam/lightning/lightning = new(src)
|
||||
lightning.power = power
|
||||
lightning.launch(target)
|
||||
visible_message("<span class='danger'>\The [src] strikes \the [target] with lightning!</span>")
|
||||
95
code/game/gamemodes/technomancer/equipment.dm
Normal file
95
code/game/gamemodes/technomancer/equipment.dm
Normal file
@@ -0,0 +1,95 @@
|
||||
/datum/technomancer/equipment/default_core
|
||||
name = "Manipulation Core"
|
||||
desc = "The default core that you most likely already have. This is here in-case you change your mind after buying \
|
||||
another core, don't forget to refund the old core. This has a capacity of 10,000 units of energy, and recharges at a \
|
||||
rate of 50 units."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/technomancer_core
|
||||
|
||||
/datum/technomancer/equipment/rapid_core
|
||||
name = "Rapid Core"
|
||||
desc = "A core optimized for passive regeneration, however at the cost of capacity. Has a capacity of 7,000 units of energy, and \
|
||||
recharges at a rate of 70 units."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/technomancer_core/rapid
|
||||
|
||||
/datum/technomancer/equipment/bulky_core
|
||||
name = "Bulky Core"
|
||||
desc = "This core has very large capacitors, however it also has a subpar fractal reactor. The user is recommended to \
|
||||
purchase one or more energy-generating Functions as well if using this core. Has a capacity of 20,000 units of energy, \
|
||||
and recharges at a rate of 25 units."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/technomancer_core/bulky
|
||||
|
||||
/datum/technomancer/equipment/unstable
|
||||
name = "Unstable Core"
|
||||
desc = "This core feeds off unstable energies around the user in addition to a fractal reactor. This means that it performs \
|
||||
better as the user has more instability, which could prove dangerous to the inexperienced or unprepared. Has a capacity of 13,000 \
|
||||
units of energy, and recharges at a rate of 35 units at no instability, and approximately 110 units when within the \
|
||||
'yellow zone' of instability."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/technomancer_core/unstable
|
||||
|
||||
/datum/technomancer/equipment/hypo_belt
|
||||
name = "Hypo Belt"
|
||||
desc = "A medical belt designed to carry autoinjectors and other medical equipment. Comes with one of each hypo."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/storage/belt/medical/technomancer
|
||||
|
||||
/obj/item/weapon/storage/belt/medical/technomancer
|
||||
name = "hypo belt"
|
||||
desc = "A medical belt designed to carry autoinjectors and other medical equipment."
|
||||
|
||||
/obj/item/weapon/storage/belt/medical/technomancer/New()
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ(src)
|
||||
new /obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat(src)
|
||||
..()
|
||||
|
||||
/datum/technomancer/equipment/thermals
|
||||
name = "Thermoncle"
|
||||
desc = "A fancy monocle with a thermal optics lens installed. Allows you to see people across walls."
|
||||
cost = 150
|
||||
obj_path = /obj/item/clothing/glasses/thermal/plain/monocle
|
||||
|
||||
/datum/technomancer/equipment/med_hud
|
||||
name = "Medical HUD"
|
||||
desc = "A commonly available HUD for medical professionals, which displays how healthy an individual is. \
|
||||
Recommended for support-based apprentices!"
|
||||
cost = 30
|
||||
obj_path = /obj/item/clothing/glasses/thermal/plain/monocle
|
||||
|
||||
|
||||
/datum/technomancer/equipment/scepter
|
||||
name = "Scepter of Empowerment"
|
||||
desc = "A gem sometimes found in the depths of asteroids makes up the basis for this device. Energy is channeled into it from \
|
||||
the Core and the user, causing many functions to be enhanced in various ways, so long as it is held in the off-hand. \
|
||||
Be careful not to lose this!"
|
||||
cost = 300
|
||||
obj_path = /obj/item/weapon/scepter
|
||||
|
||||
/obj/item/weapon/scepter
|
||||
name = "scepter of empowerment"
|
||||
desc = "It's a purple gem, attached to a rod and a handle, along with small wires. It looks like it would make a good club."
|
||||
icon = 'icons/obj/technomancer.dmi'
|
||||
icon_state = "scepter"
|
||||
force = 15
|
||||
|
||||
/obj/item/weapon/scepter/attack_self(mob/living/carbon/human/user)
|
||||
var/obj/item/item_to_test = user.get_other_hand(src)
|
||||
if(istype(item_to_test, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/S = item_to_test
|
||||
S.on_scepter_use_cast(user)
|
||||
|
||||
/obj/item/weapon/scepter/afterattack(atom/target, mob/living/carbon/human/user, proximity_flag, click_parameters)
|
||||
if(proximity_flag)
|
||||
return ..()
|
||||
var/obj/item/item_to_test = user.get_other_hand(src)
|
||||
if(istype(item_to_test, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/S = item_to_test
|
||||
S.on_scepter_ranged_cast(target, user)
|
||||
193
code/game/gamemodes/technomancer/instability.dm
Normal file
193
code/game/gamemodes/technomancer/instability.dm
Normal file
@@ -0,0 +1,193 @@
|
||||
/mob/living
|
||||
var/instability = 0
|
||||
var/last_instability_event = null // most recent world.time that something bad happened due to instability.
|
||||
|
||||
// Proc: adjust_instability()
|
||||
// Parameters: 0
|
||||
// Description: Does nothing, because inheritence.
|
||||
/mob/living/proc/adjust_instability()
|
||||
return
|
||||
|
||||
// Proc: adjust_instability()
|
||||
// Parameters: 1 (amount - how much instability to give)
|
||||
// Description: Adds or subtracks instability to the mob, then updates the hud.
|
||||
/mob/living/carbon/human/adjust_instability(var/amount)
|
||||
instability = min(max(instability + amount, 0), 200)
|
||||
instability_update_hud()
|
||||
|
||||
// Proc: instability_update_hud()
|
||||
// Parameters: 0
|
||||
// Description: Sets the HUD icon to the correct state.
|
||||
/mob/living/carbon/human/proc/instability_update_hud()
|
||||
if(client && hud_used)
|
||||
switch(instability)
|
||||
if(0 to 10)
|
||||
wiz_instability_display.icon_state = "instability-1"
|
||||
if(11 to 30)
|
||||
wiz_instability_display.icon_state = "instability0"
|
||||
if(31 to 50)
|
||||
wiz_instability_display.icon_state = "instability1"
|
||||
if(51 to 100)
|
||||
wiz_instability_display.icon_state = "instability2"
|
||||
if(101 to 200)
|
||||
wiz_instability_display.icon_state = "instability3"
|
||||
|
||||
// Proc: Life()
|
||||
// Parameters: 0
|
||||
// Description: Makes instability tick along with Life().
|
||||
/mob/living/carbon/human/Life()
|
||||
. = ..()
|
||||
handle_instability()
|
||||
|
||||
// Proc: handle_instability()
|
||||
// Parameters: 0
|
||||
// Description: Makes instability decay. instability_effects() handles the bad effects for having instability. It will also hold back
|
||||
// from causing bad effects more than one every ten seconds, to prevent sudden death from angry RNG.
|
||||
/mob/living/carbon/human/proc/handle_instability()
|
||||
instability = Clamp(instability, 0, 200)
|
||||
instability_update_hud()
|
||||
//This should cushon against really bad luck.
|
||||
if(instability && last_instability_event < (world.time - 10 SECONDS) && prob(20))
|
||||
instability_effects()
|
||||
switch(instability)
|
||||
if(1 to 10)
|
||||
adjust_instability(-1)
|
||||
if(11 to 20)
|
||||
adjust_instability(-2)
|
||||
if(21 to 30)
|
||||
adjust_instability(-3)
|
||||
if(31 to 40)
|
||||
adjust_instability(-4)
|
||||
if(41 to 50)
|
||||
adjust_instability(-5)
|
||||
if(51 to 100)
|
||||
adjust_instability(-10)
|
||||
if(101 to 200)
|
||||
adjust_instability(-20)
|
||||
|
||||
/*
|
||||
[16:18:08] <PsiOmegaDelta> Sparks
|
||||
[16:18:10] <PsiOmegaDelta> Wormholes
|
||||
[16:18:16] <PsiOmegaDelta> Random spells firing off on their own
|
||||
[16:18:22] <PsiOmegaDelta> The possibilities are endless
|
||||
[16:19:00] <PsiOmegaDelta> Random objects phasing into reality, only to disappear again
|
||||
[16:19:05] <PsiOmegaDelta> Things briefly duplicating
|
||||
[16:20:56] <PsiOmegaDelta> Glass cracking, eventually breaking
|
||||
*/
|
||||
// Proc: instability_effects()
|
||||
// Parameters: 0
|
||||
// Description: Does a variety of bad effects to the entity holding onto the instability, with more severe effects occuring if they have
|
||||
// a lot of instability.
|
||||
/mob/living/carbon/human/proc/instability_effects()
|
||||
if(instability)
|
||||
var/rng = 0
|
||||
last_instability_event = world.time
|
||||
spawn(1)
|
||||
var/image/instability_flash = image('icons/obj/spells.dmi',"instability")
|
||||
overlays |= instability_flash
|
||||
sleep(4)
|
||||
overlays.Remove(instability_flash)
|
||||
qdel(instability_flash)
|
||||
switch(instability)
|
||||
if(1 to 10) //Harmless
|
||||
return
|
||||
if(11 to 30) //Minor
|
||||
rng = rand(0,1)
|
||||
switch(rng)
|
||||
if(0)
|
||||
var/datum/effect/effect/system/spark_spread/sparks = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
sparks.set_up(5, 0, src)
|
||||
sparks.attach(loc)
|
||||
// var/datum/effect/effect/system/spark_spread/spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
// spark_system.set_up(5, 0, get_turf(src))
|
||||
// spark_system.attach(src)
|
||||
sparks.start()
|
||||
visible_message("<span class='warning'>Electrical sparks manifest from nowhere around \the [src]!</span>")
|
||||
qdel(sparks)
|
||||
if(1)
|
||||
return
|
||||
|
||||
if(31 to 50) //Moderate
|
||||
rng = rand(0,8)
|
||||
switch(rng)
|
||||
if(0)
|
||||
apply_effect(instability * 0.5, IRRADIATE)
|
||||
if(1)
|
||||
return
|
||||
// visible_message("<span class='warning'>\The [src] suddenly collapses!</span>",
|
||||
// "<span class='danger'>You suddenly feel very weak, and you fall down!</span>")
|
||||
// Weaken(instability * 0.1)
|
||||
if(2)
|
||||
if(can_feel_pain())
|
||||
apply_effect(instability * 0.5, AGONY)
|
||||
src << "<span class='danger'>You feel a sharp pain!</span>"
|
||||
if(3)
|
||||
apply_effect(instability * 0.5, EYE_BLUR)
|
||||
src << "<span class='danger'>Your eyes start to get cloudy!</span>"
|
||||
if(4)
|
||||
electrocute_act(instability * 0.3, "unstable energies")
|
||||
if(5)
|
||||
adjustFireLoss(instability * 0.2) //10 burn @ 50 instability
|
||||
src << "<span class='danger'>You feel your skin burn!</span>"
|
||||
if(6)
|
||||
adjustBruteLoss(instability * 0.2) //10 brute @ 50 instability
|
||||
src << "<span class='danger'>You feel a sharp pain as an unseen force harms your body!</span>"
|
||||
if(7)
|
||||
adjustToxLoss(instability * 0.2) //10 tox @ 50 instability
|
||||
if(8)
|
||||
safe_blink(src, range = 6)
|
||||
src << "<span class='warning'>You're teleported against your will!</span>"
|
||||
|
||||
if(51 to 100) //Severe
|
||||
rng = rand(0,8)
|
||||
switch(rng)
|
||||
if(0)
|
||||
apply_effect(instability * 0.7, IRRADIATE)
|
||||
if(1)
|
||||
// visible_message("<span class='warning'>\The [src] suddenly collapses!</span>",
|
||||
// "<span class='danger'>You suddenly feel very light-headed, and faint!</span>")
|
||||
// Paralyse(instability * 0.1)
|
||||
return
|
||||
if(2)
|
||||
if(can_feel_pain())
|
||||
apply_effect(instability * 0.7, AGONY)
|
||||
src << "<span class='danger'>You feel an extremly angonizing pain from all over your body!</span>"
|
||||
if(3)
|
||||
apply_effect(instability * 0.5, EYE_BLUR)
|
||||
src << "<span class='danger'>Your eyes start to get cloudy!</span>"
|
||||
if(4)
|
||||
electrocute_act(instability * 0.5, "extremely unstable energies")
|
||||
if(5)
|
||||
fire_act()
|
||||
src << "<span class='danger'>You spontaneously combust!</span>"
|
||||
if(6)
|
||||
adjustCloneLoss(instability * 0.05) //5 cloneloss @ 100 instability
|
||||
src << "<span class='danger'>You feel your body slowly degenerate.</span>"
|
||||
if(7)
|
||||
adjustToxLoss(instability * 0.25) //25 tox @ 100 instability
|
||||
if(101 to 200) //Lethal
|
||||
rng = rand(0,8)
|
||||
switch(rng)
|
||||
if(0)
|
||||
apply_effect(instability, IRRADIATE)
|
||||
if(1)
|
||||
visible_message("<span class='warning'>\The [src] suddenly collapses!</span>",
|
||||
"<span class='danger'>You suddenly feel very light-headed, and faint!</span>")
|
||||
Paralyse(instability * 0.1)
|
||||
if(2)
|
||||
if(can_feel_pain())
|
||||
apply_effect(instability, AGONY)
|
||||
src << "<span class='danger'>You feel an extremly angonizing pain from all over your body!</span>"
|
||||
if(3)
|
||||
apply_effect(instability, EYE_BLUR)
|
||||
src << "<span class='danger'>Your eyes start to get cloudy!</span>"
|
||||
if(4)
|
||||
electrocute_act(instability, "extremely unstable energies")
|
||||
if(5)
|
||||
fire_act()
|
||||
src << "<span class='danger'>You spontaneously combust!</span>"
|
||||
if(6)
|
||||
adjustCloneLoss(instability * 0.10) //5 cloneloss @ 100 instability
|
||||
src << "<span class='danger'>You feel your body slowly degenerate.</span>"
|
||||
if(7)
|
||||
adjustToxLoss(instability * 0.40) //25 tox @ 100 instability
|
||||
54
code/game/gamemodes/technomancer/presets.dm
Normal file
54
code/game/gamemodes/technomancer/presets.dm
Normal file
@@ -0,0 +1,54 @@
|
||||
/datum/technomancer/presets
|
||||
name = "Preset Template"
|
||||
desc = "If you see me, I'm broken."
|
||||
obj_path = null
|
||||
var/list/bundled = list()
|
||||
|
||||
/datum/technomancer/presets/basic
|
||||
name = "Basic"
|
||||
desc = "This preset is created to be simple to understand and use, yet sufficent to face a wide range of hazards that may lay \
|
||||
ahead. It includes Force Missile, Beam, Passwall, Repel Missiles, Pulsar, and Discharge. Force Missile and Beam will be your \
|
||||
bread and butter, as both can be used frequently and cheaply. Passwall allows you to teleport to an adjacent room. Repel Missiles should \
|
||||
be applied before going into a fight, and Pulsar is an excellent 'area denial' function, that forces the opposing force to decide \
|
||||
if being struck with EMP is worth staying still. Discharge can be used if the opposing force is using energy weapons heavily, or \
|
||||
if you are fighting synthetics."
|
||||
cost = 600
|
||||
|
||||
/datum/technomancer/presets/summoner
|
||||
name = "Summoner"
|
||||
desc = "This preset is dedicated to amassing a large army of entities to do your bidding. It includes the Summoner Core, to allow \
|
||||
for more efficency, as well as Summon Creature and Summon Automatron, for obvious reasons. The functions Aspect Aura and \
|
||||
Mend Wounds are also included, to allow for an area of effect heal that is ideal for keeping your (nonrobotic) army alive. \
|
||||
Finally, this includes the Scepter of Enpowerment, to make your summoned entities friendly to you and your allies, without the \
|
||||
need for the Control function. The Control function can be obtained if one wishes to micromanage their summon army, however."
|
||||
cost = 600
|
||||
|
||||
/datum/technomancer/presets/illusionist
|
||||
name = "Illusionist"
|
||||
desc = "This preset is designed to facilitate the screwing of the opposing forces' minds, as all of the functions included \
|
||||
lend themselves to trickery. Illusion, Audible Deception, Chroma, and Darkness will allow you to make the crew question \
|
||||
reality itself. Passwall will let you slip into adjacent rooms, while the TODO lets you see everything without walls getting \
|
||||
in the way, so that you may manipulate without being seen in the same room. It may be wise to invest in at least one function \
|
||||
that can do harm, in case they wise up."
|
||||
cost = 400
|
||||
|
||||
/datum/technomancer/presets/healer
|
||||
name = "Healer"
|
||||
desc = "This preset is recommended for apprentices who wish to support their master. It contains many healing and support \
|
||||
functions, such as Mend Wounds, Mend Burns, Purify, Oxygenate, Aspect Aura, Shared Burden, Link, Resurrect, and Great Mend Wounds. \
|
||||
Be aware that a lot of these functions create a lot of instability, so prepare for that if you can."
|
||||
cost = 600
|
||||
|
||||
/datum/technomancer/presets/support
|
||||
name = "Support"
|
||||
desc = "This preset is recommended for apprentices who wish to support their master. It contains many functions focused on \
|
||||
augmenting you and your master's survival and capabilities, with functions such as Repel Missiles, Shared Burden, Link, Summon \
|
||||
Ward, Dispel, Corona, and Steal Food."
|
||||
cost = 600
|
||||
|
||||
/datum/technomancer/presets/rainbow
|
||||
name = "Rainbow Mage"
|
||||
desc = "This preset includes many Aspect functions, such as Aspect Aura, Aspect Bolt, Aspect Cloud, Aspect Weapon, etc, as well as \
|
||||
cheap functions beloning to each aspect, for the purposes of combining with an aspect function. This allows you to be \
|
||||
very flexable, however functions made from aspect functions tend to be weaker due to this. Not recommended for beginners."
|
||||
cost = 600
|
||||
295
code/game/gamemodes/technomancer/spell_objs.dm
Normal file
295
code/game/gamemodes/technomancer/spell_objs.dm
Normal file
@@ -0,0 +1,295 @@
|
||||
//cast_method flags
|
||||
#define CAST_USE 1 // Clicking the spell in your hand.
|
||||
#define CAST_MELEE 2 // Clicking an atom in melee range.
|
||||
#define CAST_RANGED 4 // Clicking an atom beyond melee range.
|
||||
#define CAST_THROW 8 // Throwing the spell and hitting an atom.
|
||||
#define CAST_COMBINE 16 // Clicking another spell with this spell.
|
||||
#define CAST_INNATE 32 // Activates upon verb usage, used for mobs without hands.
|
||||
|
||||
//Aspects
|
||||
#define ASPECT_FIRE "fire" //Damage over time and raising body-temp. Firesuits protect from this.
|
||||
#define ASPECT_FROST "frost" //Slows down the affected, also involves imbedding with icicles. Winter coats protect from this.
|
||||
#define ASPECT_SHOCK "shock" //Energy-expensive, usually stuns. Insulated armor protects from this.
|
||||
#define ASPECT_AIR "air" //Mostly involves manipulation of atmos, useless in a vacuum. Magboots protect from this.
|
||||
#define ASPECT_FORCE "force" //Manipulates gravity to push things away or towards a location.
|
||||
#define ASPECT_TELE "tele" //Teleportation of self, other objects, or other people.
|
||||
#define ASPECT_DARK "dark" //Makes all those photons vanish using magic-- WITH SCIENCE. Used for sneaky stuff.
|
||||
#define ASPECT_LIGHT "light" //The opposite of dark, usually blinds, makes holo-illusions, or makes laser lightshows.
|
||||
#define ASPECT_BIOMED "biomed" //Mainly concerned with healing and restoration.
|
||||
#define ASPECT_EMP "emp" //Unused now.
|
||||
#define ASPECT_UNSTABLE "unstable" //Heavily RNG-based, causes instability to the victim.
|
||||
#define ASPECT_CHROMATIC "chromatic" //Used to combine with other spells.
|
||||
|
||||
/obj/item/weapon/spell
|
||||
name = "glowing particles"
|
||||
desc = "Your hands appear to be glowing brightly."
|
||||
icon = 'icons/obj/spells.dmi'
|
||||
icon_state = "generic"
|
||||
item_icons = list(
|
||||
slot_l_hand_str = 'icons/mob/items/lefthand_spells.dmi',
|
||||
slot_r_hand_str = 'icons/mob/items/righthand_spells.dmi',
|
||||
)
|
||||
throwforce = 0
|
||||
force = 0
|
||||
var/mob/living/carbon/human/owner = null
|
||||
var/obj/item/weapon/technomancer_core/core = null
|
||||
var/cast_methods = null // Controls how the spell is casted.
|
||||
var/aspect = null // Used for combining spells.
|
||||
var/toggled = 0 // Mainly used for overlays.
|
||||
var/cooldown = 0 // If set, will add a cooldown overlay and adjust click delay. Must be a multiple of 5 for overlays.
|
||||
var/cast_sound = null // Sound file played when this is used.
|
||||
|
||||
// Proc: on_use_cast()
|
||||
// Parameters: 1 (user - the technomancer casting the spell)
|
||||
// Description: Override this for clicking the spell in your hands.
|
||||
/obj/item/weapon/spell/proc/on_use_cast(mob/user)
|
||||
return
|
||||
|
||||
// Proc: on_throw_cast()
|
||||
// Parameters: 1 (hit_atom - the atom hit by the spell object)
|
||||
// Description: Override this for throwing effects.
|
||||
/obj/item/weapon/spell/proc/on_throw_cast(atom/hit_atom)
|
||||
return
|
||||
|
||||
// Proc: on_ranged_cast()
|
||||
// Parameters: 2 (hit_atom - the atom clicked on by the user, user - the technomancer that clicked hit_atom)
|
||||
// Description: Override this for ranged effects.
|
||||
/obj/item/weapon/spell/proc/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
return
|
||||
|
||||
// Proc: on_melee_cast()
|
||||
// Parameters: 3 (hit_atom - the atom clicked on by the user, user - the technomancer that clicked hit_atom, def_zone - unknown)
|
||||
// Description: Override this for effects that occur at melee range.
|
||||
/obj/item/weapon/spell/proc/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
return
|
||||
|
||||
// Proc: on_combine_cast()
|
||||
// Parameters: 2 (I - the item trying to merge with the spell, user - the technomancer who initiated the merge)
|
||||
// Description: Override this for combining spells, like Aspect spells.
|
||||
/obj/item/weapon/spell/proc/on_combine_cast(obj/item/I, mob/user)
|
||||
return
|
||||
|
||||
// Proc: on_innate_cast()
|
||||
// Parameters: 1 (user - the entity who is casting innately (without using hands).)
|
||||
// Description: Override this for casting without using hands (and as a result not using spell objects).
|
||||
/obj/item/weapon/spell/proc/on_innate_cast(mob/user)
|
||||
return
|
||||
|
||||
// Proc: on_scepter_use_cast()
|
||||
// Parameters: 1 (user - the holder of the Scepter that clicked.)
|
||||
// Description: Override this for spell casts which have additional functionality when a Scepter is held in the offhand, and the
|
||||
// scepter is being clicked by the technomancer in their hand.
|
||||
/obj/item/weapon/spell/proc/on_scepter_use_cast(mob/user)
|
||||
return
|
||||
|
||||
// Proc: on_scepter_use_cast()
|
||||
// Parameters: 2 (hit_atom - the atom clicked by user, user - the holder of the Scepter that clicked.)
|
||||
// Description: Similar to the above proc, however this is for when someone with a Scepter clicks something far away with the scepter
|
||||
// while holding a spell in the offhand that reacts to that.
|
||||
/obj/item/weapon/spell/proc/on_scepter_ranged_cast(atom/hit_atom, mob/user)
|
||||
return
|
||||
|
||||
// Proc: pay_energy()
|
||||
// Parameters: 1 (amount - how much to test and drain if there is enough)
|
||||
// Description: Use this to make spells cost energy. It returns false if the technomancer cannot pay for the spell for any reason, and
|
||||
// if they are able to pay, it is deducted automatically.
|
||||
/obj/item/weapon/spell/proc/pay_energy(var/amount)
|
||||
if(!core)
|
||||
return 0
|
||||
return core.pay_energy(amount)
|
||||
|
||||
// Proc: give_energy()
|
||||
// Parameters: 1 (amount - how much to give to the technomancer)
|
||||
// Description: Redirects the call to the core's give_energy().
|
||||
/obj/item/weapon/spell/proc/give_energy(var/amount)
|
||||
if(!core)
|
||||
return 0
|
||||
return core.give_energy(amount)
|
||||
|
||||
// Proc: adjust_instability()
|
||||
// Parameters: 1 (amount - how much instability to give)
|
||||
// Description: Use this to quickly add or subtract instability from the caster of the spell. Owner is set by New().
|
||||
/obj/item/weapon/spell/proc/adjust_instability(var/amount)
|
||||
if(!owner)
|
||||
return 0
|
||||
owner.adjust_instability(amount)
|
||||
|
||||
// Proc: New()
|
||||
// Parameters: 0
|
||||
// Description: Sets owner to equal its loc, links to the owner's core, then applies overlays if needed.
|
||||
/obj/item/weapon/spell/New()
|
||||
..()
|
||||
if(ishuman(loc))
|
||||
owner = loc
|
||||
if(owner)
|
||||
if(istype(/obj/item/weapon/technomancer_core, owner.back))
|
||||
core = owner.back
|
||||
update_icon()
|
||||
|
||||
// Proc: Destroy()
|
||||
// Parameters: 0
|
||||
// Description: Nulls object references so it can qdel() cleanly.
|
||||
/obj/item/weapon/spell/Destroy()
|
||||
owner = null
|
||||
core = null
|
||||
..()
|
||||
|
||||
// Proc: update_icon()
|
||||
// Parameters: 0
|
||||
// Description: Applys an overlay if it is a passive spell.
|
||||
/obj/item/weapon/spell/update_icon()
|
||||
if(toggled)
|
||||
var/image/new_overlay = image('icons/obj/spells.dmi',"toggled")
|
||||
overlays |= new_overlay
|
||||
else
|
||||
overlays.Cut()
|
||||
..()
|
||||
|
||||
// Proc: run_checks()
|
||||
// Parameters: 0
|
||||
// Description: Ensures spells should not function if something is wrong. If a core is missing, it will try to find one, then fail
|
||||
// if it still can't find one. It will also check if the core is being worn properly, and finally checks if the owner is a technomancer.
|
||||
/obj/item/weapon/spell/proc/run_checks()
|
||||
if(!owner)
|
||||
return 0
|
||||
if(!core)
|
||||
core = locate(/obj/item/weapon/technomancer_core) in owner
|
||||
if(!core)
|
||||
owner << "<span class='danger'>You need to be wearing a core on your back!</span>"
|
||||
return 0
|
||||
if(core.loc != owner || owner.back != core) //Make sure the core's being worn.
|
||||
owner << "<span class='danger'>You need to be wearing a core on your back!</span>"
|
||||
return 0
|
||||
if(!technomancers.is_antagonist(owner.mind)) //Now make sure the person using this is the actual antag.
|
||||
owner << "<span class='danger'>You can't seem to figure out how to make the machine work properly.</span>"
|
||||
return 0
|
||||
return 1
|
||||
|
||||
// Proc: check_for_scepter()
|
||||
// Parameters: 0
|
||||
// Description: Terrible code to check if a scepter is in the offhand, returns 1 if yes.
|
||||
/obj/item/weapon/spell/proc/check_for_scepter()
|
||||
if(!src || !owner) return 0
|
||||
if(owner.r_hand == src)
|
||||
if(istype(owner.l_hand, /obj/item/weapon/scepter))
|
||||
return 1
|
||||
else
|
||||
if(istype(owner.r_hand, /obj/item/weapon/scepter))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
// Proc: get_other_hand()
|
||||
// Parameters: 1 (I - item being compared to determine what the offhand is)
|
||||
// Description: Helper for Aspect spells.
|
||||
/mob/living/carbon/human/proc/get_other_hand(var/obj/item/I)
|
||||
if(r_hand == I)
|
||||
return l_hand
|
||||
else
|
||||
return r_hand
|
||||
|
||||
// Proc: attack_self()
|
||||
// Parameters: 1 (user - the Technomancer that invoked this proc)
|
||||
// Description: Tries to call on_use_cast() if it is allowed to do so. Don't override this, override on_use_cast() instead.
|
||||
/obj/item/weapon/spell/attack_self(mob/user)
|
||||
if(run_checks() && (cast_methods & CAST_USE))
|
||||
on_use_cast(user)
|
||||
..()
|
||||
|
||||
// Proc: attackby()
|
||||
// Parameters: 2 (W - the item this spell object is hitting, user - the technomancer who clicked the other object)
|
||||
// Description: Tries to combine the spells, if W is a spell, and has CHROMATIC aspect.
|
||||
/obj/item/weapon/spell/attackby(obj/item/W, mob/user)
|
||||
if(istype(W, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/spell = W
|
||||
if(run_checks() & (cast_methods & CAST_COMBINE))
|
||||
spell.on_combine_cast(src, user)
|
||||
else
|
||||
..()
|
||||
|
||||
// Proc: afterattack()
|
||||
// Parameters: 4 (target - the atom clicked on by user, user - the technomancer who clicked with the spell, proximity_flag - argument
|
||||
// telling the proc if target is adjacent to user, click_parameters - information on where exactly the click occured on the screen.)
|
||||
// Description: Tests to make sure it can cast, then casts a combined, ranged, or melee spell based on what it can do and the
|
||||
// range the click occured. Melee casts have higher priority than ranged if both are possible. Sets cooldown at the end.
|
||||
// Don't override this for spells, override the on_*_cast() spells shown above.
|
||||
/obj/item/weapon/spell/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!run_checks())
|
||||
return
|
||||
if(!proximity_flag)
|
||||
if(cast_methods & CAST_RANGED)
|
||||
on_ranged_cast(target, user)
|
||||
else
|
||||
if(istype(target, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/spell = target
|
||||
if(spell.cast_methods & CAST_COMBINE)
|
||||
spell.on_combine_cast(src, user)
|
||||
return
|
||||
if(cast_methods & CAST_MELEE)
|
||||
on_melee_cast(target, user)
|
||||
else if(cast_methods & CAST_RANGED) //Try to use a ranged method if a melee one doesn't exist.
|
||||
on_ranged_cast(target, user)
|
||||
if(cooldown)
|
||||
user.setClickCooldown(cooldown)
|
||||
flick("cooldown_[cooldown]",src)
|
||||
|
||||
// Proc: place_spell_in_hand()
|
||||
// Parameters: 1 (path - the type path for the spell that is desired.)
|
||||
// Description: Returns immediately, this is here to override for other mobs as needed.
|
||||
/mob/living/proc/place_spell_in_hand(var/path)
|
||||
return
|
||||
|
||||
// Proc: place_spell_in_hand()
|
||||
// Parameters: 1 (path - the type path for the spell that is desired.)
|
||||
// Description: Gives the spell to the human mob, if it is allowed to have spells, hands are not full, etc. Otherwise it deletes itself.
|
||||
/mob/living/carbon/human/place_spell_in_hand(var/path)
|
||||
if(!path || !ispath(path))
|
||||
return 0
|
||||
|
||||
//var/obj/item/weapon/spell/S = PoolOrNew(path, src)
|
||||
var/obj/item/weapon/spell/S = new path(src)
|
||||
|
||||
//No hands needed for innate casts.
|
||||
if(S.cast_methods & CAST_INNATE)
|
||||
if(S.run_checks())
|
||||
S.on_innate_cast(src)
|
||||
|
||||
if(l_hand && r_hand) //Make sure our hands aren't full.
|
||||
if(istype(r_hand, /obj/item/weapon/spell)) //If they are full, perhaps we can still be useful.
|
||||
var/obj/item/weapon/spell/r_spell = r_hand
|
||||
if(r_spell.aspect == ASPECT_CHROMATIC) //Check if we can combine the new spell with one in our hands.
|
||||
r_spell.on_combine_cast(S, src)
|
||||
else if(istype(l_hand, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/l_spell = l_hand
|
||||
if(l_spell.aspect == ASPECT_CHROMATIC) //Check the other hand too.
|
||||
l_spell.on_combine_cast(S, src)
|
||||
else //Welp
|
||||
src << "<span class='warning'>You require a free hand to use this function.</span>"
|
||||
return 0
|
||||
|
||||
if(S.run_checks())
|
||||
put_in_hands(S)
|
||||
return 1
|
||||
else
|
||||
qdel(S)
|
||||
return 0
|
||||
|
||||
// Proc: dropped()
|
||||
// Parameters: 0
|
||||
// Description: Deletes the spell object immediately.
|
||||
/obj/item/weapon/spell/dropped()
|
||||
spawn(1)
|
||||
if(src)
|
||||
qdel(src)
|
||||
|
||||
// Proc: throw_impact()
|
||||
// Parameters: 1 (hit_atom - the atom that got hit by the spell as it was thrown)
|
||||
// Description: Calls on_throw_cast() on whatever was hit, then deletes itself incase it missed.
|
||||
/obj/item/weapon/spell/throw_impact(atom/hit_atom)
|
||||
..()
|
||||
if(cast_methods & CAST_THROW)
|
||||
on_throw_cast(hit_atom)
|
||||
|
||||
// If we miss or hit an obstacle, we still want to delete the spell.
|
||||
spawn(20)
|
||||
if(src)
|
||||
qdel(src)
|
||||
26
code/game/gamemodes/technomancer/spell_objs_helpers.dm
Normal file
26
code/game/gamemodes/technomancer/spell_objs_helpers.dm
Normal file
@@ -0,0 +1,26 @@
|
||||
//Returns 1 if the turf is dense, or if there's dense objects on it, unless told to ignore them.
|
||||
/turf/proc/check_density(var/ignore_objs = 0)
|
||||
if(density)
|
||||
return 1
|
||||
if(!ignore_objs)
|
||||
for(var/atom/movable/stuff in contents)
|
||||
if(stuff.density)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
// Used to distinguish friend from foe.
|
||||
/obj/item/weapon/spell/proc/is_ally(var/mob/living/L)
|
||||
if(L == owner) // The best ally is ourselves.
|
||||
return 1
|
||||
if(L.mind && technomancers.is_antagonist(L.mind)) // This should be done better since we might want opposing technomancers later.
|
||||
return 1
|
||||
if(istype(L, /mob/living/simple_animal/hostile)) // Mind controlled simple mobs count as allies too.
|
||||
var/mob/living/simple_animal/SA = L
|
||||
if(owner in SA.friends)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/spell/proc/allowed_to_teleport()
|
||||
if(owner && owner.z in config.admin_levels)
|
||||
return 0
|
||||
return 1
|
||||
39
code/game/gamemodes/technomancer/spells/abjuration.dm
Normal file
39
code/game/gamemodes/technomancer/spells/abjuration.dm
Normal file
@@ -0,0 +1,39 @@
|
||||
/datum/technomancer/spell/abjuration
|
||||
name = "Abjuration"
|
||||
desc = "This ability attempts to send summoned or teleported entities or anomalies to the place from whence they came, or at least \
|
||||
far away from the caster. Failing that, it may inhibit those entities in some form."
|
||||
cost = 40
|
||||
obj_path = /obj/item/weapon/spell/abjuration
|
||||
|
||||
/obj/item/weapon/spell/abjuration
|
||||
name = "abjuration"
|
||||
desc = "Useful for unruly minions, hostile summoners, or for fighting the horrors that may await you with your hubris."
|
||||
icon_state = "generic"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/obj/item/weapon/spell/abjuration/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living) && pay_energy(500))
|
||||
var/mob/living/L = hit_atom
|
||||
var/mob/living/simple_animal/SA = null
|
||||
|
||||
//Bit of a roundabout typecheck, in order to test for two variables from two different mob types in one line.
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
SA = L
|
||||
if(L.summoned || (SA && SA.supernatural) )
|
||||
if(L.client) // Player-controlled mobs are immune to being killed by this.
|
||||
user << "<span class='warning'>\The [L] resists your attempt to banish it!</span>"
|
||||
L << "<span class='warning'>\The [user] tried to teleport you far away, but failed.</span>"
|
||||
return 0
|
||||
else
|
||||
visible_message("<span class='notice'>\The [L] vanishes!</span>")
|
||||
qdel(L)
|
||||
else if(istype(L, /mob/living/simple_animal/construct))
|
||||
var/mob/living/simple_animal/construct/evil = L
|
||||
evil << "<span class='danger'>\The [user]'s abjuration purges your form!</span>"
|
||||
evil.purge = 3
|
||||
adjust_instability(5)
|
||||
// In case NarNar comes back someday.
|
||||
if(istype(hit_atom, /obj/singularity/narsie))
|
||||
user << "<span class='danger'>One does not simply abjurate Nar'sie away.</span>"
|
||||
adjust_instability(200)
|
||||
72
code/game/gamemodes/technomancer/spells/apportation.dm
Normal file
72
code/game/gamemodes/technomancer/spells/apportation.dm
Normal file
@@ -0,0 +1,72 @@
|
||||
/datum/technomancer/spell/apportation
|
||||
name = "Apportation"
|
||||
desc = "This allows you to teleport objects into your hand, or to pull people towards you. If they're close enough, the function \
|
||||
will grab them automatically."
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/spell/apportation
|
||||
|
||||
/obj/item/weapon/spell/apportation
|
||||
name = "apportation"
|
||||
icon_state = "apportation"
|
||||
desc = "Allows you to reach through Bluespace with your hand, and grab something, bringing it to you instantly."
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/obj/item/weapon/spell/apportation/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
var/atom/movable/AM = hit_atom
|
||||
|
||||
if(!AM.loc) //Don't teleport HUD telements to us.
|
||||
return
|
||||
if(AM.anchored)
|
||||
user << "<span class='warning'>\The [hit_atom] is firmly secured and anchored, you can't move it!</span>"
|
||||
return
|
||||
//Teleporting an item.
|
||||
if(istype(hit_atom, /obj/item))
|
||||
var/obj/item/I = hit_atom
|
||||
|
||||
var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread
|
||||
var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread
|
||||
s1.set_up(2, 1, user)
|
||||
s2.set_up(2, 1, I)
|
||||
s1.start()
|
||||
s2.start()
|
||||
I.visible_message("<span class='danger'>\The [I] vanishes into thin air!</span>")
|
||||
I.forceMove(get_turf(user))
|
||||
user.drop_item(src)
|
||||
src.loc = null
|
||||
user.put_in_hands(I)
|
||||
user.visible_message("<span class='notice'>\A [I] appears in \the [user]'s hand!</span>")
|
||||
qdel(src)
|
||||
//Now let's try to teleport a living mob.
|
||||
else if(istype(hit_atom, /mob/living))
|
||||
var/mob/living/L = hit_atom
|
||||
L << "<span class='danger'>You are teleported towards \the [user].</span>"
|
||||
var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread
|
||||
var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread
|
||||
s1.set_up(2, 1, user)
|
||||
s2.set_up(2, 1, L)
|
||||
s1.start()
|
||||
s2.start()
|
||||
L.throw_at(get_step(get_turf(src),get_turf(L)), 4, 1, src)
|
||||
user.drop_item(src)
|
||||
src.loc = null
|
||||
|
||||
spawn(1 SECOND)
|
||||
if(!user.Adjacent(L))
|
||||
user << "<span class='warning'>\The [L] is out of your reach.</span>"
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
L.Weaken(3)
|
||||
user.visible_message("<span class='warning'><b>\The [user]</b> seizes [L]!</span>")
|
||||
|
||||
var/obj/item/weapon/grab/G = new(user,L)
|
||||
|
||||
user.put_in_hands(G)
|
||||
|
||||
G.state = GRAB_PASSIVE
|
||||
G.icon_state = "grabbed1"
|
||||
G.synch()
|
||||
qdel(src)
|
||||
|
||||
135
code/game/gamemodes/technomancer/spells/aspect_aura.dm
Normal file
135
code/game/gamemodes/technomancer/spells/aspect_aura.dm
Normal file
@@ -0,0 +1,135 @@
|
||||
/datum/technomancer/spell/aspect_aura
|
||||
name = "Aspect Aura"
|
||||
desc = "This aura function takes on the properties of other functions based on which aspect is introduced to it, applying \
|
||||
it to everyone nearby."
|
||||
cost = 200
|
||||
obj_path = /mob/living/carbon/human/proc/technomancer_aspect_aura
|
||||
|
||||
/mob/living/carbon/human/proc/technomancer_aspect_aura()
|
||||
place_spell_in_hand(/obj/item/weapon/spell/aspect_aura)
|
||||
|
||||
/obj/item/weapon/spell/aspect_aura
|
||||
name = "aspect aura"
|
||||
desc = "Combine this with another spell to finish the function."
|
||||
icon_state = "aspect_bolt"
|
||||
cast_methods = CAST_COMBINE
|
||||
aspect = ASPECT_CHROMATIC
|
||||
|
||||
/obj/item/weapon/spell/aspect_aura/on_combine_cast(obj/item/W, var/mob/living/carbon/human/user)
|
||||
if(istype(W, /obj/item/weapon/spell))
|
||||
var/obj/item/weapon/spell/spell = W
|
||||
if(!spell.aspect || spell.aspect == ASPECT_CHROMATIC)
|
||||
user << "<span class='warning'>You cannot combine \the [spell] with \the [src], as the aspects are incompatable.</span>"
|
||||
return
|
||||
user.drop_item(src)
|
||||
src.loc = null
|
||||
spawn(1)
|
||||
switch(spell.aspect)
|
||||
if(ASPECT_FIRE)
|
||||
user.place_spell_in_hand(/obj/item/weapon/spell/aura/fire)
|
||||
if(ASPECT_FROST)
|
||||
user.place_spell_in_hand(/obj/item/weapon/spell/aura/frost)
|
||||
if(ASPECT_BIOMED)
|
||||
user.place_spell_in_hand(/obj/item/weapon/spell/aura/biomed)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/aura
|
||||
name = "aura template"
|
||||
desc = "If you can read me, the game broke! Yay!"
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_CHROMATIC
|
||||
var/glow_color = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/spell/aura/New()
|
||||
..()
|
||||
set_light(7, 4, l_color = glow_color)
|
||||
processing_objects |= src
|
||||
|
||||
/obj/item/weapon/spell/aura/Destroy()
|
||||
processing_objects -= src
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/aura/process()
|
||||
return
|
||||
|
||||
/obj/item/weapon/spell/aura/fire
|
||||
name = "heat aura"
|
||||
desc = "Things are starting to heat up."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_FIRE
|
||||
glow_color = "#FF6A00"
|
||||
|
||||
/obj/item/weapon/spell/aura/fire/process()
|
||||
if(!pay_energy(100))
|
||||
qdel(src)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
for(var/mob/living/carbon/human/H in nearby_mobs)
|
||||
if(H == owner || H.mind && technomancers.is_antagonist(H.mind)) //Don't heat up allies.
|
||||
continue
|
||||
|
||||
//We use hotspot_expose() to allow firesuits to protect from this aura.
|
||||
var/turf/location = get_turf(H)
|
||||
location.hotspot_expose(1000, 50, 1)
|
||||
|
||||
owner.adjust_instability(1)
|
||||
|
||||
/obj/item/weapon/spell/aura/frost
|
||||
name = "chilling aura"
|
||||
desc = "Your enemies will find it hard to chase you if they freeze to death."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_FROST
|
||||
glow_color = "#FF6A00"
|
||||
|
||||
/obj/item/weapon/spell/aura/frost/process()
|
||||
if(!pay_energy(100))
|
||||
qdel(src)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
for(var/mob/living/carbon/human/H in nearby_mobs)
|
||||
if(H == owner || H.mind && technomancers.is_antagonist(H.mind)) //Don't chill allies.
|
||||
continue
|
||||
|
||||
//We use hotspot_expose() to allow firesuits to protect from this aura.
|
||||
var/turf/location = get_turf(H)
|
||||
location.hotspot_expose(1, 50, 1)
|
||||
|
||||
owner.adjust_instability(1)
|
||||
|
||||
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed
|
||||
name = "restoration aura"
|
||||
desc = "Allows everyone, or just your allies, to slowly regenerate."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_BIOMED
|
||||
glow_color = "#0000FF"
|
||||
var/regen_tick = 0
|
||||
var/heal_allies_only = 1
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed/process()
|
||||
if(!pay_energy(75))
|
||||
qdel(src)
|
||||
regen_tick++
|
||||
if(regen_tick % 5 == 0)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
var/list/mobs_to_heal = list()
|
||||
if(heal_allies_only)
|
||||
for(var/mob/living/carbon/human/H in nearby_mobs) //Heal our apprentices
|
||||
if(H.mind && technomancers.is_antagonist(H.mind))
|
||||
mobs_to_heal |= H
|
||||
for(var/mob/living/simple_animal/hostile/SAH in nearby_mobs) //Heal our controlled mobs
|
||||
if(owner in SAH.friends)
|
||||
mobs_to_heal |= SAH
|
||||
else
|
||||
mobs_to_heal = nearby_mobs //Heal everyone!
|
||||
for(var/mob/living/L in mobs_to_heal)
|
||||
L.adjustBruteLoss(-5)
|
||||
L.adjustFireLoss(-5)
|
||||
owner.adjust_instability(2)
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed/on_use_cast(mob/living/user)
|
||||
heal_allies_only = !heal_allies_only
|
||||
user << "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you."
|
||||
83
code/game/gamemodes/technomancer/spells/audible_deception.dm
Normal file
83
code/game/gamemodes/technomancer/spells/audible_deception.dm
Normal file
@@ -0,0 +1,83 @@
|
||||
/datum/technomancer/spell/audible_deception
|
||||
name = "Audible Deception"
|
||||
desc = "Allows you to create a specific sound at a location of your choosing."
|
||||
enhancement_desc = "An extremely loud sound that a large amount of energy and instability becomes available, which will \
|
||||
deafen and stun all who are near the targeted tile, including yourself if unprotected."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/audible_deception
|
||||
ability_icon_state = "tech_audibledeception"
|
||||
|
||||
/obj/item/weapon/spell/audible_deception
|
||||
name = "audible deception"
|
||||
icon_state = "audible_deception"
|
||||
desc = "Make them all paranoid!"
|
||||
cast_methods = CAST_RANGED | CAST_USE
|
||||
aspect = ASPECT_AIR
|
||||
cooldown = 10
|
||||
var/list/available_sounds = list(
|
||||
"Blade Slice" = 'sound/weapons/bladeslice.ogg',
|
||||
"Energy Blade Slice" = 'sound/weapons/blade1.ogg',
|
||||
"Explosions" = "explosion",
|
||||
"Distant Explosion" = 'sound/effects/explosionfar.ogg',
|
||||
"Sparks" = "sparks",
|
||||
"Punches" = "punch",
|
||||
"Glass Shattering" = "shatter",
|
||||
"Grille Damage" = 'sound/effects/grillehit.ogg',
|
||||
"Energy Pulse" = 'sound/effects/EMPulse.ogg',
|
||||
"Airlock" = 'sound/machines/airlock.ogg',
|
||||
"Airlock Creak" = 'sound/machines/airlock_creaking.ogg',
|
||||
|
||||
"Shotgun Pumping" = 'sound/weapons/shotgunpump.ogg',
|
||||
"Flash" = 'sound/weapons/flash.ogg',
|
||||
"Bite" = 'sound/weapons/bite.ogg',
|
||||
"Gun Firing" = 'sound/weapons/gunshot.ogg',
|
||||
"Taser Firing" = 'sound/weapons/Taser.ogg',
|
||||
"Laser Gun Firing" = 'sound/weapons/laser.ogg',
|
||||
"Xray Gun Firing" = 'sound/weapons/laser3.ogg',
|
||||
"Pulse Gun Firing" = 'sound/weapons/pulse.ogg',
|
||||
"Emitter Firing" = 'sound/weapons/emitter.ogg',
|
||||
"Energy Blade On" = 'sound/weapons/saberon.ogg',
|
||||
"Energy Blade Off" = 'sound/weapons/saberoff.ogg',
|
||||
"Wire Restraints" = 'sound/weapons/cablecuff.ogg',
|
||||
"Handcuffs" = 'sound/weapons/handcuffs.ogg',
|
||||
|
||||
"Crowbar" = 'sound/items/Crowbar.ogg',
|
||||
"Screwdriver" = 'sound/items/Screwdriver.ogg',
|
||||
"Welding" = 'sound/items/Welder.ogg',
|
||||
"Wirecutting" = 'sound/items/Wirecutter.ogg',
|
||||
|
||||
"Nymph Chirping" = 'sound/misc/nymphchirp.ogg',
|
||||
"Sad Trombone" = 'sound/misc/sadtrombone.ogg',
|
||||
"Honk" = 'sound/items/bikehorn.ogg',
|
||||
)
|
||||
var/selected_sound = null
|
||||
|
||||
/obj/item/weapon/spell/audible_deception/on_use_cast(mob/user)
|
||||
var/list/sound_options = available_sounds
|
||||
if(check_for_scepter())
|
||||
sound_options["!!AIR HORN!!"] = 'sound/items/AirHorn.ogg'
|
||||
var/new_sound = input("Select the sound you want to make.","Sounds") as null|anything in sound_options
|
||||
if(new_sound)
|
||||
selected_sound = sound_options[new_sound]
|
||||
|
||||
/obj/item/weapon/spell/audible_deception/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(selected_sound && pay_energy(200))
|
||||
playsound(T, selected_sound, 80, 1, -1)
|
||||
owner.adjust_instability(1)
|
||||
// Air Horn time.
|
||||
if(selected_sound == 'sound/items/AirHorn.ogg' && pay_energy(3800))
|
||||
owner.adjust_instability(49) // Pay for your sins.
|
||||
for(var/mob/living/carbon/M in ohearers(6, T))
|
||||
if(M.get_ear_protection() >= 2)
|
||||
continue
|
||||
M.sleeping = 0
|
||||
M.stuttering += 20
|
||||
M.ear_deaf += 30
|
||||
M.Weaken(3)
|
||||
if(prob(30))
|
||||
M.Stun(10)
|
||||
M.Paralyse(4)
|
||||
else
|
||||
M.make_jittery(50)
|
||||
M << "<font color='red' size='7'><b>HONK</b></font>"
|
||||
19
code/game/gamemodes/technomancer/spells/aura/aura.dm
Normal file
19
code/game/gamemodes/technomancer/spells/aura/aura.dm
Normal file
@@ -0,0 +1,19 @@
|
||||
/obj/item/weapon/spell/aura
|
||||
name = "aura template"
|
||||
desc = "If you can read me, the game broke! Yay!"
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = null
|
||||
var/glow_color = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/spell/aura/New()
|
||||
..()
|
||||
set_light(7, 4, l_color = glow_color)
|
||||
processing_objects |= src
|
||||
|
||||
/obj/item/weapon/spell/aura/Destroy()
|
||||
processing_objects -= src
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/aura/process()
|
||||
return
|
||||
38
code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm
Normal file
38
code/game/gamemodes/technomancer/spells/aura/biomed_aura.dm
Normal file
@@ -0,0 +1,38 @@
|
||||
/datum/technomancer/spell/biomed_aura
|
||||
name = "Restoration Aura"
|
||||
desc = "Heals you and your allies (or everyone, if you want) of trauma and burns slowly, as long as they remain within four meters."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/aura/biomed
|
||||
ability_icon_state = "tech_biomedaura"
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed
|
||||
name = "restoration aura"
|
||||
desc = "Allows everyone, or just your allies, to slowly regenerate."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_BIOMED
|
||||
glow_color = "#0000FF"
|
||||
var/regen_tick = 0
|
||||
var/heal_allies_only = 1
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed/process()
|
||||
if(!pay_energy(75))
|
||||
qdel(src)
|
||||
regen_tick++
|
||||
if(regen_tick % 5 == 0)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
var/list/mobs_to_heal = list()
|
||||
if(heal_allies_only)
|
||||
for(var/mob/living/L in nearby_mobs)
|
||||
if(is_ally(L))
|
||||
mobs_to_heal |= L
|
||||
else
|
||||
mobs_to_heal = nearby_mobs //Heal everyone!
|
||||
for(var/mob/living/L in mobs_to_heal)
|
||||
L.adjustBruteLoss(-2)
|
||||
L.adjustFireLoss(-2)
|
||||
owner.adjust_instability(2)
|
||||
|
||||
/obj/item/weapon/spell/aura/biomed/on_use_cast(mob/living/user)
|
||||
heal_allies_only = !heal_allies_only
|
||||
user << "Your aura will now heal [heal_allies_only ? "your allies" : "everyone"] near you."
|
||||
56
code/game/gamemodes/technomancer/spells/aura/fire_aura.dm
Normal file
56
code/game/gamemodes/technomancer/spells/aura/fire_aura.dm
Normal file
@@ -0,0 +1,56 @@
|
||||
/datum/technomancer/spell/fire_aura
|
||||
name = "Fire Storm"
|
||||
desc = "This causes everyone within four meters of you to heat up, eventually burning to death if they remain for too long. \
|
||||
This does not affect you or your allies. It also causes a large amount of fire to erupt around you, however the main threat is \
|
||||
still the heating up."
|
||||
enhancement_desc = "Increased heat generation, more fires, and higher temperature cap."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/aura/fire
|
||||
ability_icon_state = "tech_fireaura"
|
||||
|
||||
/obj/item/weapon/spell/aura/fire
|
||||
name = "Fire Storm"
|
||||
desc = "Things are starting to heat up."
|
||||
icon_state = "generic"
|
||||
aspect = ASPECT_FIRE
|
||||
glow_color = "#FF6A00"
|
||||
|
||||
/obj/item/weapon/spell/aura/fire/process()
|
||||
if(!pay_energy(100))
|
||||
qdel(src)
|
||||
var/list/nearby_things = range(4,owner)
|
||||
|
||||
var/fire_prob = 10
|
||||
var/temp_change = 25
|
||||
var/temp_cap = 500
|
||||
var/fire_power = 2
|
||||
|
||||
if(check_for_scepter())
|
||||
temp_change = 50
|
||||
temp_cap = 700
|
||||
fire_prob = 25
|
||||
fire_power = 4
|
||||
for(var/mob/living/carbon/human/H in nearby_things)
|
||||
if(is_ally(H))
|
||||
continue
|
||||
|
||||
var/protection = H.get_heat_protection(1000)
|
||||
if(protection < 1)
|
||||
var/heat_factor = abs(protection - 1)
|
||||
H.bodytemperature = min( (H.bodytemperature + temp_change) * heat_factor, temp_cap)
|
||||
|
||||
turf_check:
|
||||
for(var/turf/simulated/T in nearby_things)
|
||||
if(prob(fire_prob))
|
||||
for(var/mob/living/carbon/human/H in T)
|
||||
if(is_ally(H))
|
||||
continue turf_check
|
||||
T.hotspot_expose(1000, 50, 1)
|
||||
T.create_fire(fire_power)
|
||||
|
||||
|
||||
// //We use hotspot_expose() to allow firesuits to protect from this aura.
|
||||
// var/turf/location = get_turf(H)
|
||||
// location.hotspot_expose(1000, 50, 1)
|
||||
|
||||
owner.adjust_instability(1)
|
||||
42
code/game/gamemodes/technomancer/spells/aura/frost_aura.dm
Normal file
42
code/game/gamemodes/technomancer/spells/aura/frost_aura.dm
Normal file
@@ -0,0 +1,42 @@
|
||||
/datum/technomancer/spell/frost_aura
|
||||
name = "Chilling Aura"
|
||||
desc = "Lowers the core body temperature of everyone around you (except for your friends), causing them to freeze to death if \
|
||||
they stay within four meters of you."
|
||||
enhancement_desc = "The chill becomes lethal."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/aura/frost
|
||||
ability_icon_state = "tech_frostaura"
|
||||
|
||||
/obj/item/weapon/spell/aura/frost
|
||||
name = "chilling aura"
|
||||
desc = "Your enemies will find it hard to chase you if they freeze to death."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_FROST
|
||||
glow_color = "#FF6A00"
|
||||
|
||||
/obj/item/weapon/spell/aura/frost/process()
|
||||
if(!pay_energy(100))
|
||||
qdel(src)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
|
||||
var/temp_change = 25
|
||||
var/temp_cap = 260 // Just above the damage threshold, for humans. Unathi are less fortunate.
|
||||
|
||||
if(check_for_scepter())
|
||||
temp_change = 50
|
||||
temp_cap = 200
|
||||
for(var/mob/living/carbon/human/H in nearby_mobs)
|
||||
if(is_ally(H))
|
||||
continue
|
||||
|
||||
var/protection = H.get_cold_protection(1000)
|
||||
if(protection < 1)
|
||||
var/cold_factor = abs(protection - 1)
|
||||
H.bodytemperature = max( (H.bodytemperature - temp_change) * cold_factor, temp_cap)
|
||||
|
||||
// //We use hotspot_expose() to allow firesuits to protect from this aura.
|
||||
// var/turf/location = get_turf(H)
|
||||
// location.hotspot_expose(1, 50, 1)
|
||||
|
||||
owner.adjust_instability(1)
|
||||
41
code/game/gamemodes/technomancer/spells/aura/shock_aura.dm
Normal file
41
code/game/gamemodes/technomancer/spells/aura/shock_aura.dm
Normal file
@@ -0,0 +1,41 @@
|
||||
/datum/technomancer/spell/shock_aura
|
||||
name = "Electric Aura"
|
||||
desc = "Repeatively electrocutes enemies within four meters of you, as well as nearby electronics."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/aura/shock
|
||||
ability_icon_state = "tech_shockaura"
|
||||
|
||||
/obj/item/weapon/spell/aura/shock
|
||||
name = "electric aura"
|
||||
desc = "Now you are a walking electrical storm."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_SHOCK
|
||||
glow_color = "#0000FF" //TODO
|
||||
|
||||
/obj/item/weapon/spell/aura/shock/process()
|
||||
if(!pay_energy(1000))
|
||||
qdel(src)
|
||||
var/list/nearby_mobs = range(4,owner)
|
||||
var/power = 7
|
||||
if(check_for_scepter())
|
||||
power = 15
|
||||
for(var/obj/machinery/light/light in range(7, owner))
|
||||
light.flicker()
|
||||
for(var/mob/living/L in nearby_mobs)
|
||||
if(is_ally(L))
|
||||
continue
|
||||
|
||||
if(L.isSynthetic())
|
||||
L << "<span class='danger'>ERROR: Electrical fault detected!</span>"
|
||||
L.stuttering += 3
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO))
|
||||
H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected)
|
||||
else
|
||||
L.electrocute_act(power, src, 1.0, BP_TORSO)
|
||||
|
||||
|
||||
owner.adjust_instability(3)
|
||||
@@ -0,0 +1,41 @@
|
||||
/datum/technomancer/spell/unstable_aura
|
||||
name = "Degen Aura"
|
||||
desc = "Destabalizes your enemies, breaking their elements down to their basic levels, slowly killing them from the inside. \
|
||||
For each person within fourteen meters of you, they suffer 1% of their current health every second. Your allies are unharmed."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/aura/unstable
|
||||
ability_icon_state = "tech_unstableaura"
|
||||
|
||||
/obj/item/weapon/spell/aura/unstable
|
||||
name = "degen aura"
|
||||
desc = "Breaks down your entities from the inside."
|
||||
icon_state = "generic"
|
||||
cast_methods = null
|
||||
aspect = ASPECT_UNSTABLE
|
||||
glow_color = "#0000FF" //TODO
|
||||
|
||||
/obj/item/weapon/spell/aura/unstable/process()
|
||||
if(!pay_energy(500))
|
||||
qdel(src)
|
||||
var/list/nearby_mobs = range(14,owner)
|
||||
for(var/mob/living/L in nearby_mobs)
|
||||
if(is_ally(L))
|
||||
continue
|
||||
|
||||
var/damage_to_inflict = max(L.health / L.maxHealth, 0) // Otherwise, those in crit would actually be healed.
|
||||
|
||||
var/armor_factor = abs(L.getarmor(null, "energy") - 100)
|
||||
|
||||
damage_to_inflict = damage_to_inflict * armor_factor
|
||||
|
||||
if(L.isSynthetic())
|
||||
L.adjustBruteLoss(damage_to_inflict)
|
||||
if(damage_to_inflict && prob(10))
|
||||
L << "<span class='danger'>Your chassis seems to slowly be decaying and breaking down.</span>"
|
||||
else
|
||||
L.adjustToxLoss(damage_to_inflict)
|
||||
if(damage_to_inflict && prob(10))
|
||||
L << "<span class='danger'>You feel almost like you're melting from the inside!</span>"
|
||||
|
||||
|
||||
owner.adjust_instability(2)
|
||||
70
code/game/gamemodes/technomancer/spells/blink.dm
Normal file
70
code/game/gamemodes/technomancer/spells/blink.dm
Normal file
@@ -0,0 +1,70 @@
|
||||
/datum/technomancer/spell/blink
|
||||
name = "Blink"
|
||||
desc = "Force the target to teleport a short distance away. This target could be anything from something lying on the ground, to someone trying to \
|
||||
fight you, or even yourself. Using this on someone next to you makes their potential distance after teleportation greater."
|
||||
enhancement_desc = "Blink distance is increased greatly."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/blink
|
||||
|
||||
/obj/item/weapon/spell/blink
|
||||
name = "blink"
|
||||
desc = "Teleports you or someone else a short distance away."
|
||||
icon_state = "blink"
|
||||
cast_methods = CAST_RANGED | CAST_MELEE | CAST_USE
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/proc/safe_blink(atom/movable/AM, var/range = 3)
|
||||
if(AM.anchored || !AM.loc)
|
||||
return
|
||||
var/turf/starting = get_turf(AM)
|
||||
var/list/targets = list()
|
||||
|
||||
valid_turfs:
|
||||
for(var/turf/simulated/T in range(AM, range))
|
||||
if(T.density || istype(T, /turf/simulated/mineral)) //Don't blink to vacuum or a wall
|
||||
continue
|
||||
for(var/atom/movable/stuff in T.contents)
|
||||
if(stuff.density)
|
||||
continue valid_turfs
|
||||
targets.Add(T)
|
||||
|
||||
if(!targets.len)
|
||||
return
|
||||
var/turf/simulated/destination = null
|
||||
|
||||
destination = pick(targets)
|
||||
|
||||
if(destination)
|
||||
if(ismob(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.buckled)
|
||||
L.buckled.unbuckle_mob()
|
||||
AM.forceMove(destination)
|
||||
AM.visible_message("<span class='notice'>\The [AM] vanishes!</span>")
|
||||
AM << "<span class='notice'>You suddenly appear somewhere else!</span>"
|
||||
new /obj/effect/effect/sparks(destination)
|
||||
new /obj/effect/effect/sparks(starting)
|
||||
return
|
||||
|
||||
/obj/item/weapon/spell/blink/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
var/atom/movable/AM = hit_atom
|
||||
if(check_for_scepter())
|
||||
safe_blink(user, 6)
|
||||
else
|
||||
safe_blink(AM, 3)
|
||||
|
||||
/obj/item/weapon/spell/blink/on_use_cast(mob/user)
|
||||
if(check_for_scepter())
|
||||
safe_blink(user, 10)
|
||||
else
|
||||
safe_blink(user, 6)
|
||||
|
||||
/obj/item/weapon/spell/blink/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
var/atom/movable/AM = hit_atom
|
||||
visible_message("<span class='danger'>\The [user] reaches out towards \the [AM] with a glowing hand.</span>")
|
||||
if(check_for_scepter())
|
||||
safe_blink(user, 10)
|
||||
else
|
||||
safe_blink(AM, 6)
|
||||
44
code/game/gamemodes/technomancer/spells/chroma.dm
Normal file
44
code/game/gamemodes/technomancer/spells/chroma.dm
Normal file
@@ -0,0 +1,44 @@
|
||||
/datum/technomancer/spell/chroma
|
||||
name = "Chroma"
|
||||
desc = "Creates light around you, or in a location of your choosing. You can choose what color the light is. This could be \
|
||||
useful to trick someone into believing you're casting a different spell, or perhaps just for fun."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/spell/chroma
|
||||
|
||||
/obj/item/weapon/spell/chroma
|
||||
name = "chroma"
|
||||
desc = "The colors are dazzling."
|
||||
icon_state = "darkness"
|
||||
cast_methods = CAST_RANGED | CAST_USE
|
||||
aspect = ASPECT_LIGHT
|
||||
var/color_to_use = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/spell/chroma/New()
|
||||
..()
|
||||
set_light(6, 5, l_color = color_to_use)
|
||||
|
||||
/obj/effect/chroma
|
||||
name = "chroma"
|
||||
desc = "How are you examining what which cannot be seen?"
|
||||
invisibility = 101
|
||||
var/time_to_die = 2 MINUTES //Despawn after this time, if set.
|
||||
|
||||
/obj/effect/chroma/New(var/newloc, var/new_color = "#FFFFFF")
|
||||
..()
|
||||
set_light(6, 5, l_color = new_color)
|
||||
if(time_to_die)
|
||||
spawn(time_to_die)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/chroma/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(T)
|
||||
new /obj/effect/chroma(T, color_to_use)
|
||||
user << "<span class='notice'>You shift the light onto \the [T].</span>"
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/chroma/on_use_cast(mob/user)
|
||||
var/new_color = input(user, "Choose the color you want your light to be.", "Color selection") as null|color
|
||||
if(new_color)
|
||||
color_to_use = new_color
|
||||
set_light(6, 5, l_color = new_color)
|
||||
34
code/game/gamemodes/technomancer/spells/condensation.dm
Normal file
34
code/game/gamemodes/technomancer/spells/condensation.dm
Normal file
@@ -0,0 +1,34 @@
|
||||
/datum/technomancer/spell/condensation
|
||||
name = "Condensation"
|
||||
desc = "This causes rapid formation of liquid at the target, causing floors to become wet, entities to be soaked, and fires \
|
||||
to be extinguished. You can also fill contains with water if they are targeted directly."
|
||||
ability_icon_state = "tech_condensation"
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/spell/condensation
|
||||
|
||||
/obj/item/weapon/spell/condensation
|
||||
name = "condensation"
|
||||
desc = "Stronger than it appears."
|
||||
icon_state = "condensation"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_AIR
|
||||
cooldown = 2 SECONDS
|
||||
|
||||
/obj/item/weapon/spell/condensation/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(pay_energy(200))
|
||||
if(istype(hit_atom, /turf/simulated))
|
||||
var/turf/simulated/T = hit_atom
|
||||
|
||||
for(var/direction in alldirs + null) // null is for the center tile.
|
||||
spawn(1)
|
||||
var/turf/desired_turf = get_step(T,direction)
|
||||
if(desired_turf) // This shouldn't fail but...
|
||||
var/obj/effect/effect/water/W = PoolOrNew(/obj/effect/effect/water, get_turf(T))
|
||||
W.create_reagents(60)
|
||||
W.reagents.add_reagent(id = "water", amount = 60, data = null, safety = 0)
|
||||
W.set_color()
|
||||
W.set_up(desired_turf)
|
||||
flick(initial(icon_state),W) // Otherwise pooling causes the animation to stay stuck at the end.
|
||||
else if(hit_atom.reagents && !ismob(hit_atom))
|
||||
hit_atom.reagents.add_reagent(id = "water", amount = 60, data = null, safety = 0)
|
||||
adjust_instability(5)
|
||||
164
code/game/gamemodes/technomancer/spells/control.dm
Normal file
164
code/game/gamemodes/technomancer/spells/control.dm
Normal file
@@ -0,0 +1,164 @@
|
||||
/datum/technomancer/spell/control
|
||||
name = "Control"
|
||||
desc = "This function allows you to exert control over simple-minded entities to an extent, such as spiders and carp. \
|
||||
Controlled entities will not be hostile towards you, and you may direct them to move to specific areas or to attack specific \
|
||||
targets. This function will have no effect on entities of higher intelligence, such as humans and similar alien species, as it's \
|
||||
not true mind control, but merely pheromone synthesis for living animals, and electronic hacking for simple robots. The green web \
|
||||
around the entity is merely a hologram used to allow the user to know if the creature is safe or not."
|
||||
cost = 200
|
||||
obj_path = /obj/item/weapon/spell/control
|
||||
|
||||
/mob/living/carbon/human/proc/technomancer_control()
|
||||
place_spell_in_hand(/obj/item/weapon/spell/control)
|
||||
|
||||
/obj/item/weapon/spell/control
|
||||
name = "control"
|
||||
icon_state = "control"
|
||||
desc = "Now you can command your own army!"
|
||||
cast_methods = CAST_RANGED|CAST_USE
|
||||
aspect = ASPECT_BIOMED //Not sure if this should be something else.
|
||||
var/image/control_overlay = null
|
||||
var/list/controlled_mobs = list()
|
||||
var/list/allowed_mobs = list(
|
||||
/mob/living/bot,
|
||||
/mob/living/simple_animal/cat,
|
||||
/mob/living/simple_animal/chick,
|
||||
/mob/living/simple_animal/chicken,
|
||||
/mob/living/simple_animal/corgi,
|
||||
/mob/living/simple_animal/cow,
|
||||
/mob/living/simple_animal/crab,
|
||||
/mob/living/simple_animal/lizard,
|
||||
/mob/living/simple_animal/mouse,
|
||||
/mob/living/simple_animal/parrot,
|
||||
/mob/living/simple_animal/slime,
|
||||
/mob/living/simple_animal/adultslime,
|
||||
/mob/living/simple_animal/tindalos,
|
||||
/mob/living/simple_animal/yithian,
|
||||
/mob/living/simple_animal/hostile/bear,
|
||||
/mob/living/simple_animal/hostile/carp,
|
||||
/mob/living/simple_animal/hostile/scarybat,
|
||||
/mob/living/simple_animal/hostile/viscerator,
|
||||
/mob/living/simple_animal/hostile/retaliate/malf_drone,
|
||||
/mob/living/simple_animal/hostile/giant_spider,
|
||||
/mob/living/simple_animal/hostile/hivebot,
|
||||
/mob/living/simple_animal/hostile/diyaab, //Doubt these will get used but might as well,
|
||||
/mob/living/simple_animal/hostile/samak,
|
||||
/mob/living/simple_animal/hostile/shantak
|
||||
)
|
||||
|
||||
//This unfortunately is gonna be rather messy due to the various mobtypes involved.
|
||||
/obj/item/weapon/spell/control/proc/select(var/mob/living/L)
|
||||
if(!(is_type_in_list(L, allowed_mobs)))
|
||||
return 0
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
SA.ai_inactive = 1
|
||||
SA.friends |= src.owner
|
||||
SA.stance = STANCE_IDLE
|
||||
|
||||
L.overlays |= control_overlay
|
||||
controlled_mobs |= L
|
||||
|
||||
/obj/item/weapon/spell/control/proc/deselect(var/mob/living/L)
|
||||
if(!(L in controlled_mobs))
|
||||
return 0
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
SA.ai_inactive = 1
|
||||
if(istype(SA, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/hostile/SAH = SA
|
||||
SAH.friends.Remove(owner)
|
||||
|
||||
L.overlays.Remove(control_overlay)
|
||||
controlled_mobs.Remove(L)
|
||||
|
||||
/obj/item/weapon/spell/control/proc/move_all(turf/T)
|
||||
for(var/mob/living/living in controlled_mobs)
|
||||
if(living.stat)
|
||||
deselect(living)
|
||||
continue
|
||||
if(istype(living, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = living
|
||||
SA.target_mob = null
|
||||
SA.stance = STANCE_IDLE
|
||||
walk_towards(SA,T,SA.speed)
|
||||
else
|
||||
walk_towards(living,T,5)
|
||||
|
||||
/obj/item/weapon/spell/control/proc/attack_all(mob/target)
|
||||
for(var/mob/living/L in controlled_mobs)
|
||||
if(L.stat)
|
||||
deselect(L)
|
||||
continue
|
||||
if(istype(L, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/hostile/SAH
|
||||
SAH.target_mob = target
|
||||
else if(istype(L, /mob/living/bot))
|
||||
var/mob/living/bot/B = L
|
||||
B.UnarmedAttack(L)
|
||||
|
||||
/obj/item/weapon/spell/control/New()
|
||||
control_overlay = image('icons/obj/spells.dmi',"controlled")
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/control/Destroy()
|
||||
for(var/mob/living/simple_animal/hostile/SM in controlled_mobs)
|
||||
deselect(SM)
|
||||
controlled_mobs = list()
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/control/on_use_cast(mob/living/user)
|
||||
if(controlled_mobs.len != 0)
|
||||
var/choice = alert(user,"Would you like to release control of the entities you are controlling? They won't be friendly \
|
||||
to you anymore if you do this, so be careful.","Release Control?","No","Yes")
|
||||
if(choice == "Yes")
|
||||
for(var/mob/living/L in controlled_mobs)
|
||||
deselect(L)
|
||||
user << "<span class='notice'>You've released control of all entities you had in control.</span>"
|
||||
|
||||
|
||||
/obj/item/weapon/spell/control/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
if(L == user && !controlled_mobs.len)
|
||||
user << "<span class='warning'>This function doesn't work on higher-intelligence entities, however since you're \
|
||||
trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens.</span>"
|
||||
return 0
|
||||
|
||||
if(is_type_in_list(L, allowed_mobs))
|
||||
if(!(L in controlled_mobs)) //Selecting
|
||||
if(L.client)
|
||||
user << "<span class='danger'>\The [L] seems to resist you!</span>"
|
||||
return 0
|
||||
if(pay_energy(1000))
|
||||
select(L)
|
||||
user << "<span class='notice'>\The [L] is now under your (limited) control.</span>"
|
||||
else //Deselect them
|
||||
deselect(L)
|
||||
user << "<span class='notice'>You free \the [L] from your grasp.</span>"
|
||||
|
||||
else //Let's attack
|
||||
if(!controlled_mobs.len)
|
||||
user << "<span class='warning'>You have no entities under your control to command.</span>"
|
||||
return 0
|
||||
if(pay_energy(50 * controlled_mobs.len))
|
||||
attack_all(L)
|
||||
user << "<span class='notice'>You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to \
|
||||
attack \the [L].</span>"
|
||||
//This is to stop someone from controlling beepsky and getting him to stun someone 5 times a second.
|
||||
user.setClickCooldown(8)
|
||||
owner.adjust_instability(controlled_mobs.len)
|
||||
|
||||
else if(isturf(hit_atom))
|
||||
var/turf/T = hit_atom
|
||||
if(!controlled_mobs.len)
|
||||
user << "<span class='warning'>You have no entities under your control to command.</span>"
|
||||
return 0
|
||||
if(pay_energy(50 * controlled_mobs.len))
|
||||
move_all(T)
|
||||
owner.adjust_instability(controlled_mobs.len)
|
||||
user << "<span class='notice'>You command your [controlled_mobs.len > 1 ? "entities" : "[controlled_mobs[1]]"] to move \
|
||||
towards \the [T].</span>"
|
||||
|
||||
22
code/game/gamemodes/technomancer/spells/dispel.dm
Normal file
22
code/game/gamemodes/technomancer/spells/dispel.dm
Normal file
@@ -0,0 +1,22 @@
|
||||
/datum/technomancer/spell/dispel
|
||||
name = "Dispel"
|
||||
desc = "Ends most on-going effects caused by another Technomancer function on the target. Useful if you are worried about \
|
||||
hitting an ally with a deterimental function, if your opponent has similar capabilities to you, or if you're tired of Instability \
|
||||
plaguing you."
|
||||
cost = 75
|
||||
obj_path = /obj/item/weapon/spell/dispel
|
||||
ability_icon_state = "tech_dispel"
|
||||
|
||||
/obj/item/weapon/spell/dispel
|
||||
name = "dispel"
|
||||
desc = "Useful if you're tired of glowing because of a miscast."
|
||||
icon_state = "dispel"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_BIOMED
|
||||
|
||||
/obj/item/weapon/spell/dispel/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
if(isliving(hit_atom) && pay_energy(1000))
|
||||
var/mob/living/target = hit_atom
|
||||
for(var/obj/item/weapon/inserted_spell/I in target)
|
||||
I.on_expire(dispelled = 1)
|
||||
user.adjust_instability(10)
|
||||
201
code/game/gamemodes/technomancer/spells/energy_siphon.dm
Normal file
201
code/game/gamemodes/technomancer/spells/energy_siphon.dm
Normal file
@@ -0,0 +1,201 @@
|
||||
/datum/technomancer/spell/energy_siphon
|
||||
name = "Energy Siphon"
|
||||
desc = "This creates a link to a target that drains electricity, converts it to energy that the Core can use, then absorbs it. \
|
||||
Every second, electricity is stolen until the link is broken by the target moving too far away, or having no more energy left. \
|
||||
Can drain from powercells, microbatteries, and other Cores. The beam created by the siphoning is harmful to touch."
|
||||
enhancement_desc = "Rate of siphoning is doubled."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/energy_siphon
|
||||
ability_icon_state = "tech_energysiphon"
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon
|
||||
name = "energy siphon"
|
||||
desc = "Now you are an energy vampire."
|
||||
icon_state = "energy_siphon"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_SHOCK
|
||||
var/atom/movable/siphoning = null // What the spell is currently draining. Does nothing if null.
|
||||
var/list/things_to_siphon = list() //Things which are actually drained as a result of the above not being null.
|
||||
var/flow_rate = 1000 // Limits how much electricity can be drained per second. Measured by default in god knows what.
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/New()
|
||||
..()
|
||||
processing_objects |= src
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/Destroy()
|
||||
stop_siphoning()
|
||||
processing_objects -= src
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/process()
|
||||
if(!siphoning)
|
||||
return
|
||||
if(!pay_energy(100))
|
||||
owner << "<span class='warning'>You can't afford to maintain the siphon link!</span>"
|
||||
stop_siphoning()
|
||||
return
|
||||
if(get_dist(siphoning, get_turf(src)) > 4)
|
||||
owner << "<span class='warning'>\The [siphoning] is too far to drain from!</span>"
|
||||
stop_siphoning()
|
||||
return
|
||||
if(!(siphoning in view(owner)))
|
||||
owner << "<span class='warning'>\The [siphoning] cannot be seen!</span>"
|
||||
stop_siphoning()
|
||||
return
|
||||
siphon(siphoning, owner)
|
||||
|
||||
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
var/atom/movable/AM = hit_atom
|
||||
populate_siphon_list(AM)
|
||||
if(!things_to_siphon.len)
|
||||
user << "<span class='warning'>You cannot steal energy from \a [AM].</span>"
|
||||
return 0
|
||||
siphoning = AM
|
||||
update_icon()
|
||||
else
|
||||
stop_siphoning()
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/proc/populate_siphon_list(atom/movable/target)
|
||||
things_to_siphon.Cut()
|
||||
things_to_siphon |= target // The recursive check below does not add the object being checked to its list.
|
||||
things_to_siphon |= recursive_content_check(target, things_to_siphon, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1, ignore_show_messages = 1)
|
||||
for(var/atom/movable/AM in things_to_siphon)
|
||||
if(ishuman(AM)) // We can drain FBPs, so we can skip the test below.
|
||||
var/mob/living/carbon/human/H = AM
|
||||
if(H.isSynthetic())
|
||||
continue
|
||||
if(AM.drain_power(1) <= 0) // This checks if whatever's in the list can be drained from.
|
||||
things_to_siphon.Remove(AM)
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/proc/stop_siphoning()
|
||||
siphoning = null
|
||||
things_to_siphon.Cut()
|
||||
update_icon()
|
||||
|
||||
#define SIPHON_CELL_TO_ENERGY 0.5
|
||||
#define SIPHON_FBP_TO_ENERGY 5.0
|
||||
#define SIPHON_CORE_TO_ENERGY 0.5
|
||||
|
||||
// This is called every tick, so long as a link exists between the target and the Technomancer.
|
||||
/obj/item/weapon/spell/energy_siphon/proc/siphon(atom/movable/siphoning, mob/user)
|
||||
var/list/things_to_drain = things_to_siphon // Temporary list copy of what we're gonna steal from.
|
||||
var/charge_to_give = 0 // How much energy to give to the Technomancer at the end.
|
||||
var/flow_remaining = flow_rate
|
||||
|
||||
if(!siphoning)
|
||||
return 0
|
||||
|
||||
update_icon()
|
||||
|
||||
//playsound(source = src, soundin = 'TODO', vol = 30, vary = 0, extrarange = 0, falloff = 0, is_global = 0)
|
||||
|
||||
if(check_for_scepter())
|
||||
flow_remaining = flow_remaining * 2
|
||||
|
||||
// First, we drain normal batteries.
|
||||
if(things_to_drain.len)
|
||||
// Don't bother with empty stuff.
|
||||
for(var/atom/movable/AM in things_to_drain)
|
||||
if(AM.drain_power(1) <= 0)
|
||||
things_to_drain.Remove(AM)
|
||||
if(!things_to_drain.len)
|
||||
return
|
||||
var/charge_to_steal = round(flow_remaining / things_to_drain.len) // This is to drain all the cells evenly.
|
||||
for(var/atom/movable/AM in things_to_drain)
|
||||
var/big_number = AM.drain_power(0,0,charge_to_steal / CELLRATE) // This drains the cell, and leaves us with a big number.
|
||||
flow_remaining = flow_remaining - (big_number * CELLRATE) // Which we reduce to our needed number by multiplying.
|
||||
AM.update_icon() // So guns and batteries will display correctly.
|
||||
charge_to_give = charge_to_give + (flow_rate - flow_remaining) * SIPHON_CELL_TO_ENERGY
|
||||
// If we have 'leftover' flow, let's try to do more.
|
||||
if(round(flow_remaining))
|
||||
if(ishuman(siphoning))
|
||||
var/mob/living/carbon/human/H = siphoning
|
||||
// Let's drain from FBPs. Note that it is possible for the caster to drain themselves if they are an FBP and desperate.
|
||||
if(H.isSynthetic())
|
||||
var/nutrition_to_steal = flow_remaining * 0.025 // Should steal about 25 nutrition per second by default.
|
||||
var/old_nutrition = H.nutrition
|
||||
H.nutrition = max(H.nutrition - nutrition_to_steal, 0)
|
||||
var/nutrition_delta = old_nutrition - H.nutrition
|
||||
charge_to_give += nutrition_delta * SIPHON_FBP_TO_ENERGY
|
||||
flow_remaining = flow_remaining - nutrition_to_steal / 0.025
|
||||
// Let's steal some energy from another Technomancer.
|
||||
if(istype(H.back, /obj/item/weapon/technomancer_core) && H != user)
|
||||
var/obj/item/weapon/technomancer_core/their_core = H.back
|
||||
if(their_core.pay_energy(flow_remaining / 2)) // Don't give energy from nothing.
|
||||
charge_to_give += flow_remaining * SIPHON_CORE_TO_ENERGY
|
||||
flow_remaining = 0
|
||||
|
||||
if(charge_to_give) // Shock anyone standing in the beam.
|
||||
create_lightning(user, siphoning)
|
||||
|
||||
// Now we can actually fill up the core.
|
||||
if(core.energy < core.max_energy)
|
||||
give_energy(charge_to_give)
|
||||
user << "<span class='notice'>Stolen [charge_to_give * CELLRATE] kJ and converted to [charge_to_give] Core energy.</span>"
|
||||
if( (core.max_energy - core.energy) < charge_to_give ) // We have some overflow, if this is true.
|
||||
if(user.isSynthetic()) // Let's do something with it, if we're a robot.
|
||||
charge_to_give = charge_to_give - (core.max_energy - core.energy)
|
||||
user.nutrition = min(user.nutrition + (charge_to_give / SIPHON_FBP_TO_ENERGY), 400)
|
||||
user << "<span class='notice'>Redirected energy to internal microcell.</span>"
|
||||
else
|
||||
user << "<span class='notice'>Stolen [charge_to_give * CELLRATE] kJ.</span>"
|
||||
adjust_instability(2)
|
||||
|
||||
if(flow_remaining == flow_rate) // We didn't drain anything.
|
||||
user << "<span class='warning'>\The [siphoning] cannot be drained any further.</span>"
|
||||
stop_siphoning()
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/update_icon()
|
||||
..()
|
||||
if(siphoning)
|
||||
icon_state = "energy_siphon_drain"
|
||||
else
|
||||
icon_state = "energy_siphon"
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/proc/create_lightning(mob/user, atom/source)
|
||||
if(user && source && user != source)
|
||||
spawn(0)
|
||||
var/i = 7 // process() takes two seconds to tick, this ensures the appearance of a ongoing beam.
|
||||
while(i)
|
||||
var/obj/item/projectile/beam/lightning/energy_siphon/lightning = new(get_turf(source))
|
||||
lightning.firer = user
|
||||
lightning.launch(user)
|
||||
i--
|
||||
sleep(3)
|
||||
|
||||
/obj/item/projectile/beam/lightning/energy_siphon
|
||||
name = "energy stream"
|
||||
icon_state = "lightning"
|
||||
kill_count = 6 // Backup plan in-case the effect somehow misses the Technomancer.
|
||||
power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam.
|
||||
penetrating = 5
|
||||
|
||||
/obj/item/projectile/beam/lightning/energy_siphon/Bump(atom/A as mob|obj|turf|area, forced=0)
|
||||
if(A == firer) // For this, you CAN shoot yourself.
|
||||
on_impact(A)
|
||||
|
||||
density = 0
|
||||
invisibility = 101
|
||||
|
||||
qdel(src)
|
||||
return 1
|
||||
..()
|
||||
|
||||
/obj/item/projectile/beam/lightning/energy_siphon/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
|
||||
if(target_mob == firer) // This shouldn't actually occur due to Bump(), but just in-case.
|
||||
return 1
|
||||
if(ishuman(target_mob)) // Otherwise someone else stood in the beam and is going to pay for it.
|
||||
var/mob/living/carbon/human/H = target_mob
|
||||
var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO))
|
||||
H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected)
|
||||
else
|
||||
target_mob.electrocute_act(power, src, 1.0, BP_TORSO)
|
||||
return 0 // Since this is a continous beam, it needs to keep flying until it hits the Technomancer.
|
||||
|
||||
|
||||
#undef SIPHON_CELL_TO_ENERGY
|
||||
#undef SIPHON_FBP_TO_ENERGY
|
||||
#undef SIPHON_CORE_TO_ENERGY
|
||||
62
code/game/gamemodes/technomancer/spells/flame_tongue.dm
Normal file
62
code/game/gamemodes/technomancer/spells/flame_tongue.dm
Normal file
@@ -0,0 +1,62 @@
|
||||
/datum/technomancer/spell/flame_tongue
|
||||
name = "Flame Tongue"
|
||||
desc = "Using a miniturized flamethrower in your gloves, you can emit a flame strong enough to melt both your enemies and walls."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/flame_tongue
|
||||
ability_icon_state = "tech_flametongue"
|
||||
|
||||
/obj/item/weapon/spell/flame_tongue
|
||||
name = "flame tongue"
|
||||
icon_state = "flame_tongue"
|
||||
desc = "Burn!"
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_FIRE
|
||||
var/obj/item/weapon/weldingtool/spell/welder = null
|
||||
|
||||
/obj/item/weapon/spell/flame_tongue/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#FF6A00")
|
||||
visible_message("<span class='warning'>\The [loc]'s hand begins to emit a flame.</span>")
|
||||
welder = new /obj/item/weapon/weldingtool/spell(src)
|
||||
welder.setWelding(1)
|
||||
|
||||
/obj/item/weapon/spell/flame_tongue/Destroy()
|
||||
qdel(welder)
|
||||
welder = null
|
||||
..()
|
||||
|
||||
/obj/item/weapon/weldingtool/spell
|
||||
name = "flame"
|
||||
|
||||
/obj/item/weapon/weldingtool/spell/process()
|
||||
return
|
||||
|
||||
//Needed to make the spell welder have infinite fuel. Don't worry, it uses energy instead.
|
||||
/obj/item/weapon/weldingtool/spell/remove_fuel()
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/weldingtool/spell/eyecheck(mob/user as mob)
|
||||
return
|
||||
|
||||
/obj/item/weapon/spell/flame_tongue/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
if(isliving(hit_atom) && user.a_intent != I_HELP)
|
||||
var/mob/living/L = hit_atom
|
||||
if(pay_energy(1000))
|
||||
visible_message("<span class='danger'>\The [user] reaches out towards \the [L] with the flaming hand, and they ignite!</span>")
|
||||
L << "<span class='danger'>You ignite!</span>"
|
||||
L.fire_act()
|
||||
owner.adjust_instability(12)
|
||||
else
|
||||
//This is needed in order for the welder to work, and works similarly to grippers.
|
||||
welder.loc = user
|
||||
var/resolved = hit_atom.attackby(welder, user)
|
||||
if(!resolved && welder && hit_atom)
|
||||
if(pay_energy(500))
|
||||
welder.attack(hit_atom, user, def_zone)
|
||||
owner.adjust_instability(4)
|
||||
if(welder && user && (welder.loc == user))
|
||||
welder.loc = src
|
||||
else
|
||||
welder = null
|
||||
qdel(src)
|
||||
return
|
||||
177
code/game/gamemodes/technomancer/spells/illusion.dm
Normal file
177
code/game/gamemodes/technomancer/spells/illusion.dm
Normal file
@@ -0,0 +1,177 @@
|
||||
/datum/technomancer/spell/illusion
|
||||
name = "Illusion"
|
||||
desc = "Allows you to create and control a holographic illusion, that can take the form of most object or entities."
|
||||
enhancement_desc = "Illusions will be made of hard light, allowing the interception of attacks, appearing more realistic."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/illusion
|
||||
ability_icon_state = "tech_illusion"
|
||||
|
||||
/obj/item/weapon/spell/illusion
|
||||
name = "illusion"
|
||||
icon_state = "illusion"
|
||||
desc = "Now you can toy with the minds of the whole colony."
|
||||
aspect = ASPECT_LIGHT
|
||||
cast_methods = CAST_RANGED | CAST_USE
|
||||
var/atom/movable/copied = null
|
||||
var/mob/living/simple_animal/illusion/illusion = null
|
||||
|
||||
/obj/item/weapon/spell/illusion/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
var/atom/movable/AM = hit_atom
|
||||
if(pay_energy(500))
|
||||
copied = AM
|
||||
update_icon()
|
||||
user << "<span class='notice'>You've copied \the [AM]'s appearance.</span>"
|
||||
user << 'sound/weapons/flash.ogg'
|
||||
return 1
|
||||
else if(istype(hit_atom, /turf))
|
||||
var/turf/T = hit_atom
|
||||
if(!illusion)
|
||||
if(!copied)
|
||||
copied = user
|
||||
if(pay_energy(1000)) // 4
|
||||
illusion = new(T)
|
||||
illusion.copy_appearance(copied)
|
||||
if(ishuman(copied))
|
||||
var/mob/living/carbon/human/H = copied
|
||||
// This is to try to have the illusion move at the same rate the real mob world.
|
||||
illusion.step_delay = max(H.movement_delay() + 4, 3)
|
||||
user << "<span class='notice'>An illusion of \the [copied] is made on \the [T].</span>"
|
||||
user << 'sound/effects/pop.ogg'
|
||||
return 1
|
||||
else
|
||||
if(pay_energy(300))
|
||||
spawn(1)
|
||||
illusion.walk_loop(T)
|
||||
|
||||
/obj/item/weapon/spell/illusion/on_use_cast(mob/user)
|
||||
if(illusion)
|
||||
var/what_to_say = input(user, "What do you want \the [illusion] to say?","Illusion Speak") as null|text
|
||||
what_to_say = sanitize(what_to_say)
|
||||
if(what_to_say)
|
||||
illusion.say(what_to_say)
|
||||
|
||||
/obj/item/weapon/spell/illusion/Destroy()
|
||||
if(illusion)
|
||||
qdel(illusion)
|
||||
..()
|
||||
|
||||
// Makes a tiny overlay of the thing the player has copied, so they can easily tell what they currently have.
|
||||
/obj/item/weapon/spell/illusion/update_icon()
|
||||
overlays.Cut()
|
||||
if(copied)
|
||||
var/image/temp_image = image(copied)
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(0.5, 0.5)
|
||||
temp_image.transform = M
|
||||
// temp_image.pixel_y = 8
|
||||
src.overlays.Add(temp_image)
|
||||
|
||||
|
||||
/mob/living/simple_animal/illusion
|
||||
name = "illusion" // gets overwritten
|
||||
desc = "If you can read me, the game broke. Please report this to a coder."
|
||||
resistance = 1000 // holograms are tough
|
||||
wander = 0
|
||||
response_help = "pushes a hand through"
|
||||
response_disarm = "tried to disarm"
|
||||
response_harm = "tried to punch"
|
||||
var/atom/movable/copying = null
|
||||
universal_speak = 1
|
||||
var/realistic = 0
|
||||
var/list/path = list() //Used for AStar pathfinding.
|
||||
var/walking = 0
|
||||
var/step_delay = 10
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/copy_appearance(var/atom/movable/thing_to_copy)
|
||||
if(!thing_to_copy)
|
||||
return 0
|
||||
name = thing_to_copy.name
|
||||
desc = thing_to_copy.desc
|
||||
gender = thing_to_copy.gender
|
||||
appearance = thing_to_copy.appearance
|
||||
copying = thing_to_copy
|
||||
return 1
|
||||
|
||||
// We use special movement code for illusions, because BYOND's default pathfinding will use diagonal movement if it results
|
||||
// in the shortest path. As players are incapable of moving in diagonals, we must do this or else illusions will not be convincing.
|
||||
/mob/living/simple_animal/illusion/proc/calculate_path(var/turf/targeted_loc)
|
||||
if(!path.len || !path)
|
||||
spawn(0)
|
||||
path = AStar(loc, targeted_loc, /turf/proc/CardinalTurfs, /turf/proc/Distance, 0, 10, id = null)
|
||||
if(!path)
|
||||
path = list()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/walk_path(var/turf/targeted_loc)
|
||||
if(path && path.len)
|
||||
step_to(src, path[1])
|
||||
path -= path[1]
|
||||
return
|
||||
else
|
||||
if(targeted_loc)
|
||||
calculate_path(targeted_loc)
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/walk_loop(var/turf/targeted_loc)
|
||||
if(walking) //Already busy moving somewhere else.
|
||||
return 0
|
||||
walking = 1
|
||||
calculate_path(targeted_loc)
|
||||
if(!targeted_loc)
|
||||
walking = 0
|
||||
return 0
|
||||
if(path.len == 0)
|
||||
calculate_path(targeted_loc)
|
||||
while(loc != targeted_loc)
|
||||
walk_path(targeted_loc)
|
||||
sleep(step_delay)
|
||||
walking = 0
|
||||
return 1
|
||||
|
||||
// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but
|
||||
// this is to prevent easy checks from the opposing force.
|
||||
/mob/living/simple_animal/illusion/examine(mob/user)
|
||||
if(copying)
|
||||
copying.examine(user)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/illusion/bullet_act(var/obj/item/projectile/P)
|
||||
if(!P)
|
||||
return
|
||||
|
||||
if(realistic)
|
||||
return ..()
|
||||
|
||||
return PROJECTILE_FORCE_MISS
|
||||
|
||||
/mob/living/simple_animal/illusion/attack_hand(mob/living/carbon/human/M)
|
||||
if(!realistic)
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='warning'>[M]'s hand goes through \the [src]!</span>")
|
||||
return
|
||||
else
|
||||
switch(M.a_intent)
|
||||
|
||||
if(I_HELP)
|
||||
M.visible_message("<span class='notice'>[M] hugs [src] to make \him feel better!</span>", \
|
||||
"<span class='notice'>You hug [src] to make \him feel better!</span>")
|
||||
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
if(I_DISARM)
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] attempted to disarm [src]!</span>")
|
||||
M.do_attack_animation(src)
|
||||
|
||||
if(I_GRAB)
|
||||
..()
|
||||
|
||||
if(I_HURT)
|
||||
adjustBruteLoss(harm_intent_damage)
|
||||
M.visible_message("\red [M] [response_harm] \the [src]")
|
||||
M.do_attack_animation(src)
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/illusion/ex_act()
|
||||
return
|
||||
@@ -0,0 +1,75 @@
|
||||
/datum/technomancer/spell/asphyxiation
|
||||
name = "Asphyxiation"
|
||||
desc = "Launches a projectile at a target. If the projectile hits, a short-lived toxin is created inside what the projectile \
|
||||
hits, which inhibits the delivery of oxygen. The effectiveness of the toxin is heavily dependant on how healthy the target is, \
|
||||
with the target taking more damage the more wounded they are. The effect lasts for twelve seconds."
|
||||
cost = 140
|
||||
obj_path = /obj/item/weapon/spell/insert/asphyxiation
|
||||
|
||||
/obj/item/weapon/spell/insert/asphyxiation
|
||||
name = "asphyxiation"
|
||||
desc = "Now you can cause suffication from afar!"
|
||||
icon_state = "generic"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_BIOMED
|
||||
light_color = "#FF5C5C"
|
||||
inserting = /obj/item/weapon/inserted_spell/asphyxiation
|
||||
|
||||
// maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - halloss
|
||||
|
||||
/obj/item/weapon/inserted_spell/asphyxiation/on_insert()
|
||||
spawn(1)
|
||||
if(ishuman(host))
|
||||
var/mob/living/carbon/human/H = host
|
||||
if(H.isSynthetic() || H.does_not_breathe) // It's hard to choke a robot or something that doesn't breathe.
|
||||
on_expire()
|
||||
return
|
||||
H << "<span class='warning'>You are having difficulty breathing!</span>"
|
||||
var/pulses = 3
|
||||
var/warned_victim = 0
|
||||
while(pulses)
|
||||
if(!warned_victim)
|
||||
warned_victim = predict_crit(pulses, H, 0)
|
||||
sleep(4 SECONDS)
|
||||
H.adjustOxyLoss(5)
|
||||
var/health_lost = H.maxHealth - H.getOxyLoss() + H.getToxLoss() + H.getFireLoss() + H.getBruteLoss() + H.getCloneLoss()
|
||||
H.adjustOxyLoss(round(abs(health_lost * 0.25)))
|
||||
//world << "Inflicted [round(abs(health_lost * 0.25))] damage!"
|
||||
pulses--
|
||||
if(src) //We might've been dispelled at this point and deleted, better safe than sorry.
|
||||
on_expire()
|
||||
|
||||
/obj/item/weapon/inserted_spell/asphyxiation/on_expire()
|
||||
..()
|
||||
|
||||
// if((getOxyLoss() > (species.total_health/2)) || (health <= config.health_threshold_crit))
|
||||
|
||||
/obj/item/weapon/inserted_spell/asphyxiation/proc/predict_crit(var/pulses_remaining, var/mob/living/carbon/human/victim, var/previous_damage = 0)
|
||||
if(pulses_remaining <= 0) // Infinite loop protection
|
||||
return 0
|
||||
var/health_lost
|
||||
var/predicted_damage
|
||||
// First, we sum up all the damage we have.
|
||||
health_lost = victim.getOxyLoss() + victim.getToxLoss() + victim.getFireLoss() + victim.getBruteLoss() + victim.getCloneLoss()
|
||||
// Then add the damage we had done in the last check, if such a number exists, as this is a recursive proc.
|
||||
health_lost += previous_damage
|
||||
// We inflict 25% of the total health loss as oxy damage.
|
||||
predicted_damage = round(abs(health_lost * 0.25))
|
||||
// Add our prediction to previous_damage, so we will remember it for the next iteration.
|
||||
previous_damage = previous_damage + predicted_damage
|
||||
// Now do this again a few more times.
|
||||
if(pulses_remaining)
|
||||
pulses_remaining--
|
||||
return .(pulses_remaining, victim, previous_damage)
|
||||
// Now check if our damage predictions are going to cause the victim to go into crit if no healing occurs.
|
||||
if(previous_damage + health_lost >= victim.maxHealth) // We're probably going to hardcrit
|
||||
victim << "<span class='danger'><font size='3'>A feeling of immense dread starts to overcome you as everything starts \
|
||||
to fade to black...</font></span>"
|
||||
//world << "Predicted hardcrit."
|
||||
return 1
|
||||
else if(predicted_damage >= victim.species.total_health / 2) // Or perhaps we're gonna go into 'oxy crit'.
|
||||
victim << "<span class='danger'>You feel really light-headed, and everything seems to be fading...</span>"
|
||||
//world << "Predicted oxycrit."
|
||||
return 1
|
||||
//If we're at this point, the spell is not going to result in critting.
|
||||
return 0
|
||||
35
code/game/gamemodes/technomancer/spells/insert/corona.dm
Normal file
35
code/game/gamemodes/technomancer/spells/insert/corona.dm
Normal file
@@ -0,0 +1,35 @@
|
||||
/datum/technomancer/spell/corona
|
||||
name = "Corona"
|
||||
desc = "Causes the victim to glow very brightly, which while harmless in itself, makes it easier for them to be hit. The \
|
||||
bright glow also makes it very difficult to be stealthy. The effect lasts for one minute."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/insert/corona
|
||||
ability_icon_state = "tech_corona"
|
||||
|
||||
/obj/item/weapon/spell/insert/corona
|
||||
name = "corona"
|
||||
desc = "How brillient!"
|
||||
icon_state = "radiance"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_LIGHT
|
||||
light_color = "#D9D900"
|
||||
spell_light_intensity = 5
|
||||
spell_light_range = 3
|
||||
inserting = /obj/item/weapon/inserted_spell/corona
|
||||
|
||||
/obj/item/weapon/inserted_spell/corona/on_insert()
|
||||
spawn(1)
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.evasion -= 2
|
||||
L.visible_message("<span class='warning'>You start to glow very brightly!</span>")
|
||||
spawn(1 MINUTE)
|
||||
if(src)
|
||||
on_expire()
|
||||
|
||||
/obj/item/weapon/inserted_spell/corona/on_expire()
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.evasion += 2
|
||||
L << "<span class='notice'>Your glow has ended.</span>"
|
||||
..()
|
||||
34
code/game/gamemodes/technomancer/spells/insert/haste.dm
Normal file
34
code/game/gamemodes/technomancer/spells/insert/haste.dm
Normal file
@@ -0,0 +1,34 @@
|
||||
/datum/technomancer/spell/haste
|
||||
name = "Haste"
|
||||
desc = "Allows the target to run at speeds that should not be possible for an ordinary being. For three seconds, the target \
|
||||
runs extremly fast, and cannot be slowed by any means."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/insert/haste
|
||||
ability_icon_state = "tech_haste"
|
||||
|
||||
/obj/item/weapon/spell/insert/haste
|
||||
name = "haste"
|
||||
desc = "Now you can outrun a Teshari!"
|
||||
icon_state = "haste"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_FORCE
|
||||
light_color = "#FF5C5C"
|
||||
inserting = /obj/item/weapon/inserted_spell/haste
|
||||
|
||||
/obj/item/weapon/inserted_spell/haste/on_insert()
|
||||
spawn(1)
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.force_max_speed = 1
|
||||
L << "<span class='notice'>You suddenly find it much easier to move.</span>"
|
||||
L.adjust_instability(10)
|
||||
spawn(3 SECONDS)
|
||||
if(src)
|
||||
on_expire()
|
||||
|
||||
/obj/item/weapon/inserted_spell/haste/on_expire()
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.force_max_speed = 0
|
||||
L << "<span class='warning'>You feel slow again.</span>"
|
||||
..()
|
||||
56
code/game/gamemodes/technomancer/spells/insert/insert.dm
Normal file
56
code/game/gamemodes/technomancer/spells/insert/insert.dm
Normal file
@@ -0,0 +1,56 @@
|
||||
//Template for spells which put something inside someone else, good for buffs/debuffs, damage over times and heals over time.
|
||||
|
||||
/obj/item/weapon/spell/insert
|
||||
name = "insert template"
|
||||
desc = "Tell a coder if you can read this in-game."
|
||||
icon_state = "purify"
|
||||
cast_methods = CAST_MELEE
|
||||
var/spell_color = "#03A728"
|
||||
var/spell_light_intensity = 2
|
||||
var/spell_light_range = 3
|
||||
var/obj/item/weapon/inserted_spell/inserting = null
|
||||
var/allow_stacking = 0
|
||||
|
||||
/obj/item/weapon/spell/insert/New()
|
||||
..()
|
||||
set_light(spell_light_range, spell_light_intensity, l_color = light_color)
|
||||
|
||||
/obj/item/weapon/inserted_spell
|
||||
var/mob/living/carbon/human/origin = null
|
||||
var/mob/living/host = null
|
||||
|
||||
/obj/item/weapon/inserted_spell/New(var/newloc, var/user, var/obj/item/weapon/spell/insert/inserter)
|
||||
..(newloc)
|
||||
host = newloc
|
||||
origin = user
|
||||
if(light_color)
|
||||
spawn(1)
|
||||
set_light(inserter.spell_light_range, inserter.spell_light_intensity, inserter.spell_color)
|
||||
on_insert()
|
||||
|
||||
/obj/item/weapon/inserted_spell/proc/on_insert()
|
||||
return
|
||||
|
||||
/obj/item/weapon/inserted_spell/proc/on_expire(var/dispelled = 0)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/item/weapon/spell/insert/proc/insert(var/mob/living/L, mob/user)
|
||||
if(inserting)
|
||||
if(!allow_stacking)
|
||||
for(var/obj/item/weapon/inserted_spell/IS in L.contents)
|
||||
if(IS.type == inserting)
|
||||
user << "<span class='warning'>\The [L] is already affected by \the [src].</span>"
|
||||
return
|
||||
new inserting(L,user,src)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/insert/on_melee_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living))
|
||||
var/mob/living/L = hit_atom
|
||||
insert(L,user)
|
||||
|
||||
/obj/item/weapon/spell/insert/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living))
|
||||
var/mob/living/L = hit_atom
|
||||
insert(L,user)
|
||||
27
code/game/gamemodes/technomancer/spells/insert/mend_burns.dm
Normal file
27
code/game/gamemodes/technomancer/spells/insert/mend_burns.dm
Normal file
@@ -0,0 +1,27 @@
|
||||
/datum/technomancer/spell/mend_burns
|
||||
name = "Mend Burns"
|
||||
desc = "Heals minor burns, such as from exposure to flame, electric shock, or lasers."
|
||||
cost = 120
|
||||
obj_path = /obj/item/weapon/spell/insert/mend_burns
|
||||
ability_icon_state = "tech_mendburns"
|
||||
|
||||
/obj/item/weapon/spell/insert/mend_burns
|
||||
name = "mend burns"
|
||||
desc = "Ointment is a thing of the past."
|
||||
icon_state = "mend_burns"
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_BIOMED
|
||||
light_color = "#FF5C5C"
|
||||
inserting = /obj/item/weapon/inserted_spell/mend_burns
|
||||
|
||||
/obj/item/weapon/inserted_spell/mend_burns/on_insert()
|
||||
spawn(1)
|
||||
if(ishuman(host))
|
||||
var/mob/living/carbon/human/H = host
|
||||
for(var/i = 0, i<25,i++)
|
||||
if(H)
|
||||
H.adjustFireLoss(-1)
|
||||
H.adjust_instability(0.5)
|
||||
origin.adjust_instability(0.5)
|
||||
sleep(10)
|
||||
qdel(src)
|
||||
@@ -0,0 +1,27 @@
|
||||
/datum/technomancer/spell/mend_wounds
|
||||
name = "Mend Wounds"
|
||||
desc = "Heals minor wounds, such as cuts, bruises, and other non-lifethreatening injuries."
|
||||
cost = 120
|
||||
obj_path = /obj/item/weapon/spell/insert/mend_wounds
|
||||
ability_icon_state = "tech_mendwounds"
|
||||
|
||||
/obj/item/weapon/spell/insert/mend_wounds
|
||||
name = "mend wounds"
|
||||
desc = "Watch your wounds close up before your eyes."
|
||||
icon_state = "mend_wounds"
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_BIOMED
|
||||
light_color = "#FF5C5C"
|
||||
inserting = /obj/item/weapon/inserted_spell/mend_wounds
|
||||
|
||||
/obj/item/weapon/inserted_spell/mend_wounds/on_insert()
|
||||
spawn(1)
|
||||
if(ishuman(host))
|
||||
var/mob/living/carbon/human/H = host
|
||||
for(var/i = 0, i<25,i++)
|
||||
if(H)
|
||||
H.adjustBruteLoss(-1)
|
||||
H.adjust_instability(0.5)
|
||||
origin.adjust_instability(0.5)
|
||||
sleep(10)
|
||||
qdel(src)
|
||||
33
code/game/gamemodes/technomancer/spells/insert/purify.dm
Normal file
33
code/game/gamemodes/technomancer/spells/insert/purify.dm
Normal file
@@ -0,0 +1,33 @@
|
||||
/datum/technomancer/spell/purify
|
||||
name = "Purify"
|
||||
desc = "Clenses the body of harmful impurities, such as toxins, radiation, viruses, and such."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/insert/purify
|
||||
ability_icon_state = "tech_purify"
|
||||
|
||||
/obj/item/weapon/spell/insert/purify
|
||||
name = "purify"
|
||||
desc = "Illness and toxins will be no more."
|
||||
icon_state = "purify"
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_BIOMED
|
||||
light_color = "#03A728"
|
||||
inserting = /obj/item/weapon/inserted_spell/purify
|
||||
|
||||
/obj/item/weapon/inserted_spell/purify/on_insert()
|
||||
spawn(1)
|
||||
if(ishuman(host))
|
||||
var/mob/living/carbon/human/H = host
|
||||
H.sdisabilities = 0
|
||||
H.disabilities = 0
|
||||
// for(var/datum/disease/D in H.viruses)
|
||||
// D.cure()
|
||||
for(var/i = 0, i<25,i++)
|
||||
if(H)
|
||||
H.adjustToxLoss(-1)
|
||||
H.adjustCloneLoss(-1)
|
||||
H.radiation = max(host.radiation - 2, 0)
|
||||
H.adjust_instability(0.5)
|
||||
origin.adjust_instability(0.5)
|
||||
sleep(10)
|
||||
qdel(src)
|
||||
@@ -0,0 +1,33 @@
|
||||
/datum/technomancer/spell/repel_missiles
|
||||
name = "Repel Missiles"
|
||||
desc = "Places a repulsion field around you, which attempts to deflect incoming bullets and lasers, making them 30% less likely \
|
||||
to hit you. The field lasts for two minutes and can be granted to yourself or an ally."
|
||||
cost = 60
|
||||
obj_path = /obj/item/weapon/spell/insert/repel_missiles
|
||||
ability_icon_state = "tech_repelmissiles"
|
||||
|
||||
/obj/item/weapon/spell/insert/repel_missiles
|
||||
name = "repel missiles"
|
||||
desc = "Use it before they start shooting at you!"
|
||||
icon_state = "generic"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_FORCE
|
||||
light_color = "#FF5C5C"
|
||||
inserting = /obj/item/weapon/inserted_spell/repel_missiles
|
||||
|
||||
/obj/item/weapon/inserted_spell/repel_missiles/on_insert()
|
||||
spawn(1)
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.evasion += 2
|
||||
L << "<span class='notice'>You have a repulsion field around you, which will attempt to deflect projectiles.</span>"
|
||||
spawn(2 MINUTES)
|
||||
if(src)
|
||||
on_expire()
|
||||
|
||||
/obj/item/weapon/inserted_spell/repel_missiles/on_expire()
|
||||
if(isliving(host))
|
||||
var/mob/living/L = host
|
||||
L.evasion -= 2
|
||||
L << "<span class='warning'>Your repulsion field has expired.</span>"
|
||||
..()
|
||||
26
code/game/gamemodes/technomancer/spells/instability_tap.dm
Normal file
26
code/game/gamemodes/technomancer/spells/instability_tap.dm
Normal file
@@ -0,0 +1,26 @@
|
||||
/datum/technomancer/spell/instability_tap
|
||||
name = "Instability Tap"
|
||||
desc = "Creates a large sum of energy, at the cost of a very large amount of instability afflicting you."
|
||||
enhancement_desc = "50% more energy gained, 20% less instability gained."
|
||||
cost = 120
|
||||
obj_path = /obj/item/weapon/spell/instability_tap
|
||||
ability_icon_state = "tech_instabilitytap"
|
||||
|
||||
/obj/item/weapon/spell/instability_tap
|
||||
name = "instability tap"
|
||||
desc = "Short term gain for long term consequences never end bad, right?"
|
||||
cast_methods = CAST_USE
|
||||
aspect = ASPECT_UNSTABLE
|
||||
|
||||
/obj/item/weapon/spell/instability_tap/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#FA58F4")
|
||||
|
||||
/obj/item/weapon/spell/instability_tap/on_use_cast(mob/user)
|
||||
if(check_for_scepter())
|
||||
core.give_energy(7500)
|
||||
owner.adjust_instability(40)
|
||||
else
|
||||
core.give_energy(5000)
|
||||
owner.adjust_instability(50)
|
||||
qdel(src)
|
||||
88
code/game/gamemodes/technomancer/spells/mark_recall.dm
Normal file
88
code/game/gamemodes/technomancer/spells/mark_recall.dm
Normal file
@@ -0,0 +1,88 @@
|
||||
/datum/technomancer/spell/mark
|
||||
name = "Mark"
|
||||
desc = "This function places a specific 'mark' beacon under you, which is used by the Recall function as a destination. \
|
||||
Note that using Mark again will move the destination instead of creating a second destination, and only one destination \
|
||||
can exist, regardless of who casted Mark."
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/spell/mark
|
||||
ability_icon_state = "tech_mark"
|
||||
|
||||
//The object to teleport to when Recall is used.
|
||||
/obj/effect/mark_spell
|
||||
name = "mark"
|
||||
desc = "This is a strange looking disturbance."
|
||||
opacity = 0
|
||||
density = 0
|
||||
anchored = 1
|
||||
|
||||
//This is global, to avoid looping through a list of all objects, or god forbid, looping through world.
|
||||
/var/global/obj/effect/mark_spell/mark_spell_ref = null
|
||||
|
||||
/obj/item/weapon/spell/mark
|
||||
name = "mark"
|
||||
icon_state = "mark"
|
||||
desc = "Marks a specific location to be used by Recall."
|
||||
cast_methods = CAST_USE
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/obj/item/weapon/spell/mark/on_use_cast(mob/living/user)
|
||||
if(pay_energy(1000))
|
||||
if(!mark_spell_ref)
|
||||
mark_spell_ref = new(get_turf(user))
|
||||
user << "<span class='notice'>You mark \the [get_turf(user)] under you.</span>"
|
||||
else
|
||||
mark_spell_ref.forceMove(get_turf(user))
|
||||
user << "<span class='notice'>Your mark is moved from its old position to \the [get_turf(user)] under you.</span>"
|
||||
owner.adjust_instability(5)
|
||||
return 1
|
||||
else
|
||||
user << "<span class='warning'>You can't afford the energy cost!</span>"
|
||||
return 0
|
||||
|
||||
//Recall
|
||||
|
||||
/datum/technomancer/spell/recall
|
||||
name = "Recall"
|
||||
desc = "This function teleports you to where you placed a mark using the Mark function. Without the Mark function, this \
|
||||
function is useless. Note that teleporting takes three seconds. Being incapacitated while teleporting will cancel it."
|
||||
enhancement_desc = "Recall takes two seconds instead of three."
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/spell/recall
|
||||
ability_icon_state = "tech_recall"
|
||||
|
||||
/obj/item/weapon/spell/recall
|
||||
name = "recall"
|
||||
icon_state = "recall"
|
||||
desc = "This will bring you to your Mark."
|
||||
cast_methods = CAST_USE
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/obj/item/weapon/spell/recall/on_use_cast(mob/living/user)
|
||||
if(pay_energy(3000))
|
||||
if(!mark_spell_ref)
|
||||
user << "<span class='danger'>There's no Mark!</span>"
|
||||
return 0
|
||||
else
|
||||
visible_message("<span class='warning'>\The [user] starts glowing!</span>")
|
||||
var/light_intensity = 2
|
||||
var/time_left = 3
|
||||
if(check_for_scepter())
|
||||
time_left = 2
|
||||
while(time_left)
|
||||
if(user.incapacitated())
|
||||
visible_message("<span class='notice'>\The [user]'s glow fades.</span>")
|
||||
user << "<span class='danger'>You cannot Recall while incapacitated!</span>"
|
||||
return 0
|
||||
light_intensity++
|
||||
set_light(light_intensity, light_intensity, l_color = "#006AFF")
|
||||
time_left--
|
||||
sleep(1 SECOND)
|
||||
user.forceMove(get_turf(mark_spell_ref))
|
||||
user << "<span class='notice'>You are teleported to your Mark.</span>"
|
||||
owner.adjust_instability(25)
|
||||
qdel(src)
|
||||
return 1
|
||||
else
|
||||
user << "<span class='warning'>You can't afford the energy cost!</span>"
|
||||
return 0
|
||||
|
||||
30
code/game/gamemodes/technomancer/spells/oxygenate.dm
Normal file
30
code/game/gamemodes/technomancer/spells/oxygenate.dm
Normal file
@@ -0,0 +1,30 @@
|
||||
/datum/technomancer/spell/oxygenate
|
||||
name = "Oxygenate"
|
||||
desc = "This function creates oxygen at a location of your chosing. If used on a humanoid entity, it heals oxygen deprivation. \
|
||||
If casted on the envirnment, air (oxygen and nitrogen) is moved from a distant location to your target."
|
||||
cost = 70
|
||||
obj_path = /obj/item/weapon/spell/oxygenate
|
||||
ability_icon_state = "oxygenate"
|
||||
|
||||
/obj/item/weapon/spell/oxygenate
|
||||
name = "oxygenate"
|
||||
desc = "Atmospherics is obsolete."
|
||||
icon_state = "darkness" //wip
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_AIR
|
||||
cooldown = 30
|
||||
|
||||
/obj/item/weapon/spell/oxygenate/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(ishuman(hit_atom))
|
||||
var/mob/living/carbon/human/H = hit_atom
|
||||
if(pay_energy(1500))
|
||||
H.adjustOxyLoss(-35)
|
||||
owner.adjust_instability(10)
|
||||
return
|
||||
else if(isturf(hit_atom))
|
||||
var/turf/T = hit_atom
|
||||
if(pay_energy(1500))
|
||||
T.assume_gas("oxygen", 200)
|
||||
T.assume_gas("nitrogen", 800)
|
||||
playsound(src.loc, 'sound/effects/spray.ogg', 50, 1, -3)
|
||||
owner.adjust_instability(10)
|
||||
73
code/game/gamemodes/technomancer/spells/passwall.dm
Normal file
73
code/game/gamemodes/technomancer/spells/passwall.dm
Normal file
@@ -0,0 +1,73 @@
|
||||
/datum/technomancer/spell/passwall
|
||||
name = "Passwall"
|
||||
desc = "An uncommon function that allows the user to phase through matter (usually walls) in order to enter or exit a room. Be careful you don't pass into \
|
||||
somewhere dangerous."
|
||||
enhancement_desc = "Cost per tile is halved."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/passwall
|
||||
ability_icon_state = "tech_passwall"
|
||||
|
||||
/obj/item/weapon/spell/passwall
|
||||
name = "passwall"
|
||||
desc = "No walls can hold you back."
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_TELE
|
||||
var/maximum_distance = 20 //Measured in tiles.
|
||||
var/busy = 0
|
||||
|
||||
/obj/item/weapon/spell/passwall/on_melee_cast(atom/hit_atom, mob/user)
|
||||
if(busy) //Prevent someone from trying to get two uses of the spell from one instance.
|
||||
return 0
|
||||
if(!allowed_to_teleport())
|
||||
user << "<span class='warning'>You can't teleport here!</span>"
|
||||
return 0
|
||||
if(isturf(hit_atom))
|
||||
var/turf/T = hit_atom //Turf we touched.
|
||||
var/turf/our_turf = get_turf(user) //Where we are.
|
||||
if(!T.density)
|
||||
user << "<span class='warning'>Perhaps you should try using passWALL on a wall.</span>"
|
||||
return 0
|
||||
var/direction = get_dir(our_turf, T)
|
||||
var/total_cost = 0
|
||||
var/turf/checked_turf = T //Turf we're currently checking for density in the loop below.
|
||||
var/turf/found_turf = null //Our destination, if one is found.
|
||||
var/i = maximum_distance
|
||||
|
||||
visible_message("<span class='info'>[user] rests a hand on \the [T].</span>")
|
||||
busy = 1
|
||||
|
||||
var/datum/effect/effect/system/spark_spread/spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
spark_system.set_up(5, 0, our_turf)
|
||||
|
||||
while(i)
|
||||
checked_turf = get_step(checked_turf, direction) //Advance in the given direction
|
||||
total_cost += check_for_scepter() ? 400 : 800 //Phasing through matter's expensive, you know.
|
||||
i--
|
||||
if(!checked_turf.density) //If we found a destination (a non-dense turf), then we can stop.
|
||||
var/dense_objs_on_turf = 0
|
||||
for(var/atom/movable/stuff in checked_turf.contents) //Make sure nothing dense is where we want to go, like an airlock or window.
|
||||
if(stuff.density)
|
||||
dense_objs_on_turf = 1
|
||||
|
||||
if(!dense_objs_on_turf) //If we found a non-dense turf with nothing dense on it, then that's our destination.
|
||||
found_turf = checked_turf
|
||||
break
|
||||
sleep(10)
|
||||
|
||||
if(found_turf)
|
||||
if(user.loc != our_turf)
|
||||
user << "<span class='warning'>You need to stand still in order to phase through the wall.</span>"
|
||||
return 0
|
||||
if(pay_energy(total_cost) && !user.incapacitated() )
|
||||
visible_message("<span class='warning'>[user] appears to phase through \the [T]!</span>")
|
||||
user << "<span class='info'>You find a destination on the other side of \the [T], and phase through it.</span>"
|
||||
spark_system.start()
|
||||
user.forceMove(found_turf)
|
||||
qdel(src)
|
||||
return 1
|
||||
else
|
||||
user << "<span class='warning'>You don't have enough energy to phase through these walls!</span>"
|
||||
busy = 0
|
||||
else
|
||||
user << "<span class='info'>You weren't able to find an open space to go to.</span>"
|
||||
busy = 0
|
||||
47
code/game/gamemodes/technomancer/spells/phase_shift.dm
Normal file
47
code/game/gamemodes/technomancer/spells/phase_shift.dm
Normal file
@@ -0,0 +1,47 @@
|
||||
/datum/technomancer/spell/phase_shift
|
||||
name = "Phase Shift"
|
||||
desc = "Hides you in the safest possible place, where no harm can come to you. Unfortunately you can only stay inside for a few moments before \
|
||||
draining your powercell."
|
||||
cost = 80
|
||||
obj_path = /obj/item/weapon/spell/phase_shift
|
||||
|
||||
/obj/item/weapon/spell/phase_shift
|
||||
name = "phase shift"
|
||||
desc = "Allows you to dodge your untimely fate by shifting your location somewhere else, so long as you can sustain the energy to do so."
|
||||
cast_methods = CAST_USE
|
||||
aspect = ASPECT_TELE
|
||||
|
||||
/obj/item/weapon/spell/phase_shift/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#FA58F4")
|
||||
|
||||
/obj/effect/phase_shift
|
||||
name = "rift"
|
||||
desc = "There was a maniac here a moment ago..."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "rift"
|
||||
|
||||
/obj/effect/phase_shift/ex_act()
|
||||
return
|
||||
|
||||
/obj/effect/phase_shift/New()
|
||||
..()
|
||||
set_light(3, 5, l_color = "#FA58F4")
|
||||
|
||||
/obj/effect/phase_shift/Destroy()
|
||||
for(var/atom/movable/AM in contents) //Eject everything out.
|
||||
AM.forceMove(get_turf(src))
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/phase_shift/on_use_cast(mob/user)
|
||||
if(isturf(user.loc)) //Check if we're not already in a rift.
|
||||
var/obj/effect/phase_shift/PS = new(get_turf(user))
|
||||
visible_message("<span class='warning'>[user] vanishes into a pink rift!</span>")
|
||||
user << "<span class='info'>You create an unstable rift, and go through it. Be sure to not stay too long.</span>"
|
||||
user.forceMove(PS)
|
||||
else //We're already in a rift, time to get out.
|
||||
if(istype(loc, /obj/effect/phase_shift))
|
||||
var/obj/effect/phase_shift/PS = user.loc
|
||||
qdel(PS) //Ejecting is handled in Destory()
|
||||
visible_message("<span class='warning'>[user] reappears from the rift as it collapses.</span>")
|
||||
qdel(src)
|
||||
23
code/game/gamemodes/technomancer/spells/projectile/beam.dm
Normal file
23
code/game/gamemodes/technomancer/spells/projectile/beam.dm
Normal file
@@ -0,0 +1,23 @@
|
||||
/datum/technomancer/spell/beam
|
||||
name = "Beam"
|
||||
desc = "Fires a laser at your target. Cheap, reliable, and a bit boring."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/projectile/beam
|
||||
|
||||
/obj/item/weapon/spell/projectile/beam
|
||||
name = "beam"
|
||||
icon_state = "beam"
|
||||
desc = "Boring, but practical."
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_LIGHT
|
||||
spell_projectile = /obj/item/projectile/beam/blue
|
||||
energy_cost_per_shot = 500
|
||||
instability_per_shot = 3
|
||||
cooldown = 10
|
||||
|
||||
/obj/item/projectile/beam/blue
|
||||
damage = 20
|
||||
|
||||
muzzle_type = /obj/effect/projectile/laser_blue/muzzle
|
||||
tracer_type = /obj/effect/projectile/laser_blue/tracer
|
||||
impact_type = /obj/effect/projectile/laser_blue/impact
|
||||
@@ -0,0 +1,77 @@
|
||||
/datum/technomancer/spell/chain_lightning
|
||||
name = "Chain Lightning"
|
||||
desc = "This dangerous function shoots lightning that will strike someone, then bounce to a nearby person. Be careful that \
|
||||
it does not bounce to you. The lighting prefers to bounce to people with the least resistance to electricity. It will \
|
||||
strike up to four targets, including yourself if conditions allow it to occur."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/projectile/chain_lightning
|
||||
|
||||
/obj/item/weapon/spell/projectile/chain_lightning
|
||||
name = "chain lightning"
|
||||
icon_state = "chain_lightning"
|
||||
desc = "Fun for the whole security team! Just don't kill yourself in the process.."
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_SHOCK
|
||||
spell_projectile = /obj/item/projectile/beam/chain_lightning
|
||||
energy_cost_per_shot = 3000
|
||||
instability_per_shot = 10
|
||||
cooldown = 20
|
||||
|
||||
/obj/item/projectile/beam/chain_lightning
|
||||
name = "lightning"
|
||||
icon_state = "lightning"
|
||||
nodamage = 1
|
||||
damage_type = HALLOSS
|
||||
|
||||
muzzle_type = /obj/effect/projectile/lightning/muzzle
|
||||
tracer_type = /obj/effect/projectile/lightning/tracer
|
||||
impact_type = /obj/effect/projectile/lightning/impact
|
||||
|
||||
var/bounces = 3 //How many times it 'chains'. Note that the first hit is not counted as it counts /bounces/.
|
||||
var/list/hit_mobs = list() //Mobs which were already hit.
|
||||
var/power = 20 //How hard it will hit for with electrocute_act(), decreases with each bounce.
|
||||
|
||||
/obj/item/projectile/beam/chain_lightning/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
|
||||
//First we shock the guy we just hit.
|
||||
if(ishuman(target_mob))
|
||||
var/mob/living/carbon/human/H = target_mob
|
||||
var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO))
|
||||
H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected)
|
||||
else
|
||||
target_mob.electrocute_act(power, src, 1.0, BP_TORSO)
|
||||
hit_mobs |= target_mob
|
||||
|
||||
//Each bounce reduces the damage of the bolt.
|
||||
power = power * 0.80
|
||||
if(bounces)
|
||||
//All possible targets.
|
||||
var/list/potential_targets = view(target_mob, 3)
|
||||
|
||||
//Filtered targets, so we don't hit the same person twice.
|
||||
var/list/filtered_targets = list()
|
||||
for(var/mob/living/L in potential_targets)
|
||||
if(L in hit_mobs)
|
||||
continue
|
||||
filtered_targets |= L
|
||||
|
||||
var/mob/living/new_target = null
|
||||
var/siemens_comparison = 0
|
||||
|
||||
for(var/mob/living/carbon/human/H in filtered_targets)
|
||||
var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO))
|
||||
var/their_siemens = H.get_siemens_coefficient_organ(affected)
|
||||
if(their_siemens > siemens_comparison) //We want as conductive as possible, so higher is better.
|
||||
new_target = H
|
||||
siemens_comparison = their_siemens
|
||||
|
||||
if(new_target)
|
||||
var/turf/curloc = get_turf(target_mob)
|
||||
curloc.visible_message("<span class='danger'>\The [src] bounces to \the [new_target]!</span>")
|
||||
redirect(new_target.x, new_target.y, curloc, firer)
|
||||
bounces--
|
||||
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/datum/technomancer/spell/force_missile
|
||||
name = "Force Missile"
|
||||
desc = "This fires a missile at your target. It's cheap to use, however the projectile itself moves and impacts in such a way \
|
||||
that armor designed to protect from blunt force will mitigate this function as well."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/projectile/force_missile
|
||||
|
||||
/obj/item/weapon/spell/projectile/force_missile
|
||||
name = "force missile"
|
||||
icon_state = "force_missile"
|
||||
desc = "Make it rain!"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_FORCE
|
||||
spell_projectile = /obj/item/projectile/force_missile
|
||||
energy_cost_per_shot = 400
|
||||
instability_per_shot = 2
|
||||
cooldown = 10
|
||||
|
||||
/obj/item/projectile/force_missile
|
||||
name = "force missile"
|
||||
icon_state = "force_missile"
|
||||
damage = 20
|
||||
damage_type = BRUTE
|
||||
check_armour = "melee"
|
||||
@@ -0,0 +1,40 @@
|
||||
/datum/technomancer/spell/lightning
|
||||
name = "Lightning Strike"
|
||||
desc = "This uses a hidden electrolaser, which creates a laser beam to ionize the enviroment, allowing for ideal conditions \
|
||||
for a directed lightning strike to occur. The lightning is very strong, however it requires a few seconds to prepare a \
|
||||
strike."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/projectile/lightning
|
||||
|
||||
/obj/item/weapon/spell/projectile/lightning
|
||||
name = "lightning strike"
|
||||
icon_state = "lightning_strike"
|
||||
desc = "Now you can feel like Zeus."
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_SHOCK
|
||||
spell_projectile = /obj/item/projectile/beam/lightning
|
||||
energy_cost_per_shot = 2500
|
||||
instability_per_shot = 10
|
||||
cooldown = 20
|
||||
pre_shot_delay = 20
|
||||
|
||||
/obj/item/projectile/beam/lightning
|
||||
name = "lightning"
|
||||
icon_state = "lightning"
|
||||
nodamage = 1
|
||||
damage_type = HALLOSS
|
||||
|
||||
muzzle_type = /obj/effect/projectile/lightning/muzzle
|
||||
tracer_type = /obj/effect/projectile/lightning/tracer
|
||||
impact_type = /obj/effect/projectile/lightning/impact
|
||||
|
||||
var/power = 60 //How hard it will hit for with electrocute_act().
|
||||
|
||||
/obj/item/projectile/beam/lightning/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0)
|
||||
if(ishuman(target_mob))
|
||||
var/mob/living/carbon/human/H = target_mob
|
||||
var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO))
|
||||
H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected)
|
||||
else
|
||||
target_mob.electrocute_act(power, src, 1.0, BP_TORSO)
|
||||
return 1
|
||||
@@ -0,0 +1,42 @@
|
||||
/datum/technomancer/spell/overload
|
||||
name = "Overload"
|
||||
desc = "Fires a bolt of highly unstable energy, that does damaged equal to 1.5% of the technomancer's current reserve of energy. \
|
||||
This energy pierces all known armor."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/projectile/overload
|
||||
|
||||
/obj/item/weapon/spell/projectile/overload
|
||||
name = "overload"
|
||||
icon_state = "overload"
|
||||
desc = "Hope your Core's full."
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_UNSTABLE
|
||||
spell_projectile = /obj/item/projectile/overload
|
||||
energy_cost_per_shot = 0 // Handled later
|
||||
instability_per_shot = 15
|
||||
cooldown = 10
|
||||
pre_shot_delay = 4
|
||||
|
||||
/obj/item/projectile/overload
|
||||
name = "overloaded bolt"
|
||||
icon_state = "bluespace"
|
||||
// nodamage = 1
|
||||
damage_type = BURN
|
||||
|
||||
/obj/item/weapon/spell/projectile/overload/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
energy_cost_per_shot = round(core.max_energy * 0.15)
|
||||
var/energy_before_firing = core.energy
|
||||
if(set_up(hit_atom, user))
|
||||
var/obj/item/projectile/overload/P = new spell_projectile(get_turf(user))
|
||||
P.launch(hit_atom)
|
||||
if(check_for_scepter())
|
||||
P.damage = round(energy_before_firing * 0.003) // 3% of their current energy pool.
|
||||
else
|
||||
P.damage = round(energy_before_firing * 0.002) // 2% of their current energy pool.
|
||||
owner.adjust_instability(instability_per_shot)
|
||||
return 1
|
||||
|
||||
|
||||
// muzzle_type = /obj/effect/projectile/lightning/muzzle
|
||||
// tracer_type = /obj/effect/projectile/lightning/tracer
|
||||
// impact_type = /obj/effect/projectile/lightning/impact
|
||||
@@ -0,0 +1,30 @@
|
||||
/obj/item/weapon/spell/projectile
|
||||
name = "projectile template"
|
||||
icon_state = "generic"
|
||||
desc = "This is a generic template that shoots projectiles. If you can read this, the game broke!"
|
||||
cast_methods = CAST_RANGED
|
||||
var/obj/item/projectile/spell_projectile = null
|
||||
var/energy_cost_per_shot = 0
|
||||
var/instability_per_shot = 0
|
||||
var/pre_shot_delay = 0
|
||||
|
||||
/obj/item/weapon/spell/projectile/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(set_up(hit_atom, user))
|
||||
var/obj/item/projectile/new_projectile = new spell_projectile(get_turf(user))
|
||||
new_projectile.launch(T)
|
||||
owner.adjust_instability(instability_per_shot)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/spell/projectile/proc/set_up(atom/hit_atom, mob/living/user)
|
||||
if(spell_projectile)
|
||||
if(pay_energy(energy_cost_per_shot))
|
||||
if(pre_shot_delay)
|
||||
var/image/target_image = image(icon = 'icons/obj/spells.dmi', loc = get_turf(hit_atom), icon_state = "target")
|
||||
user << target_image
|
||||
user.Stun(pre_shot_delay)
|
||||
sleep(pre_shot_delay)
|
||||
qdel(target_image)
|
||||
return 1
|
||||
return 0
|
||||
44
code/game/gamemodes/technomancer/spells/radiance.dm
Normal file
44
code/game/gamemodes/technomancer/spells/radiance.dm
Normal file
@@ -0,0 +1,44 @@
|
||||
/datum/technomancer/spell/radiance
|
||||
name = "Radiance"
|
||||
desc = "Causes you to be very radiant, glowing brightly in visible light, thermal energy, and deadly ionizing radiation."
|
||||
cost = 180
|
||||
obj_path = /obj/item/weapon/spell/radiance
|
||||
|
||||
/obj/item/weapon/spell/radiance
|
||||
name = "radiance"
|
||||
desc = "You will glow with a radiance similar to that of Supermatter."
|
||||
icon_state = "radiance"
|
||||
aspect = ASPECT_LIGHT
|
||||
var/power = 100
|
||||
toggled = 1
|
||||
|
||||
/obj/item/weapon/spell/radiance/New()
|
||||
..()
|
||||
set_light(7, 4, l_color = "#D9D900")
|
||||
processing_objects |= src
|
||||
|
||||
/obj/item/weapon/spell/radiance/Destroy()
|
||||
processing_objects -= src
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/radiance/process()
|
||||
var/turf/T = get_turf(src)
|
||||
var/datum/gas_mixture/removed = null
|
||||
var/datum/gas_mixture/env = null
|
||||
|
||||
if(!istype(T, /turf/space))
|
||||
env = T.return_air()
|
||||
removed = env.remove(0.25 * env.total_moles) //Remove gas from surrounding area
|
||||
|
||||
var/thermal_power = 300 * power
|
||||
|
||||
removed.add_thermal_energy(thermal_power)
|
||||
removed.temperature = between(0, removed.temperature, 10000)
|
||||
|
||||
env.merge(removed)
|
||||
|
||||
for(var/mob/living/L in range(T, round(sqrt(power / 2))))
|
||||
var/radius = max(get_dist(L, src), 1)
|
||||
var/rads = (power / 10) * ( 1 / (radius**2) )
|
||||
L.apply_effect(rads, IRRADIATE)
|
||||
owner.adjust_instability(2)
|
||||
93
code/game/gamemodes/technomancer/spells/reflect.dm
Normal file
93
code/game/gamemodes/technomancer/spells/reflect.dm
Normal file
@@ -0,0 +1,93 @@
|
||||
/datum/technomancer/spell/reflect
|
||||
name = "Reflect"
|
||||
desc = "Emits a protective shield fron your hand in front of you, which will reflect one attack back at the attacker."
|
||||
cost = 120
|
||||
obj_path = /obj/item/weapon/spell/reflect
|
||||
ability_icon_state = "tech_reflect"
|
||||
|
||||
/obj/item/weapon/spell/reflect
|
||||
name = "\proper reflect shield"
|
||||
icon_state = "reflect"
|
||||
desc = "A very protective combat shield that'll reflect the next attack at the unfortunate person who tried to shoot you."
|
||||
aspect = ASPECT_FORCE
|
||||
toggled = 1
|
||||
var/reflecting = 0
|
||||
var/damage_to_energy_multiplier = 60.0 //Determines how much energy to charge for blocking, e.g. 20 damage attack = 1200 energy cost
|
||||
var/datum/effect/effect/system/spark_spread/spark_system = null
|
||||
|
||||
/obj/item/weapon/spell/reflect/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#006AFF")
|
||||
spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
spark_system.set_up(5, 0, src)
|
||||
owner << "<span class='notice'>Your shield will expire in 3 seconds!</span>"
|
||||
spawn(3 SECONDS)
|
||||
if(src)
|
||||
owner << "<span class='danger'>Your shield expires!</span>"
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/reflect/Destroy()
|
||||
spark_system = null
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/reflect/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
if(user.incapacitated())
|
||||
return 0
|
||||
|
||||
var/damage_to_energy_cost = (damage_to_energy_multiplier * damage)
|
||||
|
||||
if(!pay_energy(damage_to_energy_cost))
|
||||
owner << "<span class='danger'>Your shield fades due to lack of energy!</span>"
|
||||
qdel(src)
|
||||
return 0
|
||||
|
||||
//block as long as they are not directly behind us
|
||||
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
|
||||
if(check_shield_arc(user, bad_arc, damage_source, attacker))
|
||||
|
||||
if(istype(damage_source, /obj/item/projectile))
|
||||
var/obj/item/projectile/P = damage_source
|
||||
|
||||
if(P.starting && !P.reflected)
|
||||
visible_message("<span class='danger'>\The [user]'s [src.name] reflects [attack_text]!</span>")
|
||||
|
||||
var/turf/curloc = get_turf(user)
|
||||
|
||||
// redirect the projectile
|
||||
P.redirect(P.starting.x, P.starting.y, curloc, user)
|
||||
P.reflected = 1
|
||||
if(check_for_scepter())
|
||||
P.damage = P.damage * 1.5
|
||||
|
||||
spark_system.start()
|
||||
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
|
||||
// now send a log so that admins don't think they're shooting themselves on purpose.
|
||||
log_and_message_admins("[user] reflected [attacker]'s attack back at them.")
|
||||
|
||||
if(!reflecting)
|
||||
reflecting = 1
|
||||
spawn(1 SECOND) //To ensure that most or all of a burst fire cycle is reflected.
|
||||
owner << "<span class='danger'>Your shield fades due being used up!</span>"
|
||||
qdel(src)
|
||||
|
||||
return PROJECTILE_CONTINUE // complete projectile permutation
|
||||
|
||||
else if(istype(damage_source, /obj/item/weapon))
|
||||
var/obj/item/weapon/W = damage_source
|
||||
if(attacker)
|
||||
W.attack(attacker)
|
||||
attacker << "<span class='danger'>Your [damage_source.name] goes through \the [src] in one location, comes out \
|
||||
on the same side, and hits you!</span>"
|
||||
|
||||
spark_system.start()
|
||||
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
|
||||
|
||||
log_and_message_admins("[user] reflected [attacker]'s attack back at them.")
|
||||
|
||||
if(!reflecting)
|
||||
reflecting = 1
|
||||
spawn(1 SECOND) //To ensure that most or all of a burst fire cycle is reflected.
|
||||
owner << "<span class='danger'>Your shield fades due being used up!</span>"
|
||||
qdel(src)
|
||||
return 1
|
||||
return 0
|
||||
61
code/game/gamemodes/technomancer/spells/resurrect.dm
Normal file
61
code/game/gamemodes/technomancer/spells/resurrect.dm
Normal file
@@ -0,0 +1,61 @@
|
||||
/datum/technomancer/spell/resurrect
|
||||
name = "Resurrect"
|
||||
desc = "This function injects various regenetive medical compounds and nanomachines, in an effort to restart the body, \
|
||||
however this must be done soon after they die, as this will have no effect on people who have died long ago. It also doesn't \
|
||||
resolve whatever caused them to die originally."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/resurrect
|
||||
ability_icon_state = "tech_resurrect"
|
||||
|
||||
/obj/item/weapon/spell/resurrect
|
||||
name = "resurrect"
|
||||
icon_state = "radiance"
|
||||
desc = "Perhaps this can save a trip to cloning?"
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_BIOMED
|
||||
|
||||
/obj/item/weapon/spell/resurrect/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
if(L == user)
|
||||
user << "<span class='warning'>Clever as you may seem, this won't work on yourself while alive.</span>"
|
||||
return 0
|
||||
if(L.stat != DEAD)
|
||||
user << "<span class='warning'>\The [L] isn't dead!</span>"
|
||||
return 0
|
||||
if(pay_energy(5000))
|
||||
if(L.tod > world.time + 10 MINUTES)
|
||||
user << "<span class='danger'>\The [L]'s been dead for too long, even this function cannot replace cloning at \
|
||||
this point.</span>"
|
||||
return 0
|
||||
user << "<span class='notice'>You stab \the [L] with a hidden integrated hypo, attempting to bring them back...</span>"
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SM = L
|
||||
SM.health = SM.maxHealth / 3
|
||||
SM.stat = CONSCIOUS
|
||||
dead_mob_list -= SM
|
||||
living_mob_list += SM
|
||||
SM.icon_state = SM.icon_living
|
||||
owner.adjust_instability(30)
|
||||
else if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
|
||||
if(!H.client && H.mind) //Don't force the dead person to come back if they don't want to.
|
||||
for(var/mob/observer/dead/ghost in player_list)
|
||||
if(ghost.mind == H.mind)
|
||||
ghost << "<b><font color = #330033><font size = 3>The Technomancer [user.real_name] is trying to \
|
||||
revive you. Return to your body if you want to be resurrected!</b> \
|
||||
(Verbs -> Ghost -> Re-enter corpse)</font></font>"
|
||||
break
|
||||
|
||||
sleep(10 SECONDS)
|
||||
if(H.client)
|
||||
L.stat = CONSCIOUS //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again.
|
||||
dead_mob_list -= H
|
||||
living_mob_list += H
|
||||
visible_message("<span class='danger'>\The [H]'s eyes open!</span>")
|
||||
user << "<span class='notice'>It's alive!</span>"
|
||||
owner.adjust_instability(100)
|
||||
else
|
||||
user << "<span class='warning'>The body of \the [H] doesn't seem to respond, perhaps you could try again?</span>"
|
||||
owner.adjust_instability(10)
|
||||
27
code/game/gamemodes/technomancer/spells/shared_burden.dm
Normal file
27
code/game/gamemodes/technomancer/spells/shared_burden.dm
Normal file
@@ -0,0 +1,27 @@
|
||||
/datum/technomancer/spell/shared_burden
|
||||
name = "Shared Burden"
|
||||
desc = "One of the few functions able to adjust instability, this allows you to take someone else's instability."
|
||||
cost = 50
|
||||
obj_path = /obj/item/weapon/spell/shared_burden
|
||||
|
||||
/obj/item/weapon/spell/shared_burden
|
||||
name = "shared burden"
|
||||
icon_state = "shared_burden"
|
||||
desc = "Send instability from the target to you, for whatever reason you'd want to."
|
||||
cast_methods = CAST_MELEE
|
||||
aspect = ASPECT_UNSTABLE
|
||||
|
||||
/obj/item/weapon/spell/shared_burden/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
if(ishuman(hit_atom))
|
||||
var/mob/living/carbon/human/H = hit_atom
|
||||
if(H == user)
|
||||
user << "<span class='warning'>Draining instability out of you to put it back seems a bit pointless.</span>"
|
||||
return 0
|
||||
if(!H.instability)
|
||||
user << "<span class='warning'>\The [H] has no instability to drain.</span>"
|
||||
return 0
|
||||
if(pay_energy(500))
|
||||
var/instability_to_drain = min(H.instability, 25)
|
||||
user << "<span class='notice'>You draw instability away from \the [H] and towards you.</span>"
|
||||
owner.adjust_instability(instability_to_drain)
|
||||
H.adjust_instability(-instability_to_drain)
|
||||
55
code/game/gamemodes/technomancer/spells/shield.dm
Normal file
55
code/game/gamemodes/technomancer/spells/shield.dm
Normal file
@@ -0,0 +1,55 @@
|
||||
/datum/technomancer/spell/shield
|
||||
name = "Shield"
|
||||
desc = "Emits a protective shield fron your hand in front of you, which will protect you from almost anything able to harm \
|
||||
you, so long as you can power it."
|
||||
cost = 120
|
||||
obj_path = /obj/item/weapon/spell/shield
|
||||
ability_icon_state = "tech_shield"
|
||||
|
||||
/obj/item/weapon/spell/shield
|
||||
name = "\proper energy shield"
|
||||
icon_state = "shield"
|
||||
desc = "A very protective combat shield that'll stop almost anything from hitting you, at least from the front."
|
||||
aspect = ASPECT_FORCE
|
||||
toggled = 1
|
||||
var/damage_to_energy_multiplier = 30.0 //Determines how much energy to charge for blocking, e.g. 20 damage attack = 600 energy cost
|
||||
var/datum/effect/effect/system/spark_spread/spark_system = null
|
||||
|
||||
/obj/item/weapon/spell/shield/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#006AFF")
|
||||
spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
spark_system.set_up(5, 0, src)
|
||||
|
||||
/obj/item/weapon/spell/shield/Destroy()
|
||||
spark_system = null
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
if(user.incapacitated())
|
||||
return 0
|
||||
|
||||
var/damage_to_energy_cost = damage_to_energy_multiplier * damage
|
||||
|
||||
if(issmall(user)) // Smaller shields are more efficent.
|
||||
damage_to_energy_cost *= 0.75
|
||||
|
||||
if(istype(owner.get_other_hand(src), src.type)) // Two shields in both hands.
|
||||
damage_to_energy_cost *= 0.75
|
||||
|
||||
else if(check_for_scepter())
|
||||
damage_to_energy_cost *= 0.50
|
||||
|
||||
if(!pay_energy(damage_to_energy_cost))
|
||||
owner << "<span class='danger'>Your shield fades due to lack of energy!</span>"
|
||||
qdel(src)
|
||||
return 0
|
||||
|
||||
//block as long as they are not directly behind us
|
||||
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
|
||||
if(check_shield_arc(user, bad_arc, damage_source, attacker))
|
||||
user.visible_message("<span class='danger'>\The [user]'s [src] blocks [attack_text]!</span>")
|
||||
spark_system.start()
|
||||
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
|
||||
return 1
|
||||
return 0
|
||||
28
code/game/gamemodes/technomancer/spells/spawner/darkness.dm
Normal file
28
code/game/gamemodes/technomancer/spells/spawner/darkness.dm
Normal file
@@ -0,0 +1,28 @@
|
||||
/datum/technomancer/spell/darkness
|
||||
name = "Darkness"
|
||||
desc = "Disrupts photons moving in a local area, causing darkness to shroud yourself or a position of your choosing."
|
||||
cost = 30
|
||||
obj_path = /obj/item/weapon/spell/spawner/darkness
|
||||
|
||||
/obj/item/weapon/spell/spawner/darkness
|
||||
name = "darkness"
|
||||
desc = "Not even light can stand in your way now."
|
||||
icon_state = "darkness"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_DARK
|
||||
spawner_type = /obj/effect/temporary_effect/darkness
|
||||
|
||||
/obj/item/weapon/spell/spawner/darkness/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(pay_energy(500))
|
||||
owner.adjust_instability(4)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/spawner/darkness/New()
|
||||
..()
|
||||
set_light(6, -5, l_color = "#FFFFFF")
|
||||
|
||||
/obj/effect/temporary_effect/darkness
|
||||
name = "darkness"
|
||||
time_to_die = 2 MINUTES
|
||||
new_light_range = 6
|
||||
new_light_power = -5
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/technomancer/spell/fire_blast
|
||||
name = "Fire Blast"
|
||||
desc = "Causes a disturbance on a targeted tile. After two and a half seconds, it will explode in a small radius around it. Be \
|
||||
sure to not be close to the disturbance yourself."
|
||||
cost = 175
|
||||
obj_path = /obj/item/weapon/spell/spawner/fire_blast
|
||||
|
||||
/obj/item/weapon/spell/spawner/fire_blast
|
||||
name = "fire blast"
|
||||
desc = "Leading your booms might be needed."
|
||||
icon_state = "fire_blast"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_FIRE
|
||||
spawner_type = /obj/effect/temporary_effect/fire_blast
|
||||
|
||||
/obj/item/weapon/spell/spawner/fire_blast/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(pay_energy(2000))
|
||||
owner.adjust_instability(12)
|
||||
..() // Makes the booms happen.
|
||||
|
||||
/obj/effect/temporary_effect/fire_blast
|
||||
name = "fire blast"
|
||||
desc = "Run!"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "at_shield1"
|
||||
time_to_die = 2.5 SECONDS // After which we go boom.
|
||||
invisibility = 0
|
||||
new_light_range = 4
|
||||
new_light_power = 5
|
||||
new_light_color = "#FF6A00"
|
||||
|
||||
/obj/effect/temporary_effect/fire_blast/Destroy()
|
||||
explosion(get_turf(src), -1, 1, 2, 5, adminlog = 1)
|
||||
..()
|
||||
49
code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
Normal file
49
code/game/gamemodes/technomancer/spells/spawner/pulsar.dm
Normal file
@@ -0,0 +1,49 @@
|
||||
/datum/technomancer/spell/pulsar
|
||||
name = "Pulsar"
|
||||
desc = "Emits electronic pulses to destroy, disable, or otherwise harm devices and machines. Be sure to not hit yourself with this."
|
||||
cost = 150
|
||||
obj_path = /obj/item/weapon/spell/spawner/pulsar
|
||||
|
||||
/obj/item/weapon/spell/spawner/pulsar
|
||||
name = "pulsar"
|
||||
desc = "Be sure to not hit yourself!"
|
||||
icon_state = "radiance"
|
||||
cast_methods = CAST_RANGED | CAST_THROW
|
||||
aspect = ASPECT_EMP
|
||||
spawner_type = /obj/effect/temporary_effect/pulsar
|
||||
|
||||
/obj/item/weapon/spell/spawner/pulsar/New()
|
||||
..()
|
||||
set_light(3, 2, l_color = "#2ECCFA")
|
||||
|
||||
/obj/item/weapon/spell/spawner/pulsar/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(pay_energy(4000))
|
||||
owner.adjust_instability(8)
|
||||
..()
|
||||
|
||||
/obj/item/weapon/spell/spawner/pulsar/on_throw_cast(atom/hit_atom, mob/user)
|
||||
empulse(hit_atom, 1, 1, log=1)
|
||||
|
||||
/obj/effect/temporary_effect/pulsar
|
||||
name = "pulsar"
|
||||
desc = "Not a real pulsar, but still emits loads of EMP."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "shield2"
|
||||
time_to_die = null
|
||||
invisibility = 0
|
||||
new_light_range = 4
|
||||
new_light_power = 5
|
||||
new_light_color = "#2ECCFA"
|
||||
var/pulses_remaining = 3
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/New()
|
||||
..()
|
||||
pulse_loop()
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/proc/pulse_loop()
|
||||
while(pulses_remaining)
|
||||
sleep(2 SECONDS)
|
||||
empulse(src, heavy_range = 1, light_range = 2, log = 1)
|
||||
pulses_remaining--
|
||||
qdel(src)
|
||||
|
||||
30
code/game/gamemodes/technomancer/spells/spawner/spawner.dm
Normal file
30
code/game/gamemodes/technomancer/spells/spawner/spawner.dm
Normal file
@@ -0,0 +1,30 @@
|
||||
/obj/item/weapon/spell/spawner
|
||||
name = "spawner template"
|
||||
desc = "If you see me, someone messed up."
|
||||
icon_state = "darkness"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = null
|
||||
var/obj/effect/spawner_type = null
|
||||
|
||||
/obj/effect/temporary_effect
|
||||
name = "self deleting effect"
|
||||
desc = "How are you examining what which cannot be seen?"
|
||||
invisibility = 101
|
||||
var/time_to_die = 10 SECONDS // Afer which, it will delete itself.
|
||||
var/new_light_range = 6
|
||||
var/new_light_power = 6
|
||||
var/new_light_color = "#FFFFFF"
|
||||
|
||||
/obj/effect/temporary_effect/New()
|
||||
..()
|
||||
set_light(new_light_range, new_light_power, l_color = new_light_color)
|
||||
if(time_to_die)
|
||||
spawn(time_to_die)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/spell/spawner/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(T)
|
||||
new spawner_type(T)
|
||||
user << "<span class='notice'>You shift \the [src] onto \the [T].</span>"
|
||||
qdel(src)
|
||||
41
code/game/gamemodes/technomancer/spells/summon/summon.dm
Normal file
41
code/game/gamemodes/technomancer/spells/summon/summon.dm
Normal file
@@ -0,0 +1,41 @@
|
||||
/mob/living/
|
||||
var/summoned = 0
|
||||
|
||||
/obj/item/weapon/spell/summon
|
||||
name = "summon template"
|
||||
desc = "Chitter chitter."
|
||||
cast_methods = CAST_RANGED | CAST_USE
|
||||
aspect = ASPECT_TELE
|
||||
var/mob/living/summoned_mob_type = null // The type to use when making new mobs when summoned.
|
||||
var/list/summon_options = list()
|
||||
var/energy_cost = 0
|
||||
var/instability_cost = 0
|
||||
|
||||
/obj/item/weapon/spell/summon/on_ranged_cast(atom/hit_atom, mob/living/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(summoned_mob_type && core.summoned_mobs.len < core.max_summons && pay_energy(energy_cost))
|
||||
var/obj/effect/E = new(T)
|
||||
E.icon = 'icons/obj/objects.dmi'
|
||||
E.icon_state = "anom"
|
||||
sleep(5 SECONDS)
|
||||
qdel(E)
|
||||
var/mob/living/L = new summoned_mob_type(T)
|
||||
core.summoned_mobs |= L
|
||||
L.summoned = 1
|
||||
var/image/summon_underlay = image('icons/obj/objects.dmi',"anom")
|
||||
summon_underlay.alpha = 127
|
||||
L.underlays |= summon_underlay
|
||||
on_summon(L)
|
||||
user << "<span class='notice'>You've successfully teleported \a [L] to you!</span>"
|
||||
visible_message("<span class='warning'>\A [L] appears from no-where!</span>")
|
||||
user.adjust_instability(instability_cost)
|
||||
|
||||
/obj/item/weapon/spell/summon/on_use_cast(mob/living/user)
|
||||
if(summon_options.len)
|
||||
var/choice = input(user, "Choose a creature to kidnap from somewhere!", "Summon") as null|anything in summon_options
|
||||
if(choice)
|
||||
summoned_mob_type = summon_options[choice]
|
||||
|
||||
// Called when a new mob is summoned, override for special behaviour.
|
||||
/obj/item/weapon/spell/summon/proc/on_summon(var/mob/living/summoned)
|
||||
return
|
||||
@@ -0,0 +1,45 @@
|
||||
/datum/technomancer/spell/summon_creature
|
||||
name = "Summon Creature"
|
||||
desc = "Teleports a specific creature from their current location in the universe to the targeted tile, \
|
||||
after a delay. The creature summoned can be chosen by using the ability in your hand. \
|
||||
Available creatures are; mice, crabs, parrots, bats, goats, cats, corgis, spiders, and space carp. \
|
||||
The creatures take a few moments to be teleported to the targeted tile. Note that the creatures summoned are \
|
||||
not inherently loyal to the technomancer, and that the creatures will be hurt slightly from being teleported to you."
|
||||
enhancement_desc = "Summoned entities will never harm their summoner."
|
||||
cost = 200
|
||||
obj_path = /obj/item/weapon/spell/summon/summon_creature
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_creature
|
||||
name = "summon creature"
|
||||
desc = "Chitter chitter."
|
||||
summoned_mob_type = null
|
||||
summon_options = list(
|
||||
"Mouse" = /mob/living/simple_animal/mouse,
|
||||
"Lizard" = /mob/living/simple_animal/lizard,
|
||||
"Chicken" = /mob/living/simple_animal/chicken,
|
||||
"Chick" = /mob/living/simple_animal/chick,
|
||||
"Crab" = /mob/living/simple_animal/crab,
|
||||
"Parrot" = /mob/living/simple_animal/parrot,
|
||||
"Goat" = /mob/living/simple_animal/hostile/retaliate/goat,
|
||||
"Cat" = /mob/living/simple_animal/cat,
|
||||
"Kitten" = /mob/living/simple_animal/cat/kitten,
|
||||
"Corgi" = /mob/living/simple_animal/corgi,
|
||||
"Corgi Pup" = /mob/living/simple_animal/corgi/puppy,
|
||||
"BAT" = /mob/living/simple_animal/hostile/scarybat,
|
||||
"SPIDER" = /mob/living/simple_animal/hostile/giant_spider,
|
||||
"SPIDER HUNTER" = /mob/living/simple_animal/hostile/giant_spider/hunter,
|
||||
"SPIDER NURSE" = /mob/living/simple_animal/hostile/giant_spider/nurse,
|
||||
"CARP" = /mob/living/simple_animal/hostile/carp,
|
||||
"BEAR" = /mob/living/simple_animal/hostile/bear
|
||||
)
|
||||
cooldown = 30
|
||||
instability_cost = 10
|
||||
energy_cost = 1000
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/summoned)
|
||||
if(check_for_scepter())
|
||||
// summoned.faction = "technomancer"
|
||||
if(istype(summoned, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/SA = summoned
|
||||
SA.friends.Add(owner)
|
||||
summoned.health = round(summoned.maxHealth * 0.7)
|
||||
110
code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
Normal file
110
code/game/gamemodes/technomancer/spells/summon/summon_ward.dm
Normal file
@@ -0,0 +1,110 @@
|
||||
/datum/technomancer/spell/summon_ward
|
||||
name = "Summon Ward"
|
||||
desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you and your allies when it sees entities \
|
||||
moving around it, or when it is attacked. They can see for up to five meters. Wards expire in six minutes."
|
||||
enhancement_desc = "Wards can detect invisibile entities, and are more specific in relaying information about what it sees."
|
||||
cost = 100
|
||||
obj_path = /obj/item/weapon/spell/summon/summon_ward
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_ward
|
||||
name = "summon ward"
|
||||
desc = "Finally, someone you can depend on to watch your back."
|
||||
cast_methods = CAST_RANGED
|
||||
summoned_mob_type = /mob/living/simple_animal/ward
|
||||
cooldown = 10
|
||||
instability_cost = 5
|
||||
energy_cost = 500
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_animal/ward/ward)
|
||||
ward.creator = owner
|
||||
if(check_for_scepter())
|
||||
ward.true_sight = 1
|
||||
ward.see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
|
||||
/mob/living/simple_animal/ward
|
||||
name = "ward"
|
||||
desc = "It's a little flying drone that seems to be watching you..."
|
||||
icon = 'icons/mob/critter.dmi'
|
||||
icon_state = "ward"
|
||||
resistance = 5
|
||||
wander = 0
|
||||
response_help = "pets the"
|
||||
response_disarm = "swats away"
|
||||
response_harm = "punches"
|
||||
var/true_sight = 0 // If true, detects more than what the Technomancer normally can't.
|
||||
var/mob/living/carbon/human/creator = null
|
||||
var/list/seen_mobs = list()
|
||||
|
||||
/mob/living/simple_animal/ward/New()
|
||||
..()
|
||||
spawn(6 MINUTES)
|
||||
expire()
|
||||
|
||||
/mob/living/simple_animal/ward/death()
|
||||
if(creator)
|
||||
creator << "<span class='danger'>Your ward inside [get_area(src)] was killed!</span>"
|
||||
|
||||
/mob/living/simple_animal/ward/proc/expire()
|
||||
if(creator && src)
|
||||
creator << "<span class='warning'>Your ward inside [get_area(src)] expired.</span>"
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/ward/Life()
|
||||
..()
|
||||
detect_mobs()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/ward/proc/detect_mobs()
|
||||
var/list/things_in_sight = view(5,src)
|
||||
var/list/newly_seen_mobs = list()
|
||||
for(var/mob/living/L in things_in_sight)
|
||||
if(L == creator) // I really wish is_ally() was usable here.
|
||||
continue
|
||||
|
||||
if(istype(L, /mob/living/simple_animal/ward))
|
||||
continue
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
if(creator in SA.friends)
|
||||
continue
|
||||
|
||||
if(!true_sight)
|
||||
var/atom/movable/lighting_overlay/light = locate(/atom/movable/lighting_overlay) in get_turf(L)
|
||||
var/light_amount = 1 // Unsimulated tiles are pretend-lit, so we need to be pretend too if that somehow happens.
|
||||
if(light)
|
||||
light_amount = (light.lum_r + light.lum_g + light.lum_b) / 3
|
||||
|
||||
if(light_amount <= 0.5)
|
||||
continue // Too dark to see.
|
||||
|
||||
if(L.alpha <= 127)
|
||||
continue // Too transparent, as a mercy to camo lings.
|
||||
|
||||
// Warn the Technomancer when it sees a new mob.
|
||||
if(!(L in seen_mobs))
|
||||
seen_mobs.Add(L)
|
||||
newly_seen_mobs.Add(L)
|
||||
if(creator)
|
||||
if(true_sight)
|
||||
creator << "<span class='notice'>Your ward at [get_area(src)] detected [english_list(newly_seen_mobs)].</span>"
|
||||
else
|
||||
creator << "<span class='notice'>Your ward at [get_area(src)] detected something.</span>"
|
||||
|
||||
// Now get rid of old mobs that left vision.
|
||||
for(var/mob/living/L in seen_mobs)
|
||||
if(!(L in things_in_sight))
|
||||
seen_mobs.Remove(L)
|
||||
|
||||
|
||||
/mob/living/simple_animal/ward/update_icon()
|
||||
if(seen_mobs.len)
|
||||
icon_state = "ward_spotted"
|
||||
set_light(3, 3, l_color = "FF0000")
|
||||
else
|
||||
icon_state = "ward"
|
||||
set_light(3, 3, l_color = "00FF00")
|
||||
if(true_sight)
|
||||
overlays.Cut()
|
||||
var/image/I = image('icons/mob/critter.dmi',"ward_truesight")
|
||||
overlays.Add(I)
|
||||
79
code/game/gamemodes/technomancer/spells/warp_strike.dm
Normal file
79
code/game/gamemodes/technomancer/spells/warp_strike.dm
Normal file
@@ -0,0 +1,79 @@
|
||||
/datum/technomancer/spell/warp_strike
|
||||
name = "Warp Strike"
|
||||
desc = "Teleports you next to your target, and attacks them with whatever is in your off-hand, spell or object."
|
||||
cost = 200
|
||||
obj_path = /obj/item/weapon/spell/warp_strike
|
||||
|
||||
/obj/item/weapon/spell/warp_strike
|
||||
name = "warp strike"
|
||||
desc = "The answer to the problem of bringing a knife to a gun fight."
|
||||
icon_state = "tech_warpstrike"
|
||||
cast_methods = CAST_RANGED
|
||||
aspect = ASPECT_TELE
|
||||
var/datum/effect/effect/system/spark_spread/sparks
|
||||
|
||||
/obj/item/weapon/spell/warp_strike/New()
|
||||
..()
|
||||
sparks = PoolOrNew(/datum/effect/effect/system/spark_spread)
|
||||
sparks.set_up(5, 0, src)
|
||||
sparks.attach(loc)
|
||||
|
||||
/obj/item/weapon/spell/warp_strike/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
var/turf/T = get_turf(hit_atom)
|
||||
if(T)
|
||||
//First, we handle who to teleport to.
|
||||
user.setClickCooldown(5)
|
||||
var/list/potential_targets = view(T, 2) //Everyone in a 5x5 range of the tile we clicked on.
|
||||
var/mob/living/chosen_target = null //The person who's about to get attacked.
|
||||
|
||||
//Find us someone to robust.
|
||||
for(var/mob/living/L in potential_targets)
|
||||
if(L == user || L.invisibility > user.see_invisible) //Don't target ourselves or people we can't see.
|
||||
continue
|
||||
if(!L.stat) //Don't want to target dead people or SSDs.
|
||||
chosen_target = L
|
||||
break
|
||||
|
||||
if(!chosen_target)
|
||||
return 0
|
||||
|
||||
//Now we handle picking a place for the user to teleport to.
|
||||
var/list/tele_target_candidates = view(get_turf(chosen_target), 1)
|
||||
var/list/valid_tele_targets = list()
|
||||
var/turf/tele_target = null
|
||||
for(var/turf/checked_turf in tele_target_candidates)
|
||||
if(!checked_turf.check_density())
|
||||
valid_tele_targets.Add(checked_turf)
|
||||
|
||||
tele_target = pick(valid_tele_targets)
|
||||
|
||||
//Pay for our teleport.
|
||||
if(!pay_energy(2000) || !tele_target)
|
||||
return 0
|
||||
|
||||
//Teleporting time.
|
||||
user.forceMove(tele_target)
|
||||
var/new_dir = get_dir(user, chosen_target)
|
||||
user.dir = new_dir
|
||||
sparks.start()
|
||||
owner.adjust_instability(12)
|
||||
|
||||
//Finally, we handle striking the victim with whatever's in the user's offhand.
|
||||
var/obj/item/I = user.get_inactive_hand()
|
||||
var/list/blacklisted_items = list(/obj/item/weapon/gun) //We don't want these items to be used, likely because it would break balance.
|
||||
if(I)
|
||||
|
||||
if(is_path_in_list(I.type, blacklisted_items))
|
||||
user << "<span class='danger'>You can't use \the [I] while warping!</span>"
|
||||
return
|
||||
|
||||
if(istype(I, /obj/item/weapon))
|
||||
var/obj/item/weapon/W = I
|
||||
W.attack(chosen_target, user)
|
||||
W.afterattack(chosen_target, user)
|
||||
else
|
||||
I.attack(chosen_target, user)
|
||||
I.afterattack(chosen_target, user)
|
||||
else
|
||||
chosen_target.attack_hand(user)
|
||||
|
||||
14
code/game/gamemodes/technomancer/technomancer.dm
Normal file
14
code/game/gamemodes/technomancer/technomancer.dm
Normal file
@@ -0,0 +1,14 @@
|
||||
/datum/game_mode/technomancer
|
||||
name = "Technomancer"
|
||||
round_description = "An entity possessing advanced, unknown technology is onboard, who is capable of accomplishing amazing feats."
|
||||
extended_round_description = "A powerful entity capable of manipulating space around them, has arrived the station. \
|
||||
They have a wide variety of powers and functions available to them that makes your own simple moral self tremble with \
|
||||
fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if \
|
||||
their powers can be used for good or if their arrival foreshadows the destruction of the entire colony, or worse."
|
||||
config_tag = "technomancer"
|
||||
votable = 0
|
||||
required_players = 8
|
||||
required_players_secret = 10
|
||||
required_enemies = 1
|
||||
end_on_antag_death = 0
|
||||
antag_tags = list(MODE_TECHNOMANCER)
|
||||
@@ -3,6 +3,7 @@
|
||||
round_description = "There is a SPACE WIZARD on the station. You can't let the magician achieve their objectives!"
|
||||
extended_round_description = "A powerful entity capable of manipulating the elements around him, most commonly referred to as a 'wizard', has infiltrated the station. They have a wide variety of powers and spells available to them that makes your own simple moral self tremble with fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if their powers can be used for good or if their arrival foreshadows the destruction of the entire station."
|
||||
config_tag = "wizard"
|
||||
votable = 0
|
||||
required_players = 1
|
||||
required_players_secret = 6
|
||||
required_enemies = 1
|
||||
|
||||
@@ -12,4 +12,5 @@
|
||||
#define ACCESS_TYPE_CENTCOM 1
|
||||
#define ACCESS_TYPE_STATION 2
|
||||
#define ACCESS_TYPE_SYNDICATE 4
|
||||
#define ACCESS_TYPE_ALL (ACCESS_TYPE_CENTCOM|ACCESS_TYPE_STATION|ACCESS_TYPE_SYNDICATE)
|
||||
#define ACCESS_TYPE_PRIVATE 8
|
||||
#define ACCESS_TYPE_ALL (ACCESS_TYPE_CENTCOM|ACCESS_TYPE_STATION|ACCESS_TYPE_SYNDICATE|ACCESS_TYPE_PRIVATE)
|
||||
|
||||
@@ -125,6 +125,13 @@
|
||||
|
||||
return priv_syndicate_access
|
||||
|
||||
/var/list/priv_private_access
|
||||
/proc/get_all_private_access()
|
||||
if(!priv_private_access)
|
||||
priv_private_access = get_access_ids(ACCESS_TYPE_PRIVATE)
|
||||
|
||||
return priv_syndicate_access
|
||||
|
||||
/var/list/priv_region_access
|
||||
/proc/get_region_accesses(var/code)
|
||||
if(code == ACCESS_REGION_ALL)
|
||||
|
||||
@@ -457,3 +457,8 @@
|
||||
/datum/access/crate_cash
|
||||
id = access_crate_cash
|
||||
access_type = ACCESS_TYPE_NONE
|
||||
|
||||
/var/const/access_trader = 160//General Beruang Trader Access
|
||||
/datum/access/trader
|
||||
id = access_trader
|
||||
access_type = ACCESS_TYPE_PRIVATE
|
||||
|
||||
@@ -342,7 +342,7 @@ var/global/datum/controller/occupations/job_master
|
||||
else
|
||||
permitted = 1
|
||||
|
||||
if(G.whitelisted && !is_alien_whitelisted(H, G.whitelisted))
|
||||
if(G.whitelisted && !is_alien_whitelisted(H, all_species[G.whitelisted]))
|
||||
permitted = 0
|
||||
|
||||
if(!permitted)
|
||||
|
||||
@@ -30,22 +30,54 @@ var/list/whitelist = list()
|
||||
else
|
||||
alien_whitelist = splittext(text, "\n")
|
||||
|
||||
//todo: admin aliens
|
||||
/proc/is_alien_whitelisted(mob/M, var/species)
|
||||
if(!config.usealienwhitelist)
|
||||
/proc/is_alien_whitelisted(mob/M, var/datum/species/species)
|
||||
//They are admin or the whitelist isn't in use
|
||||
if(whitelist_overrides(M))
|
||||
return 1
|
||||
if(species == "human" || species == "Human")
|
||||
return 1
|
||||
if(check_rights(R_ADMIN, 0))
|
||||
return 1
|
||||
if(!alien_whitelist)
|
||||
|
||||
//You did something wrong
|
||||
if(!M || !species)
|
||||
return 0
|
||||
if(M && species)
|
||||
|
||||
//The species isn't even whitelisted
|
||||
if(!(species.spawn_flags & SPECIES_IS_WHITELISTED))
|
||||
return 1
|
||||
|
||||
//If we have a loaded file, search it
|
||||
if(alien_whitelist)
|
||||
for (var/s in alien_whitelist)
|
||||
if(findtext(s,"[M.ckey] - [species]"))
|
||||
if(findtext(s,"[M.ckey] - [species.name]"))
|
||||
return 1
|
||||
if(findtext(s,"[M.ckey] - All"))
|
||||
return 1
|
||||
|
||||
/proc/is_lang_whitelisted(mob/M, var/datum/language/language)
|
||||
//They are admin or the whitelist isn't in use
|
||||
if(whitelist_overrides(M))
|
||||
return 1
|
||||
|
||||
//You did something wrong
|
||||
if(!M || !language)
|
||||
return 0
|
||||
|
||||
//The language isn't even whitelisted
|
||||
if(!(language.flags & WHITELISTED))
|
||||
return 1
|
||||
|
||||
//If we have a loaded file, search it
|
||||
if(alien_whitelist)
|
||||
for (var/s in alien_whitelist)
|
||||
if(findtext(s,"[M.ckey] - [language.name]"))
|
||||
return 1
|
||||
if(findtext(s,"[M.ckey] - All"))
|
||||
return 1
|
||||
|
||||
/proc/whitelist_overrides(mob/M)
|
||||
if(!config.usealienwhitelist)
|
||||
return 1
|
||||
if(check_rights(R_ADMIN, 0))
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
#undef WHITELISTFILE
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
path = /obj/item/weapon/reagent_containers/glass/bucket
|
||||
category = "General"
|
||||
|
||||
/datum/autolathe/recipe/cooler_bottle
|
||||
name = "water-cooler bottle"
|
||||
path = /obj/item/weapon/reagent_containers/glass/cooler_bottle
|
||||
category = "General"
|
||||
|
||||
/datum/autolathe/recipe/drinkingglass
|
||||
name = "drinking glass"
|
||||
path = /obj/item/weapon/reagent_containers/food/drinks/glass2/square
|
||||
|
||||
@@ -17,8 +17,7 @@ var/global/list/station_networks = list(
|
||||
NETWORK_RESEARCH_OUTPOST,
|
||||
NETWORK_ROBOTS,
|
||||
NETWORK_PRISON,
|
||||
NETWORK_SECURITY,
|
||||
NETWORK_COMMUNICATORS
|
||||
NETWORK_SECURITY
|
||||
)
|
||||
var/global/list/engineering_networks = list(
|
||||
NETWORK_ENGINE,
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
req_access = list(access_robotics)
|
||||
circuit = /obj/item/weapon/circuitboard/robotics
|
||||
|
||||
var/safety = 1
|
||||
|
||||
/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob)
|
||||
ui_interact(user)
|
||||
|
||||
@@ -19,8 +17,6 @@
|
||||
/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
var/data[0]
|
||||
data["robots"] = get_cyborgs(user)
|
||||
data["safety"] = safety
|
||||
// Also applies for cyborgs. Hides the manual self-destruct button.
|
||||
data["is_ai"] = issilicon(user)
|
||||
|
||||
|
||||
@@ -39,39 +35,8 @@
|
||||
user << "Access Denied"
|
||||
return
|
||||
|
||||
// Destroys the cyborg
|
||||
if(href_list["detonate"])
|
||||
var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"])
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
if(isAI(user) && (target.connected_ai != user))
|
||||
user << "Access Denied. This robot is not linked to you."
|
||||
return
|
||||
// Cyborgs may blow up themselves via the console
|
||||
if(isrobot(user) && user != target)
|
||||
user << "Access Denied."
|
||||
return
|
||||
var/choice = input("Really detonate [target.name]?") in list ("Yes", "No")
|
||||
if(choice != "Yes")
|
||||
return
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
|
||||
// Antagonistic cyborgs? Left here for downstream
|
||||
if(target.mind && (target.mind.special_role || target.emagged))
|
||||
target << "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered."
|
||||
target.ResetSecurityCodes()
|
||||
else
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] detonated [target.name]!</span>")
|
||||
log_game("[key_name(usr)] detonated [target.name]!")
|
||||
target << "<span class='danger'>Self-destruct command received.</span>"
|
||||
spawn(10)
|
||||
target.self_destruct()
|
||||
|
||||
|
||||
|
||||
// Locks or unlocks the cyborg
|
||||
else if (href_list["lockdown"])
|
||||
if (href_list["lockdown"])
|
||||
var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"])
|
||||
if(!target || !istype(target))
|
||||
return
|
||||
@@ -137,37 +102,6 @@
|
||||
target.emagged = 1
|
||||
target << "<span class='notice'>Failsafe protocols overriden. New tools available.</span>"
|
||||
|
||||
// Arms the emergency self-destruct system
|
||||
else if(href_list["arm"])
|
||||
if(istype(user, /mob/living/silicon))
|
||||
user << "Access Denied"
|
||||
return
|
||||
|
||||
safety = !safety
|
||||
user << "You [safety ? "disarm" : "arm"] the emergency self destruct"
|
||||
|
||||
// Destroys all accessible cyborgs if safety is disabled
|
||||
else if(href_list["nuke"])
|
||||
if(istype(user, /mob/living/silicon))
|
||||
user << "Access Denied"
|
||||
return
|
||||
if(safety)
|
||||
user << "Self-destruct aborted - safety active"
|
||||
return
|
||||
|
||||
message_admins("<span class='notice'>[key_name_admin(usr)] detonated all cyborgs!</span>")
|
||||
log_game("[key_name(usr)] detonated all cyborgs!")
|
||||
|
||||
for(var/mob/living/silicon/robot/R in mob_list)
|
||||
if(istype(R, /mob/living/silicon/robot/drone))
|
||||
continue
|
||||
// Ignore antagonistic cyborgs
|
||||
if(R.scrambledcodes)
|
||||
continue
|
||||
R << "<span class='danger'>Self-destruct command received.</span>"
|
||||
spawn(10)
|
||||
R.self_destruct()
|
||||
|
||||
|
||||
// Proc: get_cyborgs()
|
||||
// Parameters: 1 (operator - mob which is operating the console.)
|
||||
|
||||
@@ -212,6 +212,8 @@
|
||||
if(!hacked)
|
||||
user << "<span class='notice'>Special supplies unlocked.</span>"
|
||||
hacked = 1
|
||||
can_order_contraband = 1
|
||||
req_access = list()
|
||||
return 1
|
||||
|
||||
/obj/machinery/computer/supplycomp/Topic(href, href_list)
|
||||
|
||||
@@ -737,6 +737,9 @@ About the new airlock wires panel:
|
||||
return
|
||||
|
||||
src.add_fingerprint(user)
|
||||
if(istype(C, /mob/living))
|
||||
..()
|
||||
return
|
||||
if(!repairing && (istype(C, /obj/item/weapon/weldingtool) && !( src.operating > 0 ) && src.density))
|
||||
var/obj/item/weapon/weldingtool/W = C
|
||||
if(W.remove_fuel(0,user))
|
||||
@@ -810,7 +813,9 @@ About the new airlock wires panel:
|
||||
spawn(0) close(1)
|
||||
|
||||
// Check if we're using a crowbar or armblade, and if the airlock's unpowered for whatever reason (off, broken, etc).
|
||||
else if( (istype(C, /obj/item/weapon/material/twohanded/fireaxe) || istype(C, /obj/item/weapon/melee/arm_blade) ) && !arePowerSystemsOn())
|
||||
else if(istype(C, /obj/item/weapon))
|
||||
var/obj/item/weapon/W = C
|
||||
if((W.pry == 1) && !arePowerSystemsOn())
|
||||
if(locked)
|
||||
user << "<span class='notice'>The airlock's bolts prevent it from being forced.</span>"
|
||||
else if( !welded && !operating )
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user