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:
SinTwo
2016-07-13 17:07:52 -04:00
266 changed files with 10381 additions and 3553 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View 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

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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.")

View 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>"

View 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])"

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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
*/

View 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

View 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)

View 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"

View 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) )

View File

@@ -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)

View 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

View 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

View 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>")

View 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)

View 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

View 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

View 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)

View 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

View 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)

View 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)

View 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."

View 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>"

View 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

View 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."

View 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)

View 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)

View 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)

View File

@@ -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)

View 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)

View 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)

View 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)

View 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>"

View 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)

View 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

View 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

View 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

View File

@@ -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

View 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>"
..()

View 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>"
..()

View 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)

View 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)

View File

@@ -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)

View 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)

View File

@@ -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>"
..()

View 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)

View 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

View 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)

View 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

View 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)

View 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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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)

View 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

View 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)

View 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)

View 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

View 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

View File

@@ -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)
..()

View 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)

View 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)

View 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

View File

@@ -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)

View 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)

View 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)

View 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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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.)

View File

@@ -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)

View File

@@ -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