Merge branch 'master' of https://github.com/ParadiseSS13/Paradise into BookClub

This commit is contained in:
Crazylemon64
2017-01-06 05:01:43 -08:00
158 changed files with 4150 additions and 1040 deletions
+4
View File
@@ -66,6 +66,10 @@ Pipelines + Other Objects -> Pipe network
/obj/machinery/atmospherics/update_icon()
return null
/obj/machinery/atmospherics/proc/update_pipe_image()
pipe_image = image(src, loc, layer = 20, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view)
pipe_image.plane = HUD_PLANE
/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0)
if(!istype(icon_manager))
if(!safety) //to prevent infinite loops
@@ -100,6 +100,8 @@
overlays += icon_manager.get_atmos_icon("device", , , vent_icon)
update_pipe_image()
/obj/machinery/atmospherics/unary/vent_pump/update_underlays()
if(..())
underlays.Cut()
@@ -107,6 +107,7 @@
scrubber_icon = "scrubberweld"
overlays += icon_manager.get_atmos_icon("device", , , scrubber_icon)
update_pipe_image()
/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays()
if(..())
+5 -4
View File
@@ -255,10 +255,11 @@
0, 0, 1, 0,\
0, 0, 0, 1)
#define MATRIX_GREYSCALE list(0.3, 0.3, 0.3, 0,\
0.3, 0.3, 0.3, 0,\
0.3, 0.3, 0.3, 0,\
0, 0, 0, 1)
#define MATRIX_GREYSCALE list(0.33, 0.33, 0.33, 0,\
0.33, 0.33, 0.33, 0,\
0.33, 0.33, 0.33, 0,\
0.00, 0.00, 0.00, 1,\
0.00, 0.00, 0.00, 0)
//Gun trigger guards
#define TRIGGER_GUARD_ALLOW_ALL -1
#define TRIGGER_GUARD_NONE 0
+7
View File
@@ -27,3 +27,10 @@
#define DONATOR_PUBLIC 32768
#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC)
// Playtime tracking system, see jobs_exp.dm
#define EXP_TYPE_LIVING "Living"
#define EXP_TYPE_CREW "Crew"
#define EXP_TYPE_SPECIAL "Special"
#define EXP_TYPE_GHOST "Ghost"
#define EXP_TYPE_EXEMPT "Exempt"
+1
View File
@@ -115,6 +115,7 @@
result += e
else
result = first - second
return result
/*
+1 -1
View File
@@ -33,4 +33,4 @@ var/global/list/shuttle_caller_list = list() //list of all communication cons
var/global/list/tracked_implants = list() //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented...
var/global/list/pinpointer_list = list() //list of all pinpointers. Used to change stuff they are pointing to all at once.
var/global/list/abductor_equipment = list() //list of all abductor equipment
var/global/list/global_intercoms = list() //list of all intercomms, across all z-levels
var/global/list/global_intercoms = list() //list of all intercomms, across all z-levels
@@ -0,0 +1,157 @@
var/global/datum/controller/process/mob_hunt/mob_hunt_server
/datum/controller/process/mob_hunt
var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this
var/list/normal_spawns = list()
var/max_trap_spawns = 15 //change this to adjust the number of trap spawns that can exist at one time. traps spawned beyond this point clear the oldest traps
var/list/trap_spawns = list()
var/list/connected_clients = list()
var/server_status = 1 //1 is online, 0 is offline
var/reset_cooldown = 0 //number of controller cycles before the manual_reboot proc can be used again (ignored if server is offline so you can always boot back up)
var/obj/machinery/computer/mob_battle_terminal/red_terminal
var/obj/machinery/computer/mob_battle_terminal/blue_terminal
var/battle_turn = null
/datum/controller/process/mob_hunt/setup()
name = "Nano-Mob Hunter GO Server"
start_delay = 20
mob_hunt_server = src
/datum/controller/process/mob_hunt/doWork()
if(reset_cooldown) //if reset_cooldown is set (we are on cooldown, duh), reduce the remaining cooldown every cycle
reset_cooldown--
if(!server_status)
return
client_mob_update()
if(normal_spawns.len < max_normal_spawns)
spawn_mob()
//leaving this here in case admins want to use it for a random mini-event or something
/datum/controller/process/mob_hunt/proc/server_crash(recover_time = 3000)
server_status = 0
for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients)
client.disconnect("Server Crash")
for(var/obj/effect/nanomob/N in trap_spawns)
N.despawn()
for(var/obj/effect/nanomob/N in normal_spawns)
N.despawn()
//just in case
normal_spawns.Cut()
trap_spawns.Cut()
connected_clients.Cut()
if(!isnum(recover_time))
recover_time = 3000
if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still
//set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too)
addtimer(src, "auto_recover", recover_time, TRUE)
/datum/controller/process/mob_hunt/proc/client_mob_update()
var/list/ex_players = list()
for(var/datum/data/pda/app/mob_hunter_game/client in connected_clients)
var/mob/living/carbon/human/H = client.get_player()
if(connected_clients[client])
if(!H || H != connected_clients[client])
ex_players |= connected_clients[client]
connected_clients[client] = H
if(ex_players.len) //to make sure we don't do this if we didn't lose any player since the last update
for(var/obj/effect/nanomob/N in (normal_spawns + trap_spawns))
N.conceal(ex_players)
/datum/controller/process/mob_hunt/proc/auto_recover()
if(server_status != 0)
return
server_status = 1
while(normal_spawns.len < max_normal_spawns) //repopulate the server's spawns completely if we auto-recover from crash
spawn_mob()
/datum/controller/process/mob_hunt/proc/manual_reboot()
if(server_status && reset_cooldown)
return 0
for(var/obj/effect/nanomob/N in trap_spawns)
N.despawn()
for(var/obj/effect/nanomob/N in normal_spawns)
N.despawn()
server_status = 1
reset_cooldown = 25 //25 controller cycle cooldown for manual restarts
return 1
/datum/controller/process/mob_hunt/proc/spawn_mob()
var/list/nanomob_types = subtypesof(/datum/mob_hunt)
var/datum/mob_hunt/mob_info = pick(nanomob_types)
new mob_info()
/datum/controller/process/mob_hunt/proc/register_spawn(datum/mob_hunt/mob_info)
if(!mob_info)
return 0
var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info)
normal_spawns += new_mob
new_mob.reveal()
return 1
/datum/controller/process/mob_hunt/proc/register_trap(datum/mob_hunt/mob_info)
if(!mob_info)
return 0
if(!mob_info.is_trap)
return register_spawn(mob_info)
var/obj/effect/nanomob/new_mob = new /obj/effect/nanomob(mob_info.spawn_point, mob_info)
trap_spawns += new_mob
new_mob.reveal()
if(trap_spawns.len > max_trap_spawns)
var/obj/effect/nanomob/old_trap = trap_spawns[1]
old_trap.despawn()
return 1
/datum/controller/process/mob_hunt/proc/start_check()
if(battle_turn) //somehow we got called mid-battle, so lets just stop now
return
if(red_terminal && red_terminal.ready && blue_terminal && blue_terminal.ready)
battle_turn = pick("Red", "Blue")
red_terminal.audible_message("Battle starting!", null, 5)
blue_terminal.audible_message("Battle starting!", null, 5)
if(battle_turn == "Red")
red_terminal.audible_message("Red Player's Turn!", null, 5)
else if(battle_turn == "Blue")
blue_terminal.audible_message("Blue Player's Turn!", null, 5)
/datum/controller/process/mob_hunt/proc/launch_attack(team, raw_damage, datum/mob_type/attack_type)
if(!team || !raw_damage)
return
var/obj/machinery/computer/mob_battle_terminal/target = null
if(team == "Red")
target = blue_terminal
else if(team == "Blue")
target = red_terminal
else
return
target.receive_attack(raw_damage, attack_type)
/datum/controller/process/mob_hunt/proc/end_battle(loser, surrender = 0)
var/obj/machinery/computer/mob_battle_terminal/winner_terminal = null
var/obj/machinery/computer/mob_battle_terminal/loser_terminal = null
if(loser == "Red")
loser_terminal = red_terminal
winner_terminal = blue_terminal
else if (loser == "Blue")
loser_terminal = blue_terminal
winner_terminal = red_terminal
battle_turn = null
winner_terminal.ready = 0
loser_terminal.ready = 0
if(surrender) //surrender doesn't give exp, to avoid people just farming exp without actually doing a battle
winner_terminal.audible_message("Your rival surrendered!", null, 2)
else
var/progress_message = winner_terminal.mob_info.gain_exp()
winner_terminal.audible_message("[winner_terminal.team] Player wins!", null, 5)
winner_terminal.audible_message(progress_message, null, 2)
/datum/controller/process/mob_hunt/proc/end_turn()
red_terminal.updateUsrDialog()
blue_terminal.updateUsrDialog()
if(!battle_turn)
return
if(battle_turn == "Red")
battle_turn = "Blue"
blue_terminal.audible_message("Blue's turn.", null, 5)
else if(battle_turn == "Blue")
battle_turn = "Red"
blue_terminal.audible_message("Red's turn.", null, 5)
+22
View File
@@ -89,6 +89,8 @@
var/overflow_server_url
var/forbid_singulo_possession = 0
var/check_randomizer = 0
//game_options.txt configs
var/health_threshold_softcrit = 0
@@ -127,6 +129,10 @@
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database
var/use_exp_tracking = 0
var/use_exp_restrictions = 0
var/use_exp_restrictions_admin_bypass = 0
var/simultaneous_pm_warning_timeout = 100
var/assistant_maint = 0 //Do assistants get maint access?
@@ -140,6 +146,7 @@
var/main_irc = ""
var/admin_irc = ""
var/admin_notify_irc = ""
var/cidrandomizer_irc = ""
var/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix
var/default_laws = 0 //Controls what laws the AI spawns with.
@@ -240,6 +247,15 @@
if("use_age_restriction_for_antags")
config.use_age_restriction_for_antags = 1
if("use_exp_tracking")
config.use_exp_tracking = 1
if("use_exp_restrictions")
config.use_exp_restrictions = 1
if("use_exp_restrictions_admin_bypass")
config.use_exp_restrictions_admin_bypass = 1
if("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
@@ -405,6 +421,9 @@
if("forbid_singulo_possession")
forbid_singulo_possession = 1
if("check_randomizer")
check_randomizer = 1
if("popup_admin_pm")
config.popup_admin_pm = 1
@@ -472,6 +491,9 @@
if("admin_notify_irc")
config.admin_notify_irc = value
if("cidrandomizer_irc")
config.cidrandomizer_irc = value
if("python_path")
if(value)
config.python_path = value
+4 -1
View File
@@ -18,7 +18,7 @@
return
/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Lighting","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote"))
/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Lighting","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote","Mob Hunt Server"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -76,6 +76,9 @@
if("Vote")
debug_variables(vote)
feedback_add_details("admin_verb","DVote")
if("Mob Hunt Server")
debug_variables(mob_hunt_server)
feedback_add_details("admin_verb","DMobHuntServer")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return
+2 -3
View File
@@ -17,7 +17,8 @@
access_heads, access_construction, access_sec_doors,
access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -51,7 +52,6 @@
minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_mineral_storeroom)
alt_titles = list("Maintenance Technician","Engine Technician","Electrician")
minimal_player_age = 7
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_eng(H), slot_l_ear)
@@ -83,7 +83,6 @@
minimal_access = list(access_eva, access_atmospherics, access_maint_tunnels, access_external_airlocks, access_emergency_storage, access_construction, access_mineral_storeroom)
alt_titles = list("Atmospheric Technician")
minimal_player_age = 7
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_eng(H), slot_l_ear)
+3
View File
@@ -41,6 +41,9 @@
//If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.)
var/minimal_player_age = 0
var/exp_requirements = 0
var/exp_type = ""
var/admin_only = 0
var/spawn_ert = 0
+2 -1
View File
@@ -15,6 +15,8 @@
access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce,
access_keycard_auth, access_sec_doors, access_psychiatrist, access_maint_tunnels, access_paramedic, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -109,7 +111,6 @@
alt_titles = list("Pharmacist","Pharmacologist")
minimal_player_age = 7
equip(var/mob/living/carbon/human/H)
if(!H) return 0
switch(H.backbag)
+2
View File
@@ -17,6 +17,8 @@
access_research, access_robotics, access_xenobiology, access_ai_upload,
access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_maint_tunnels, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
exp_type = EXP_TYPE_CREW
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
+8
View File
@@ -17,6 +17,8 @@
access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting,
access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_pilot, access_weapons)
minimal_player_age = 21
exp_requirements = 600
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -56,6 +58,8 @@
access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_morgue, access_weapons)
minimal_access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_weapons)
minimal_player_age = 21
exp_requirements = 300
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -98,6 +102,8 @@
minimal_access = list(access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons)
alt_titles = list("Forensic Technician")
minimal_player_age = 14
exp_requirements = 300
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_sec/alt(H), slot_l_ear)
@@ -148,6 +154,8 @@
access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons)
minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons)
minimal_player_age = 14
exp_requirements = 300
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_sec/alt(H), slot_l_ear)
+5 -1
View File
@@ -8,6 +8,8 @@
supervisors = "your laws"
req_admin_notify = 1
minimal_player_age = 30
exp_requirements = 600
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -15,7 +17,7 @@
/datum/job/ai/is_position_available()
return (empty_playable_ai_cores.len != 0)
/datum/job/cyborg
title = "Cyborg"
@@ -26,6 +28,8 @@
supervisors = "your laws and the AI" //Nodrak
selection_color = "#ddffdd"
minimal_player_age = 21
exp_requirements = 300
exp_type = EXP_TYPE_CREW
alt_titles = list("Android", "Robot")
equip(var/mob/living/carbon/human/H)
+4
View File
@@ -12,6 +12,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
access = list() //See get_access()
minimal_access = list() //See get_access()
minimal_player_age = 30
exp_requirements = 600
exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/heads/captain/alt(H), slot_l_ear)
@@ -56,6 +58,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
idtype = /obj/item/weapon/card/id/silver
req_admin_notify = 1
minimal_player_age = 21
exp_requirements = 600
exp_type = EXP_TYPE_CREW
access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers,
access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads,
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
+3
View File
@@ -123,6 +123,9 @@ var/global/datum/controller/occupations/job_master
if(job.admin_only) // No admin positions either.
continue
if(job.available_in_playtime(player.client))
continue
if(jobban_isbanned(player, job.title))
Debug("GRJ isbanned failed, Player: [player], Job: [job.title]")
continue
+190
View File
@@ -0,0 +1,190 @@
// Admin Verbs
/client/proc/cmd_admin_check_player_exp() //Allows admins to determine who the newer players are.
set category = "Admin"
set name = "Check Player Playtime"
if(!check_rights(R_ADMIN))
return
var/msg = "<html><head><title>Playtime Report</title></head><body>Playtime:<BR><UL>"
for(var/client/C in clients)
msg += "<LI> - [key_name_admin(C)]: <A href='?_src_=holder;getplaytimewindow=[C.mob.UID()]'>" + C.get_exp_living() + "</a></LI>"
msg += "</UL></BODY></HTML>"
src << browse(msg, "window=Player_playtime_check")
/datum/admins/proc/cmd_show_exp_panel(var/client/C)
if(!C)
to_chat(usr, "ERROR: Client not found.")
return
if(!check_rights(R_ADMIN))
return
var/body = "<html><head><title>Playtime for [C.key]</title></head><BODY><BR>Playtime:"
body += C.get_exp_report()
body += "</BODY></HTML>"
usr << browse(body, "window=playerplaytime[C.ckey];size=550x615")
// Procs
/datum/job/proc/available_in_playtime(client/C)
if(!C)
return 0
if(!exp_requirements || !exp_type)
return 0
if(!config.use_exp_restrictions)
return 0
if(config.use_exp_restrictions_admin_bypass && check_rights(R_ADMIN, 0, C.mob))
return 0
var/list/play_records = params2list(C.prefs.exp)
var/isexempt = text2num(play_records[EXP_TYPE_EXEMPT])
if(isexempt)
return 0
var/my_exp = text2num(play_records[get_exp_req_type()])
var/job_requirement = text2num(get_exp_req_amount())
if(my_exp >= job_requirement)
return 0
else
return (job_requirement - my_exp)
/datum/job/proc/get_exp_req_amount()
return exp_requirements
/datum/job/proc/get_exp_req_type()
return exp_type
/mob/proc/get_exp_report()
if(client)
return client.get_exp_report()
else
return "[src] has no client."
/client/proc/get_exp_report()
if(!config.use_exp_tracking)
return "Tracking is disabled in the server configuration file."
var/list/play_records = params2list(prefs.exp)
if(!play_records.len)
return "[key] has no records."
var/return_text = "<UL>"
var/list/exp_data = list()
for(var/category in exp_jobsmap)
if(text2num(play_records[category]))
exp_data[category] = text2num(play_records[category])
else
exp_data[category] = 0
for(var/dep in exp_data)
if(exp_data[dep] > 0)
if(dep == EXP_TYPE_EXEMPT)
return_text += "<LI>Exempt (all jobs auto-unlocked)</LI>"
else if(exp_data[EXP_TYPE_LIVING] > 0)
var/my_pc = num2text(round(exp_data[dep]/exp_data[EXP_TYPE_LIVING]*100))
return_text += "<LI>[dep] [get_exp_format(exp_data[dep])] ([my_pc]%)</LI>"
else
return_text += "<LI>[dep] [get_exp_format(exp_data[dep])] </LI>"
if(config.use_exp_restrictions_admin_bypass && check_rights(R_ADMIN, 0, mob))
return_text += "<LI>Admin (all jobs auto-unlocked)</LI>"
return_text += "</UL>"
var/list/jobs_locked = list()
var/list/jobs_unlocked = list()
for(var/datum/job/job in job_master.occupations)
if(job.exp_requirements && job.exp_type)
if(!job.available_in_playtime(mob.client))
jobs_unlocked += job.title
else
var/xp_req = job.get_exp_req_amount()
jobs_locked += "[job.title] [get_exp_format(text2num(play_records[job.get_exp_req_type()]))] / [get_exp_format(xp_req)] as [job.get_exp_req_type()])"
if(jobs_unlocked.len)
return_text += "<BR><BR>Jobs Unlocked:<UL><LI>"
return_text += jobs_unlocked.Join("</LI><LI>")
return_text += "</LI></UL>"
if(jobs_locked.len)
return_text += "<BR><BR>Jobs Not Unlocked:<UL><LI>"
return_text += jobs_locked.Join("</LI><LI>")
return_text += "</LI></UL>"
return return_text
/client/proc/get_exp_living()
var/list/play_records = params2list(prefs.exp)
var/exp_living = text2num(play_records[EXP_TYPE_LIVING])
return get_exp_format(exp_living)
/proc/get_exp_format(var/expnum)
if(expnum > 60)
return num2text(round(expnum / 60)) + "h"
else if(expnum > 0)
return num2text(expnum) + "m"
else
return "0h"
/proc/update_exp(var/mins, var/ann = 0)
if(!establish_db_connection())
return -1
spawn(0)
for(var/client/L in clients)
if(L.inactivity >= (10 MINUTES))
continue
spawn(0)
L.update_exp_client(mins, ann)
sleep(10)
/client/proc/update_exp_client(var/minutes, var/announce_changes = 0)
if(!src ||!ckey)
return
var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'")
if(!exp_read.Execute())
var/err = exp_read.ErrorMsg()
log_game("SQL ERROR during exp_update_client read. Error : \[[err]\]\n")
message_admins("SQL ERROR during exp_update_client read. Error : \[[err]\]\n")
return
var/list/read_records = list()
var/hasread = 0
while(exp_read.NextRow())
read_records = params2list(exp_read.item[1])
hasread = 1
if(!hasread)
return
var/list/play_records = list()
for(var/rtype in exp_jobsmap)
if(text2num(read_records[rtype]))
play_records[rtype] = text2num(read_records[rtype])
else
play_records[rtype] = 0
if(mob.stat == CONSCIOUS && mob.mind.assigned_role)
play_records[EXP_TYPE_LIVING] += minutes
if(announce_changes)
to_chat(mob,"<span class='notice'>You got: [minutes] Living EXP!")
for(var/category in exp_jobsmap)
if(exp_jobsmap[category]["titles"])
if(mob.mind.assigned_role in exp_jobsmap[category]["titles"])
play_records[category] += minutes
if(announce_changes)
to_chat(mob,"<span class='notice'>You got: [minutes] [category] EXP!")
if(mob.mind.special_role)
play_records[EXP_TYPE_SPECIAL] += minutes
if(announce_changes)
to_chat(mob,"<span class='notice'>You got: [minutes] Special EXP!")
else if(isobserver(mob))
play_records[EXP_TYPE_GHOST] += minutes
if(announce_changes)
to_chat(mob,"<span class='notice'>You got: [minutes] Ghost EXP!")
else
return
var/new_exp = list2params(play_records)
prefs.exp = new_exp
new_exp = sanitizeSQL(new_exp)
var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]' WHERE ckey='[ckey]'")
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
log_game("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
message_admins("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
return
/hook/roundstart/proc/exptimer()
if(!config.sql_enabled || !config.use_exp_tracking)
return 1
spawn(0)
while(TRUE)
sleep(5 MINUTES)
update_exp(5,0)
return 1
+7
View File
@@ -183,3 +183,10 @@ var/list/whitelisted_positions = list(
return titles
var/global/list/exp_jobsmap = list(
EXP_TYPE_LIVING = list(), // all living mobs
EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions
EXP_TYPE_SPECIAL = list(), // antags, ERT, etc
EXP_TYPE_GHOST = list(), // dead people, observers
EXP_TYPE_EXEMPT = list() // special grandfather setting
)
+2 -2
View File
@@ -109,7 +109,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["on"] = on ? 1 : 0
data["gasPressure"] = round(air_contents.return_pressure())
@@ -267,7 +267,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["on"] = on ? 1 : 0
data["gasPressure"] = round(air_contents.return_pressure())
+1 -1
View File
@@ -696,7 +696,7 @@
data["danger"] = danger
return data
/obj/machinery/alarm/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/href_list = state.href_list()
+5 -4
View File
@@ -131,7 +131,7 @@ var/datum/canister_icons/canister_icon_container = new()
var/list/LL = L[i]
LL = LL.Copy() //make sure we don't edit the datum list
LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI
possibledecals[i] = LL
possibledecals.Add(LL)
/obj/machinery/portable_atmospherics/canister/proc/check_change()
var/old_flag = update_flag
@@ -181,6 +181,7 @@ update_flag
if(src.destroyed)
src.overlays = 0
src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever.
return
if(icon_state != src.canister_color["prim"])
icon_state = src.canister_color["prim"]
@@ -420,9 +421,9 @@ update_flag
data["hasHoldingTank"] = holding ? 1 : 0
if(holding)
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
return data
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
return data
/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)
if(..())
+1 -1
View File
@@ -117,7 +117,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, datum/topic_state/state = physical_state)
/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state)
var/data[0]
data["portConnected"] = connected_port ? 1 : 0
data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0)
+1 -1
View File
@@ -128,7 +128,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, datum/topic_state/state = physical_state)
/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state)
var/data[0]
data["portConnected"] = connected_port ? 1 : 0
data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0)
+1 -1
View File
@@ -92,7 +92,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/operating/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/mob/living/carbon/human/occupant = src.table.victim
data["hasOccupant"] = occupant ? 1 : 0
+28 -28
View File
@@ -1,14 +1,14 @@
/obj/machinery/computer/security
name = "security camera console"
desc = "Used to access the various cameras networks on the station."
icon_keyboard = "security_key"
icon_screen = "cameras"
light_color = LIGHT_COLOR_RED
circuit = /obj/item/weapon/circuitboard/camera
var/mapping = 0 // For the overview file (overview.dm), not used on this page
var/list/network = list()
var/list/available_networks = list()
var/list/watchers = list() //who's using the console, associated with the camera they're on.
@@ -16,7 +16,7 @@
/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[<name>] = list(<access>)
generate_network_access()
..()
/obj/machinery/computer/security/proc/generate_network_access()
available_networks["SS13"] = list(access_hos,access_captain)
available_networks["Telecomms"] = list(access_hos,access_captain)
@@ -39,7 +39,7 @@
available_networks["ERT"] = list(access_cent_specops_commander,access_cent_commander)
available_networks["CentComm"] = list(access_cent_security,access_cent_commander)
available_networks["Thunderdome"] = list(access_cent_thunder,access_cent_commander)
/obj/machinery/computer/security/Destroy()
if(watchers.len)
for(var/mob/M in watchers)
@@ -58,11 +58,11 @@
user.unset_machine()
return
return 1
/obj/machinery/computer/security/on_unset_machine(mob/user)
watchers.Remove(user)
user.reset_perspective(null)
/obj/machinery/computer/security/attack_hand(mob/user)
if(stat || ..())
user.unset_machine()
@@ -83,7 +83,7 @@
attack_hand(user)
else
attack_hand(user)
/obj/machinery.computer/security/proc/get_user_access(mob/user)
var/list/access = list()
if(ishuman(user))
@@ -105,8 +105,8 @@
ui.add_template("mapHeader", "sec_camera_map_header.tmpl")
ui.open()
/obj/machinery/computer/security/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/cameras = list()
@@ -126,7 +126,7 @@
cameras.Swap(j, j + 1)
data["cameras"] = cameras
var/list/access = get_user_access(user)
if(emagged)
access = get_all_accesses() // Assume captain level access when emagged
@@ -149,8 +149,8 @@
data["current"] = null
if(watchers[user])
var/obj/machinery/camera/watched = watchers[user]
data["current"] = watched.nano_structure()
data["current"] = watched.nano_structure()
return data
/obj/machinery/computer/security/Topic(href, href_list)
@@ -162,7 +162,7 @@
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras
if(!C)
return 1
switch_to_camera(usr, C)
else if(href_list["reset"])
@@ -180,31 +180,31 @@
else
network += net
break
nanomanager.update_uis(src)
// Check if camera is accessible when jumping
/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M)
if(CanUseTopic(M, default_state) != STATUS_INTERACTIVE || M.incapacitated() || M.blinded)
return 0
if(isrobot(M))
var/list/viewing = viewers(src)
if(!viewing.Find(M))
return 0
if(isAI(M))
var/mob/living/silicon/ai/A = M
if(!A.is_in_chassis())
return 0
if(!issilicon(M) && !Adjacent(M))
return 0
var/list/shared_networks = network & C.network
if(!shared_networks.len || !C.can_use())
return 0
return 1
// Switching to cameras
@@ -212,7 +212,7 @@
if(!can_access_camera(C, user))
user.unset_machine()
return 1
if(isAI(user))
var/mob/living/silicon/ai/A = user
A.eyeobj.setLoc(get_turf(C))
@@ -226,12 +226,12 @@
/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A)
if(user.machine != src)
return
var/obj/machinery/camera/jump_to
if(istype(A, /obj/machinery/camera))
jump_to = A
else if(ismob(A))
if(ishuman(A))
var/mob/living/carbon/human/H = A
@@ -239,11 +239,11 @@
else if(isrobot(A))
var/mob/living/silicon/robot/R = A
jump_to = R.camera
else if(isobj(A))
var/obj/O = A
jump_to = locate() in O
else if(isturf(A))
var/best_dist = INFINITY
for(var/obj/machinery/camera/camera in get_area(A))
@@ -255,10 +255,10 @@
if(dist < best_dist)
best_dist = dist
jump_to = camera
if(isnull(jump_to))
return
if(can_access_camera(jump_to, user))
switch_to_camera(user, jump_to)
+1 -1
View File
@@ -181,7 +181,7 @@ var/time_last_changed_position = 0
ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 775, 700)
ui.open()
/obj/machinery/computer/card/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["src"] = UID()
data["station_name"] = station_name()
+1 -1
View File
@@ -127,7 +127,7 @@
ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520)
ui.open()
/obj/machinery/computer/cloning/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["menu"] = src.menu
data["scanner"] = sanitize("[src.scanner]")
+13 -1
View File
@@ -276,6 +276,18 @@
src.emagged = 0
setMenuState(usr,COMM_SCREEN_MAIN)
if("RestartNanoMob")
if(mob_hunt_server)
if(mob_hunt_server.manual_reboot())
var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat",
"Capturing all of them", "Fixing minor text issues", "Being the very best",
"Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game")
to_chat(usr, "<span class='notice'>Restarting Nano-Mob Hunter GO! game server. [loading_msg]...</span>")
else
to_chat(usr, "<span class='warning'>Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.</span>")
else
to_chat(usr, "<span class='danger'>Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.</span>")
if("AcceptDocking")
to_chat(usr, "Docking request accepted!")
trade_dock_timelimit = world.time + 1200
@@ -322,7 +334,7 @@
// open the new ui window
ui.open()
/obj/machinery/computer/communications/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["is_ai"] = isAI(user)||isrobot(user)
data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state
@@ -20,7 +20,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/podtracker/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/podtracker/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/pods[0]
for(var/obj/item/device/spacepod_equipment/misc/tracker/TR in world)
+1 -1
View File
@@ -41,7 +41,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/robotics/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/robots = get_cyborgs(user)
if(robots.len)
+1 -1
View File
@@ -190,7 +190,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["isOperating"] = on
data["hasOccupant"] = occupant ? 1 : 0
@@ -27,7 +27,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data = list(
@@ -83,7 +83,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data = list(
@@ -146,7 +146,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data = list(
+1 -1
View File
@@ -180,7 +180,7 @@ FIRE ALARM
ui.open()
ui.set_auto_update(1)
/obj/machinery/firealarm/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/area/A = get_area(src)
+16 -13
View File
@@ -154,7 +154,7 @@ Class Procs:
if(use_power && stat == 0)
use_power(7500/severity)
new/obj/effect/overlay/temp/emp(src.loc)
new/obj/effect/overlay/temp/emp(loc)
..()
/obj/machinery/ex_act(severity)
@@ -184,9 +184,9 @@ Class Procs:
/obj/machinery/proc/auto_use_power()
if(!powered(power_channel))
return 0
if(src.use_power == 1)
if(use_power == 1)
use_power(idle_power_usage,power_channel, 1)
else if(src.use_power >= 2)
else if(use_power >= 2)
use_power(active_power_usage,power_channel, 1)
return 1
@@ -229,7 +229,7 @@ Class Procs:
if(!(href_list["set_tag"] in vars))
to_chat(usr, "<span class='warning'>Something went wrong: Unable to find [href_list["set_tag"]] in vars!</span>")
return 1
var/current_tag = src.vars[href_list["set_tag"]]
var/current_tag = vars[href_list["set_tag"]]
var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN)
if(newid)
vars[href_list["set_tag"]] = newid
@@ -355,13 +355,13 @@ Class Procs:
return 1
if(panel_open)
src.add_fingerprint(user)
add_fingerprint(user)
return 0
if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT))
return 1
src.add_fingerprint(user)
add_fingerprint(user)
return ..()
@@ -379,8 +379,8 @@ Class Procs:
/obj/machinery/proc/default_deconstruction_crowbar(var/obj/item/weapon/crowbar/C, var/ignore_panel = 0)
if(istype(C) && (panel_open || ignore_panel))
playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc)
playsound(loc, 'sound/items/Crowbar.ogg', 50, 1)
var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc)
M.state = 2
M.icon_state = "box_1"
for(var/obj/item/I in component_parts)
@@ -393,7 +393,7 @@ Class Procs:
/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/icon_state_open, var/icon_state_closed, var/obj/item/weapon/screwdriver/S)
if(istype(S))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(!panel_open)
panel_open = 1
icon_state = icon_state_open
@@ -407,7 +407,7 @@ Class Procs:
/obj/machinery/proc/default_change_direction_wrench(var/mob/user, var/obj/item/weapon/wrench/W)
if(panel_open && istype(W))
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
dir = turn(dir,-90)
to_chat(user, "<span class='notice'>You rotate [src].</span>")
return 1
@@ -416,11 +416,14 @@ Class Procs:
/obj/proc/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20)
if(istype(W))
to_chat(user, "<span class='notice'>Now [anchored ? "un" : ""]securing [name].</span>")
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, time, target = src))
to_chat(user, "<span class='notice'>You've [anchored ? "un" : ""]secured [name].</span>")
anchored = !anchored
playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
if(istype(src, /obj/machinery))
var/obj/machinery/M = src
M.power_change() //Turn on or off the machine depending on the status of power in the new area.
playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1)
return 1
return 0
@@ -506,7 +509,7 @@ Class Procs:
if(check_access && !allowed(perp))
threatcount += 4
if(auth_weapons && !src.allowed(perp))
if(auth_weapons && !allowed(perp))
if(istype(perp.l_hand, /obj/item/weapon/gun) || istype(perp.l_hand, /obj/item/weapon/melee))
threatcount += 4
+1 -1
View File
@@ -135,7 +135,7 @@
ui = new(user, src, ui_key, "poolcontroller.tmpl", "Pool Controller Interface", 520, 410)
ui.open()
/obj/machinery/poolcontroller/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/poolcontroller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["currentTemp"] = temperature
+1 -1
View File
@@ -49,7 +49,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/porta_turret/tag/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/porta_turret/tag/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["access"] = !isLocked(user)
data["locked"] = locked
+1 -1
View File
@@ -200,7 +200,7 @@ var/list/turret_icons
ui.open()
ui.set_auto_update(1)
/obj/machinery/porta_turret/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/porta_turret/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["access"] = !isLocked(user)
data["screen"] = screen
+1 -1
View File
@@ -115,7 +115,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
ui.open()
ui.set_auto_update(1)
/obj/machinery/requests_console/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/requests_console/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["department"] = department
+1 -1
View File
@@ -28,7 +28,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/slot_machine/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/slot_machine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["working"] = working
+1 -1
View File
@@ -75,7 +75,7 @@
ui = new(user, src, ui_key, "teleporter_console.tmpl", "Teleporter Console UI", 400, 400)
ui.open()
/obj/machinery/computer/teleporter/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/teleporter/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["powerstation"] = power_station
if(power_station)
+1 -1
View File
@@ -125,7 +125,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/turretid/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/turretid/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["access"] = !isLocked(user)
data["locked"] = locked
+3 -3
View File
@@ -416,7 +416,7 @@
ui = new(user, src, ui_key, "vending_machine.tmpl", src.name, 440, 600)
ui.open()
/obj/machinery/vending/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/vending/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/list/data = list()
if(currently_vending)
data["mode"] = 1
@@ -834,11 +834,11 @@
product_slogans = "Carts to go!"
icon_state = "cart"
icon_deny = "cart-deny"
products = list(/obj/item/device/pda =10,/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/chemistry = 10,
products = list(/obj/item/device/pda =10,/obj/item/weapon/cartridge/mob_hunt_game = 25,/obj/item/weapon/cartridge/medical = 10,/obj/item/weapon/cartridge/chemistry = 10,
/obj/item/weapon/cartridge/engineering = 10,/obj/item/weapon/cartridge/atmos = 10,/obj/item/weapon/cartridge/janitor = 10,
/obj/item/weapon/cartridge/signal/toxins = 10,/obj/item/weapon/cartridge/signal = 10)
contraband = list(/obj/item/weapon/cartridge/clown = 1,/obj/item/weapon/cartridge/mime = 1)
prices = list(/obj/item/device/pda =300,/obj/item/weapon/cartridge/medical = 200,/obj/item/weapon/cartridge/chemistry = 150,/obj/item/weapon/cartridge/engineering = 100,
prices = list(/obj/item/device/pda =300,/obj/item/weapon/cartridge/mob_hunt_game = 50,/obj/item/weapon/cartridge/medical = 200,/obj/item/weapon/cartridge/chemistry = 150,/obj/item/weapon/cartridge/engineering = 100,
/obj/item/weapon/cartridge/atmos = 75,/obj/item/weapon/cartridge/janitor = 100,/obj/item/weapon/cartridge/signal/toxins = 150,
/obj/item/weapon/cartridge/signal = 75)
+1 -1
View File
@@ -155,7 +155,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
if(!recharge_port)
reconnect()
+1 -1
View File
@@ -23,7 +23,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/mecha/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["screen"] = screen
if(screen == 0)
+1 -1
View File
@@ -48,7 +48,7 @@
ui.set_auto_update(1)
/obj/item/device/aicard/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/device/aicard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
var/mob/living/silicon/ai/AI = locate() in src
+3 -3
View File
@@ -33,8 +33,8 @@
song.ui_interact(user, ui_key, ui, force_open)
/obj/item/device/guitar/ui_data(mob/user, datum/topic_state/state = default_state)
return song.ui_data(user, state)
/obj/item/device/guitar/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
return song.ui_data(user, ui_key, state)
/obj/item/device/guitar/Topic(href, href_list)
song.Topic(href, href_list)
@@ -46,4 +46,4 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(/obj/item/weapon/screwdriver, /obj/item/weapon/wirecutters)
time = 80
time = 80
@@ -105,7 +105,7 @@
ui.open()
ui.set_auto_update(1)
/obj/item/device/radio/electropack/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/item/device/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["power"] = on
@@ -114,7 +114,7 @@ var/global/list/default_medbay_channels = list(
ui.open()
ui.set_auto_update(1)
/obj/item/device/radio/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/item/device/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["mic_status"] = broadcasting
@@ -757,7 +757,7 @@ var/global/list/default_medbay_channels = list(
ui.open()
ui.set_auto_update(1)
/obj/item/device/radio/borg/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/item/device/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["mic_status"] = broadcasting
@@ -101,7 +101,7 @@
// auto update every Master Controller tick
//ui.set_auto_update(1)
/obj/item/device/transfer_valve/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/item/device/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["attachmentOne"] = tank_one ? tank_one.name : null
+1 -1
View File
@@ -198,7 +198,7 @@ var/list/world_uplinks = list()
// open the new ui window
ui.open()
/obj/item/device/uplink/hidden/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/device/uplink/hidden/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["welcome"] = welcome
+2 -2
View File
@@ -38,8 +38,8 @@
song.ui_interact(user, ui_key, ui, force_open)
/obj/item/device/violin/ui_data(mob/user, datum/topic_state/state = default_state)
return song.ui_data(user, state)
/obj/item/device/violin/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
return song.ui_data(user, ui_key, state)
/obj/item/device/violin/Topic(href, href_list)
song.Topic(href, href_list)
+1 -1
View File
@@ -89,7 +89,7 @@ RCD
ui.open()
ui.set_auto_update(1)
/obj/item/weapon/rcd/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/weapon/rcd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["mode"] = mode
data["door_type"] = door_type
@@ -152,7 +152,7 @@
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/item/weapon/tank/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/item/weapon/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/using_internal
if(iscarbon(loc))
var/mob/living/carbon/C = loc
+3 -3
View File
@@ -122,7 +122,7 @@
ui.open()
ui.set_auto_update(1)
/datum/song/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["lines"] = lines
@@ -296,8 +296,8 @@
song.ui_interact(user, ui_key, ui, force_open)
/obj/structure/piano/ui_data(mob/user, datum/topic_state/state = default_state)
return song.ui_data(user, state)
/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
return song.ui_data(user, ui_key, state)
/obj/structure/piano/Topic(href, href_list)
song.Topic(href, href_list)
+5 -4
View File
@@ -120,10 +120,11 @@
return 0
var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter
if(!B)
blood_splatter(src,M.get_blood(M.vessel),1)
B = locate(/obj/effect/decal/cleanable/blood) in contents
B.add_blood_list(M)
return 1 //we bloodied the floor
B = blood_splatter(src,M.get_blood(M.vessel),1)
if(B)
B.add_blood_list(M)
return 1 //we bloodied the floor
return 0 // Clean floors club
// Only adds blood on the floor -- Skie
/turf/simulated/add_blood_floor(mob/living/carbon/M as mob)
+2 -1
View File
@@ -41,7 +41,8 @@ var/global/nologevent = 0
body += "<body>Options panel for <b>[M]</b>"
if(M.client)
body += " played by <b>[M.client]</b> "
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\]"
body += "\[<A href='?_src_=holder;editrights=rank;ckey=[M.ckey]'>[M.client.holder ? M.client.holder.rank : "Player"]</A>\] "
body += "\[<A href='?_src_=holder;getplaytimewindow=[M.UID()]'>" + M.client.get_exp_living() + "</a>\]"
if(istype(M, /mob/new_player))
body += " <B>Hasn't Entered Game</B> "
+1
View File
@@ -74,6 +74,7 @@ var/list/admin_verbs_admin = list(
/client/proc/debug_variables,
/client/proc/show_snpc_verbs,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
/client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/client/proc/toggle_mentor_chat
)
var/list/admin_verbs_ban = list(
+9
View File
@@ -1996,6 +1996,15 @@
fax_panel(usr)
else if(href_list["getplaytimewindow"])
if(!check_rights(R_ADMIN))
return
var/mob/M = locateUID(href_list["getplaytimewindow"])
if(!M)
to_chat(usr, "ERROR: Mob not found.")
return
cmd_show_exp_panel(M.client)
else if(href_list["jumpto"])
if(!check_rights(R_ADMIN)) return
@@ -0,0 +1,278 @@
/obj/machinery/computer/mob_battle_terminal
name = "Nano-Mob Hunter GO! Battle Terminal"
desc = "Insert a mob card to partake in life-like Nano-Mob Battle Action!"
icon_state = "mob_battle_empty"
icon_screen = null
icon_keyboard = null
density = 0
anchored = 1
var/obj/item/weapon/nanomob_card/card
var/datum/mob_hunt/mob_info
var/obj/effect/nanomob/battle/avatar
var/ready = 0
var/team = "Grey"
var/avatar_x_offset = 4
var/avatar_y_offset = 0
/obj/machinery/computer/mob_battle_terminal/red
pixel_y = 24
dir = SOUTH
team = "Red"
avatar_y_offset = -1
/obj/machinery/computer/mob_battle_terminal/blue
pixel_y = -24
dir = NORTH
team = "Blue"
avatar_y_offset = 1
/obj/machinery/computer/mob_battle_terminal/red/initialize()
..()
check_connection()
/obj/machinery/computer/mob_battle_terminal/blue/initialize()
..()
check_connection()
/obj/machinery/computer/mob_battle_terminal/update_icon()
if(card)
icon_state = "mob_battle_loaded"
else
icon_state = "mob_battle_empty"
..()
/obj/machinery/computer/mob_battle_terminal/Destroy()
eject_card(1)
if(mob_hunt_server)
if(mob_hunt_server.battle_turn)
mob_hunt_server.battle_turn = null
if(mob_hunt_server.red_terminal == src)
mob_hunt_server.red_terminal = null
if(mob_hunt_server.blue_terminal == src)
mob_hunt_server.blue_terminal = null
if(avatar)
qdel(avatar)
return ..()
/obj/machinery/computer/mob_battle_terminal/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/weapon/nanomob_card))
insert_card(O, user)
/obj/machinery/computer/mob_battle_terminal/proc/insert_card(obj/item/weapon/nanomob_card/new_card, mob/user)
if(!new_card)
return
if(card)
to_chat(user, "<span class='warning'>The card slot is currently filled.</span>")
return
if(!new_card.mob_data)
to_chat(user, "<span class='danger'>This is a blank mob card.</span>")
return
if(new_card.mob_data && !new_card.mob_data.cur_health)
to_chat(user, "<span class='warning'>This mob is incapacitated! Heal it before attempting to use it in battle!</span>")
return
user.unEquip(new_card)
new_card.forceMove(src)
card = new_card
mob_info = card.mob_data
update_icon()
update_avatar()
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/eject_card(override = 0)
if(!override)
if(ready && mob_hunt_server.battle_turn != team)
audible_message("You can't recall on your rival's turn!", null, 2)
return
card.mob_data = mob_info
mob_info = null
card.forceMove(get_turf(src))
card = null
update_avatar()
update_icon()
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/update_avatar()
//if we don't have avatars yet, spawn them
if(!avatar)
avatar = new(locate((x + avatar_x_offset), (y + avatar_y_offset), z))
//update avatar info from card
if(mob_info)
avatar.mob_info = mob_info
else
avatar.mob_info = null
//tell the avatar to update themself with the new info
avatar.update_self()
/obj/machinery/computer/mob_battle_terminal/attack_hand(mob/user)
add_fingerprint(user)
interact(user)
/obj/machinery/computer/mob_battle_terminal/attack_ai(mob/user)
to_chat(user, "<span class='warning'>You cannot interface with this portion of the simulation.</span>")
return
/obj/machinery/computer/mob_battle_terminal/interact(mob/user)
check_connection()
var/dat = ""
dat += "<table border='1' style='width:75%'>"
dat += "<tr>"
dat += "<td>"
dat += "[team] PLAYER"
dat += "</td>"
dat += "</tr>"
dat += "<tr>"
dat += "<td>"
if(!card)
dat += "<h1>No Nano-Mob card loaded.</h1>"
dat += "</td>"
dat += "</tr>"
if(ready && mob_hunt_server.battle_turn) //offer the surrender option if they are in a battle (ready), but don't have a card loaded
dat += "<tr>"
dat += "<td><a href='?src=[UID()];surrender=1'>Surrender!</a></td>"
dat += "</tr>"
else
dat += "<table>"
dat += "<tr>"
dat += "<td>"
dat += "<h1>[mob_info.mob_name]</h1>"
dat += "</td>"
if(mob_info.nickname)
dat += "<td rowspan='2'>"
else
dat += "<td>"
var/img_src = "[mob_info.icon_state_normal].png"
if(mob_info.is_shiny)
dat += "[mob_info.icon_state_shiny].png"
dat += "<img src='[img_src]'>"
dat += "</td>"
dat += "</tr>"
if(mob_info.nickname)
dat += "<tr>"
dat += "<td>"
dat += "<h2>[mob_info.nickname]</h2>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<hr>"
dat += "Health: [mob_info.cur_health] / [mob_info.max_health]<br>"
dat += "<table border='1'>"
dat += "<tr>"
if(mob_info.cur_health)
dat += "<td><a href='?src=[UID()];attack=1'>Attack!</a></td>"
else
dat += "<td>Incapacitated!</td>"
dat += "<td><a href='?src=[UID()];eject=1'>Recall!</a></td>"
dat += "</tr>"
dat += "</table>"
dat += "</td>"
dat += "</tr>"
if(!ready)
dat += "<tr>"
dat += "<td><a href='?src=[UID()];ready=1'>Battle!</a></td>"
dat += "</tr>"
if(ready && !mob_hunt_server.battle_turn)
dat += "<tr>"
dat += "<td><a href='?src=[UID()];ready=2'>Cancel Battle!</a></td>"
dat += "</tr>"
dat += "</table>"
var/datum/browser/popup = new(user, "mob_battle_terminal", "Nano-Mob Hunter GO! Battle Terminal", 575, 400)
popup.set_content(dat)
popup.open()
/obj/machinery/computer/mob_battle_terminal/Topic(href, href_list)
if(..())
return 1
if(href_list["attack"])
do_attack()
if(href_list["eject"])
eject_card()
if(href_list["surrender"])
surrender()
if(href_list["ready"])
var/option = text2num(href_list["ready"])
if(option == 1)
start_battle()
else if(option == 2)
ready = 0
audible_message("[team] Player cancels their battle challenge.", null, 5)
updateUsrDialog()
/obj/machinery/computer/mob_battle_terminal/proc/check_connection()
if(team == "Red")
if(mob_hunt_server && !mob_hunt_server.red_terminal)
mob_hunt_server.red_terminal = src
else if(team == "Blue")
if(mob_hunt_server && !mob_hunt_server.blue_terminal)
mob_hunt_server.blue_terminal = src
/obj/machinery/computer/mob_battle_terminal/proc/do_attack()
if(!ready) //no attacking if you arent ready to fight (duh)
return
if(!mob_hunt_server || team != mob_hunt_server.battle_turn) //don't attack unless it is actually our turn
return
else
var/message = "[mob_info.mob_name] attacks!"
if(mob_info.nickname)
message = "[mob_info.nickname] attacks!"
audible_message(message, null, 5)
mob_hunt_server.launch_attack(team, mob_info.get_raw_damage(), mob_info.get_attack_type())
/obj/machinery/computer/mob_battle_terminal/proc/start_battle()
if(ready) //don't do anything if we are still ready
return
if(!card) //don't do anything if there isn't a card inserted
return
ready = 1
audible_message("[team] Player is ready for battle! Waiting for rival...", null, 5)
mob_hunt_server.start_check()
/obj/machinery/computer/mob_battle_terminal/proc/receive_attack(raw_damage, datum/mob_type/attack_type)
var/message = mob_info.take_damage(raw_damage, attack_type)
avatar.audible_message(message, null, 5)
if(!mob_info.cur_health)
mob_hunt_server.end_battle(team)
eject_card(1) //force the card out, they were defeated
else
mob_hunt_server.end_turn()
/obj/machinery/computer/mob_battle_terminal/proc/surrender()
audible_message("[team] Player surrenders the battle!", null, 5)
mob_hunt_server.end_battle(team, 1)
//////////////////////////////
// Mob Healing Terminal //
// (Pokemon Center) //
//////////////////////////////
/obj/machinery/computer/mob_healer_terminal
name = "Nano-Mob Hunter GO! Restoration Terminal"
desc = "Swipe a mob card to instantly restore it to full health!"
icon_state = "mob_battle_loaded"
icon_screen = null
icon_keyboard = null
density = 0
anchored = 1
dir = EAST
/obj/machinery/computer/mob_healer_terminal/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/weapon/nanomob_card))
heal_card(O, user)
/obj/machinery/computer/mob_healer_terminal/proc/heal_card(obj/item/weapon/nanomob_card/patient, mob/user)
if(!patient)
return
if(!patient.mob_data)
to_chat(user, "<span class='danger'>This is a blank mob card.</span>")
return
if(patient.mob_data && patient.mob_data.cur_health == patient.mob_data.max_health)
to_chat(user, "<span class='warning'>This mob is already at maximum health!</span>")
return
patient.mob_data.cur_health = patient.mob_data.max_health
to_chat(user, "<span class='notify'>[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!</span>")
+162
View File
@@ -0,0 +1,162 @@
/obj/effect/nanomob
name = "Nano-Mob Avatar" //will be overridden by the mob datum name value when created
desc = "A wild Nano-Mob appeared! Hit it with your PDA with the game open to attempt to capture it!"
invisibility = 101
alpha = 128
anchored = 1 //just in case
density = 0
icon = 'icons/effects/mob_hunt.dmi'
var/state_name
var/datum/mob_hunt/mob_info = null
var/list/clients_encountered = list() //tracks who has already interacted with us, so they can't attempt a second capture
var/image/avatar
/obj/effect/nanomob/New(loc, datum/mob_hunt/new_info)
..()
if(!new_info)
qdel(src)
return
mob_info = new_info
update_self()
forceMove(mob_info.spawn_point)
if(!mob_info.is_trap)
addtimer(src, "despawn", mob_info.lifetime)
/obj/effect/nanomob/proc/update_self()
if(!mob_info)
return
name = mob_info.mob_name
desc = "A wild [name] (level [mob_info.level]) appeared! Hit it with your PDA with the game open to attempt to capture it!"
if(mob_info.is_shiny)
state_name = mob_info.icon_state_shiny
else
state_name = mob_info.icon_state_normal
avatar = image(icon, src, state_name)
avatar.override = 1
add_alt_appearance("nanomob_avatar", avatar)
/obj/effect/nanomob/attackby(obj/item/O, mob/user)
if(istype(O, /obj/item/device/pda))
var/obj/item/device/pda/P = O
attempt_capture(P, -20) //attempting a melee capture reduces the mob's effective run_chance by 20% to balance the risk of triggering a trap mob
return 1
/obj/effect/nanomob/hitby(obj/item/O)
if(istype(O, /obj/item/device/pda))
var/obj/item/device/pda/P = O
attempt_capture(P) //attempting a ranged capture does not affect the mob's effective run_chance but does prevent you from being shocked by a trap mob
return 1
/obj/effect/nanomob/proc/attempt_capture(obj/item/device/pda/P, catch_mod = 0) //negative catch_mods lower effective run chance,
if(!P || !P.current_app || !istype(P.current_app, /datum/data/pda/app/mob_hunter_game) || !P.cartridge)
return
var/datum/data/pda/app/mob_hunter_game/client = P.current_app
var/total_catch_mod = client.catch_mod + catch_mod //negative values decrease the chance of the mob running, positive values makes it more likely to flee
if(!client.connected) //must be connected to attempt captures
P.audible_message("[bicon(P)] No server connection. Capture aborted.", null, 4)
return
if(mob_info.is_trap) //traps work even if you ran into them before, which is why this is before the clients_encountered check
if(client.hacked) //hacked copies of the game (copies capable of setting traps) are protected from traps
return
if(iscarbon(P.loc))
var/mob/living/carbon/C = P.loc
//Strike them down with a lightning bolt to complete the illusion (copied from the surge reagent overdose, probably could make this a general-use proc in the future)
playsound(get_turf(C), 'sound/effects/eleczap.ogg', 75, 1)
var/icon/I=new('icons/obj/zap.dmi',"lightningend")
I.Turn(-135)
var/obj/effect/overlay/beam/B = new(get_turf(C))
B.pixel_x = rand(-20, 0)
B.pixel_y = rand(-20, 0)
B.icon = I
//then actually do the damage/stun
C.electrocute_act(20, P, 1) //same damage as a revenant's overload ability, except subject to gloves/species shock resistance (for human mobs at least)
return
if(client in clients_encountered) //we've already dealt with you, go away!
return
else //deal with the new hunter by either running away or getting caught
clients_encountered += client
var/message = "[bicon(P)] "
var/effective_run_chance = mob_info.run_chance + total_catch_mod
if((effective_run_chance > 0) && prob(effective_run_chance))
message += "Capture failed! [name] escaped [P.owner ? "from [P.owner]" : "from this hunter"]!"
conceal(client)
else
if(client.register_capture(mob_info, 1))
message += "Capture success! [P.owner ? P.owner : "This hunter"] captured [name]!"
conceal(client)
else
message += "Capture error! Try again."
clients_encountered -= client //if the capture registration failed somehow, let them have another chance with this mob
P.audible_message(message, null, 4)
/obj/effect/nanomob/proc/despawn()
if(mob_hunt_server)
if(mob_info.is_trap)
mob_hunt_server.trap_spawns -= src
else
mob_hunt_server.normal_spawns -= src
qdel(src)
/obj/effect/nanomob/proc/reveal()
if(!mob_hunt_server)
return
var/list/show_to = list()
for(var/A in mob_hunt_server.connected_clients)
if((A in clients_encountered) || !mob_hunt_server.connected_clients[A])
continue
show_to |= mob_hunt_server.connected_clients[A]
display_alt_appearance("nanomob_avatar", show_to)
/obj/effect/nanomob/proc/conceal(list/hide_from)
if(!mob_hunt_server)
return
var/list/hiding_from = list()
if(hide_from)
hiding_from = hide_from
else
for(var/A in mob_hunt_server.connected_clients)
if((A in clients_encountered) && mob_hunt_server.connected_clients[A])
hiding_from |= mob_hunt_server.connected_clients[A]
hide_alt_appearance("nanomob_avatar", hiding_from)
// BATTLE MOB AVATARS
/obj/effect/nanomob/battle
name = "Nano-Mob Battle Avatar"
desc = "A new challenger approaches!"
invisibility = 0
icon_state = "placeholder"
var/obj/machinery/computer/mob_battle_terminal/my_terminal
/obj/effect/nanomob/battle/New(loc, datum/mob_hunt/new_info)
if(new_info)
mob_info = new_info
update_self()
/obj/effect/nanomob/battle/update_self()
if(!mob_info)
name = "Nano-Mob Battle Avatar"
desc = "A new challenger approaches"
icon_state = "placeholder"
else
name = mob_info.mob_name
desc = "A tamed [name] (level [mob_info.level]) ready for battle!"
if(mob_info.is_shiny)
icon_state = mob_info.icon_state_shiny
else
icon_state = mob_info.icon_state_normal
/obj/effect/nanomob/battle/attempt_capture(obj/item/device/pda/P, catch_mod = 0)
//you can't capture battle avatars, since they belong to someone already
return
//battle avatars are always visible, so we can ignore reveal and conceal calls for them
/obj/effect/nanomob/battle/reveal()
return
/obj/effect/nanomob/battle/conceal()
return
+43
View File
@@ -0,0 +1,43 @@
//Standard Cards
/obj/item/weapon/nanomob_card
name = "Nano-Mob Hunter Trading Card"
desc = "A blank Nano-Mob Hunter Trading Card. Worthless!"
icon = 'icons/obj/card.dmi'
icon_state = "trade_card"
force = 0
throwforce = 1
w_class = 1
var/datum/mob_hunt/mob_data
/obj/item/weapon/nanomob_card/proc/update_info()
if(!mob_data)
return
if(mob_data.is_shiny)
name = "Holographic [mob_data.mob_name] Nano-Mob Hunter Card"
desc = "WOW! A holographic trading card containing a level [mob_data.level] [mob_data.mob_name]!"
icon_state = "trade_card_holo"
else
name = "[mob_data.mob_name] Nano-Mob Hunter Card"
desc = "A trading card containing a level [mob_data.level] [mob_data.mob_name]!"
//Booster Pack Cards (random mob data)
/obj/item/weapon/nanomob_card/booster
name = "Nano-Mob Hunter Booster Pack Card"
desc = "A random Nano-Mob Trading Card from a Booster Pack. Wonder what it is?"
/obj/item/weapon/nanomob_card/booster/New()
var/datum/mob_hunt/mob_info = pick(subtypesof(/datum/mob_hunt))
mob_data = new mob_info(0,null,1)
update_info()
//Booster Packs (Box of booster pack cards)
/obj/item/weapon/storage/box/nanomob_booster_pack
name = "Nano-Mob Hunter Trading Card Booster Pack"
desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!"
can_hold = list("/obj/item/weapon/nanomob_card")
/obj/item/weapon/storage/box/nanomob_booster_pack/New()
..()
for(var/i in 1 to 6)
new /obj/item/weapon/nanomob_card/booster(src)
+501
View File
@@ -0,0 +1,501 @@
#define TYPE_FIRE /datum/mob_type/fire
#define TYPE_WATER /datum/mob_type/water
#define TYPE_GRASS /datum/mob_type/grass
#define TYPE_ELECTRIC /datum/mob_type/electric
#define TYPE_GROUND /datum/mob_type/ground
#define TYPE_ROCK /datum/mob_type/rock
#define TYPE_BUG /datum/mob_type/bug
#define TYPE_POISON /datum/mob_type/poison
#define TYPE_NORMAL /datum/mob_type/normal
#define TYPE_FIGHTING /datum/mob_type/fighting
#define TYPE_PSYCHIC /datum/mob_type/psychic
#define TYPE_GHOST /datum/mob_type/ghost
#define TYPE_ICE /datum/mob_type/ice
#define TYPE_FLYING /datum/mob_type/flying
#define TYPE_BLUESPACE /datum/mob_type/bluespace
#define TYPE_DARK /datum/mob_type/dark
#define TYPE_STEEL /datum/mob_type/steel
/datum/mob_hunt
//GENERAL STATS AND VARIABLES
var/mob_name = "Generic Mob" //the mob's original name (its species/type/whatever)
var/nickname = "" //the mob's nickname (if given by the owner)
var/run_chance = 0 //percent chance the mob will escape capture attempts (higher is obviously more likely to get away)
//COMBAT STATS AND VARIABLES
var/level = 0 //actual level of this mob (don't set this, it gets overwritten in New())
var/min_level = 1 //minimum level of this mob (used for randomizing the actual level)
var/max_level = 1 //maximum level of this mob (used for randomizing the actual level)
var/exp = 0 //number of battles the mob has won towards the next level (resets to 0 on level-up)
var/exp_to_level = 3 //number of battles the mob must win to level up (in case we want to make some mobs harder or easier to level)
//the types of the mob will be used for battles to determine damage resistance or weakness (mob_type_datums.dm)
var/datum/mob_type/primary_type //Make sure you set this or the mob will be unable to deal damage and will take absurd damage in battles
var/datum/mob_type/secondary_type //Don't set if not a dual-type mob so the mob will only calculate damage based on primary type
var/base_attack = 5 //base damage dealt by the mob's attacks for battling (effectively damage dealt at level 0)
var/base_health = 5 //base health of the mob for battling (effectively max health at level 0)
var/attack_multiplier = 1 //how much additional damage per level the mob deals (level * attack_multiplier)
var/health_multiplier = 1 //how much additional health per level the mob gets (level * health_multiplier) for calculating max health
var/cur_health = 0
var/max_health = 0
//SPAWN PREFERENCES AND VARIABLES
//A note on mob spawn preferences: The mob types also have preferences, which are handled prior to per-mob preferences, so ultimately you use a combined set of preferences
var/list/area_blacklist = list() //list of areas this mob can NOT spawn in (such as the bridge)
var/list/turf_blacklist = list() //list of turfs this mob can NOT spawn on (such as wood floors)
var/list/area_whitelist = list() //list of areas this mob is more likely to spawn in (can be used to reinclude subtypes of blacklisted areas)
var/list/turf_whitelist = list() //list of turfs this mob is more likely to spawn on (can be used to reinclude subtypes of blacklisted turfs)
var/turf/spawn_point //gets set and sent to the game server to spawn its avatar (generated in select_spawn or assigned via set_trap)
var/lifetime = 6000 //number of deciseconds the mob will remain before despawning (REMEMBER: DECISECONDS! So 6000 is 600 seconds which is 10 minutes)
var/is_shiny = 0 //if this gets set at spawn (super rare), the mob is considered "shiny" and will use the shiny icon_state and holographic cards
//the icon file for mob_hunt stuff is 'icons/effects/mob_hunt.dmi' so reference that for the following vars
var/icon_state_normal = "placeholder" //the icon_state for this mob's normal version
var/icon_state_shiny = "placeholder" //the icon_state for this mob's rare shiney version
var/is_trap = 0 //if this gets set, the mob is a booby-trap and will electrocute any players that dare attempt to catch it
/datum/mob_hunt/New(set_trap = 0, turf/trap_turf = null, no_register = 0)
if(set_trap)
level = max_level
is_trap = 1
spawn_point = trap_turf
else
level = rand(min_level, max_level)
if(prob(1) && prob(1))
is_shiny = 1
max_health = base_health + (level * health_multiplier)
cur_health = max_health
if(primary_type)
primary_type = new primary_type()
if(secondary_type)
secondary_type = new secondary_type()
if(no_register) //for booster pack cards
return
if(mob_hunt_server)
if(set_trap)
if(mob_hunt_server.register_trap(src))
return
else if(select_spawn())
if(mob_hunt_server.register_spawn(src))
return
qdel(src) //if you reach this, the datum is just pure clutter, so delete it
/datum/mob_hunt/proc/select_spawn()
var/list/possible_areas = get_possible_areas()
if(!possible_areas.len)
log_admin("No possible areas to spawn [type] found. Possible code/mapping error?")
return 0
while(possible_areas.len)
//randomly select an area from our possible_areas list to try spawning in, then remove it from possible_areas so it won't get picked over and over forever.
var/area/spawn_area = locate(pickweight(possible_areas))
possible_areas -= spawn_area
if(!spawn_area)
break
//clear and generate a fresh list of turfs in the selected area, weighted based on white/black lists
var/list/possible_turfs = get_possible_turfs(spawn_area)
if(!possible_turfs.len) //If we don't have any possible turfs, this attempt was a failure. Try again.
continue
//if we got this far, we're spawning on this attempt, hooray!
spawn_point = pickweight(possible_turfs)
break
if(!spawn_point)
//if we get to this, we failed every attempt to find a suitable turf for EVERY area in our list of possible areas. DAMN.
log_admin("No acceptable turfs to spawn [type] on could be located. Possible code/mapping error, or someone replaced/destroyed all the acceptable turf types?")
return 0
return 1
/datum/mob_hunt/proc/get_possible_areas()
var/list/possible_areas = list()
//setup, sets all station areas (and subtypes) to weight 1
for(var/A in the_station_areas)
if(A == /area/holodeck) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on z2 as well
continue
if(A in possible_areas)
continue
for(var/areapath in typesof(A))
possible_areas[areapath] = 1
//primary type preferences
if(primary_type)
for(var/A in primary_type.area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in primary_type.area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//secondary type preferences
if(secondary_type)
for(var/A in secondary_type.area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in secondary_type.area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//mob preferences
for(var/A in area_whitelist)
for(var/areapath in typesof(A))
possible_areas[areapath] += 4
for(var/A in area_blacklist)
for(var/areapath in typesof(A))
possible_areas[areapath] -= 2
//removes "bad areas" which shouldn't be on-station but are subtypes of station areas. probably should the unused ones and consider repathing the rest
var/list/bad_areas = list(subtypesof(/area/construction), /area/solar/derelict_starboard, /area/solar/derelict_aft, /area/solar/constructionsite)
for(var/A in bad_areas)
possible_areas -= A
//weight check, remove negative or zero weight areas from the list, then return the list.
for(var/areapath in possible_areas)
//remove any areas that shouldn't be on the station-level
if(possible_areas[areapath] < 1)
possible_areas -= areapath
continue
return possible_areas
/datum/mob_hunt/proc/get_possible_turfs(area/spawn_area)
if(!spawn_area)
return list()
var/list/possible_turfs = list()
//setup, sets all turfs in spawn_area to weight 1
for(var/turf/T in spawn_area)
if(!is_station_level(T.z)) //mobs will only consider station-level turfs for spawning. Largely here so we won't have to worry about mapping errors or mobs on the derelict solars
continue
possible_turfs[T] = 1
//primary type preferences
if(primary_type)
if(is_type_in_list(T, primary_type.turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, primary_type.turf_blacklist))
possible_turfs[T] -= 2
//secondary type preferences
if(secondary_type)
if(is_type_in_list(T, secondary_type.turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, secondary_type.turf_blacklist))
possible_turfs[T] -= 2
//mob preferences
if(is_type_in_list(T, turf_whitelist))
possible_turfs[T] += 4
if(is_type_in_list(T, turf_blacklist))
possible_turfs[T] -= 2
//weight check, remove negative or zero weight turfs from the list, then return the list
if(possible_turfs[T] < 1)
possible_turfs -= T
return possible_turfs
/datum/mob_hunt/proc/calc_def_multiplier(datum/mob_type/attack_type)
if(!primary_type)
return 99 //typeless mobs are weak to everything since they shouldn't exist
if(!attack_type) //typeless attacks will return a multiplier of 1 in case we want to use this for calculating unmodified damage for some reason (UI maybe?)
return 1
var/multiplier = 1
if(attack_type in primary_type.immunity)
return 0 //a single immunity negates all damage
else if(attack_type in primary_type.resistance)
multiplier *= 0.5
else if(attack_type in primary_type.weakness)
multiplier *= 2
else
multiplier *= 1
if(!secondary_type) //if we don't have a second type, we're done here
return multiplier
if(attack_type in secondary_type.immunity)
return 0 //a single immunity negates all damage
else if(attack_type in secondary_type.resistance)
multiplier *= 0.5
else if(attack_type in secondary_type.weakness)
multiplier *= 2
else
multiplier *= 1
return multiplier
/datum/mob_hunt/proc/take_damage(raw_damage, datum/mob_type/attack_type)
var/message = ""
var/multiplier = calc_def_multiplier(attack_type)
var/total_damage = raw_damage * multiplier
if(!cur_health) //it's already downed, quit hitting it
return null
if(!total_damage)
message += "The attack is completely ineffective! "
else
cur_health = max(cur_health - total_damage, 0)
switch(multiplier)
if(0)
message += "The attack is completely ineffective! "
if(0.25)
message += "It's barely effective... "
if(0.5)
message += "It's not very effective... "
if(2)
message += "It's super effective! "
if(4)
message += "It's ultra effective! "
if(99)
message += pick("REKT! ", "DUNKED! ", "DEFENSE BREAK! ", "WOMBO-COMBO'D!")
if(!cur_health)
message += "[nickname ? nickname : mob_name] is downed!"
return message
/datum/mob_hunt/proc/get_raw_damage()
return (level * attack_multiplier)
/datum/mob_hunt/proc/get_attack_type()
var/datum/mob_type/attack_type = primary_type
if(secondary_type && prob(40))
attack_type = secondary_type
return attack_type
/datum/mob_hunt/proc/gain_exp()
exp++
var/message = "[nickname ? nickname : mob_name] gained EXP! ([exp] / [exp_to_level] EXP)"
if(exp >= exp_to_level)
message = levelup()
return message
/datum/mob_hunt/proc/levelup()
var/message = ""
level++
exp = 0
if(level > max_level) //This is where we would trigger an evolution, when those are added (need to add evolved forms first)
level = max_level //for now though, we'll just cap them back at their max_level
message += "[nickname ? nickname : mob_name] can't get any stronger right now!"
else
max_health = base_health + (level * health_multiplier)
cur_health = max_health //full heal on level-up
message += "[nickname ? nickname : mob_name] has reached level [level]!"
return message
/datum/mob_hunt/proc/get_type1()
if(!primary_type)
return "Typeless"
else
return primary_type.name
/datum/mob_hunt/proc/get_type2()
if(!secondary_type)
return null
else
return secondary_type.name
/datum/mob_hunt/nemabug
mob_name = "Nemabug"
run_chance = 50
min_level = 1
max_level = 10
primary_type = TYPE_BUG
icon_state_normal = "nemabug"
icon_state_shiny = "nemabug_shiny"
lifetime = 6000
/datum/mob_hunt/stoutquill
mob_name = "Stoutquill"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_ICE
icon_state_normal = "stoutquill"
icon_state_shiny = "stoutquill_shiny"
lifetime = 4500
/datum/mob_hunt/spectra
mob_name = "Spectra"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_POISON
icon_state_normal = "spectra"
icon_state_shiny = "spectra_shiny"
lifetime = 6000
/datum/mob_hunt/dunny
mob_name = "Dunny"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_FIRE
icon_state_normal = "dunny"
icon_state_shiny = "dunny_shiny"
lifetime = 6000
/datum/mob_hunt/buffsel
mob_name = "Buffsel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_ROCK
icon_state_normal = "buffsel"
icon_state_shiny = "buffsel_shiny"
lifetime = 6000
/datum/mob_hunt/quarrel
mob_name = "Quarrel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_NORMAL
icon_state_normal = "quarrel"
icon_state_shiny = "quarrel_shiny"
lifetime = 6000
/datum/mob_hunt/vulerrt
mob_name = "Vulerrt"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_DARK
icon_state_normal = "vulerrt"
icon_state_shiny = "vulerrt_shiny"
turf_whitelist = list()
lifetime = 4500
/datum/mob_hunt/strudel
mob_name = "Strudel"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_ELECTRIC
icon_state_normal = "strudel"
icon_state_shiny = "strudel_shiny"
lifetime = 4500
/datum/mob_hunt/folstick
mob_name = "Folstick"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_WATER
icon_state_normal = "folstick"
icon_state_shiny = "folstick_shiny"
lifetime = 6000
/datum/mob_hunt/glimmerflare
mob_name = "Glimmerflare"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_PSYCHIC
icon_state_normal = "glimmerflare"
icon_state_shiny = "glimmerflare_shiny"
lifetime = 4500
/datum/mob_hunt/leecoon
mob_name = "Leecoon"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_GRASS
icon_state_normal = "leecoon"
icon_state_shiny = "leecoon_shiny"
lifetime = 6000
/datum/mob_hunt/halk
mob_name = "Halk"
run_chance = 35
min_level = 1
max_level = 10
primary_type = TYPE_FLYING
icon_state_normal = "halk"
icon_state_shiny = "halk_shiny"
lifetime = 6000
/datum/mob_hunt/gooby
mob_name = "Gooby"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_ELECTRIC
secondary_type = TYPE_BUG
icon_state_normal = "gooby"
icon_state_shiny = "gooby_shiny"
lifetime = 3000
/datum/mob_hunt/pandoom
mob_name = "Pandoom"
run_chance = 50
min_level = 5
max_level = 15
primary_type = TYPE_GHOST
icon_state_normal = "pandoom"
icon_state_shiny = "pandoom_shiny"
lifetime = 4500
/datum/mob_hunt/relish
mob_name = "Relish"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIRE
secondary_type = TYPE_GROUND
icon_state_normal = "relish"
icon_state_shiny = "relish_shiny"
lifetime = 3000
/datum/mob_hunt/xofine
mob_name = "Xofine"
run_chance = 50
min_level = 5
max_level = 10
primary_type = TYPE_FIRE
secondary_type = TYPE_NORMAL
icon_state_normal = "xofine"
icon_state_shiny = "xofine_shiny"
lifetime = 3000
/datum/mob_hunt/gitten
mob_name = "Gitten"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_WATER
secondary_type = TYPE_POISON
icon_state_normal = "gitten"
icon_state_shiny = "gitten_shiny"
lifetime = 3000
/datum/mob_hunt/nai
mob_name = "Nai"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_GRASS
secondary_type = TYPE_NORMAL
icon_state_normal = "nai"
icon_state_shiny = "nai_shiny"
lifetime = 3000
/datum/mob_hunt/pyroghast
mob_name = "Pyroghast"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIRE
secondary_type = TYPE_GHOST
icon_state_normal = "pyroghast"
icon_state_shiny = "pyroghast"
lifetime = 4500
/datum/mob_hunt/starslam
mob_name = "Starslam"
run_chance = 65
min_level = 5
max_level = 20
primary_type = TYPE_FIGHTING
secondary_type = TYPE_ICE
icon_state_normal = "starslam"
icon_state_shiny = "starslam_shiny"
lifetime = 2500
/datum/mob_hunt/pheron
mob_name = "Pheron"
run_chance = 85
min_level = 10
max_level = 20
primary_type = TYPE_BLUESPACE
icon_state_normal = "pheron"
icon_state_shiny = "pheron_shiny"
area_blacklist = list()
turf_blacklist = list()
area_whitelist = list()
turf_whitelist = list()
lifetime = 2000
@@ -0,0 +1,256 @@
/datum/mob_type
var/name = "Typeless"
var/list/weakness = list()
var/list/resistance = list()
var/list/immunity = list()
//Type-based spawn preferences (to eliminate copy-pasting the same area and turf lists on each mob type)
var/list/area_blacklist = list() //areas to be avoided
var/list/area_whitelist = list() //areas to be more preferred
var/list/turf_blacklist = list() //turf types to be avoided
var/list/turf_whitelist = list() //turf types to be more preferred
//Type defines, to avoid spelling mistakes
/datum/mob_type/fire
name = "Fire"
weakness = list(TYPE_WATER,
TYPE_ROCK,
TYPE_GROUND)
resistance = list(TYPE_BUG,
TYPE_FIRE,
TYPE_GRASS,
TYPE_ICE,
TYPE_STEEL)
area_blacklist = list(/area/crew_quarters/toilet,
/area/crew_quarters/sleep_male/toilet_male,
/area/crew_quarters/sleep_female/toilet_female,
/area/crew_quarters/locker/locker_toilet,
/area/toxins/server_coldroom)
area_whitelist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
turf_blacklist = list(/turf/simulated/floor/beach/water)
/datum/mob_type/water
name = "Water"
weakness = list(TYPE_ELECTRIC,
TYPE_GRASS)
resistance = list(TYPE_FIRE,
TYPE_ICE,
TYPE_STEEL,
TYPE_WATER)
area_blacklist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
area_whitelist = list(/area/crew_quarters/toilet,
/area/crew_quarters/sleep_male/toilet_male,
/area/crew_quarters/sleep_female/toilet_female,
/area/crew_quarters/locker/locker_toilet)
turf_whitelist = list(/turf/simulated/floor/beach/water)
/datum/mob_type/grass
name = "Grass"
weakness = list(TYPE_FIRE,
TYPE_BUG,
TYPE_POISON,
TYPE_ICE,
TYPE_FLYING)
resistance = list(TYPE_WATER,
TYPE_GRASS,
TYPE_ELECTRIC,
TYPE_GROUND)
area_blacklist = list(/area/toxins)
area_whitelist = list(/area/hydroponics,
/area/hallway/secondary/construction)
turf_whitelist = list(/turf/simulated/floor/grass)
/datum/mob_type/electric
name = "Electric"
weakness = list(TYPE_GROUND)
resistance = list(TYPE_ELECTRIC,
TYPE_FLYING,
TYPE_STEEL)
area_whitelist = list(/area/engine,
/area/toxins/server,
/area/maintenance,
/area/turret_protected/ai,
/area/turret_protected/ai_upload,
/area/turret_protected/aisat_interior,
/area/aisat,
/area/assembly)
turf_whitelist = list(/turf/simulated/floor/bluegrid)
/datum/mob_type/ground
name = "Ground"
weakness = list(TYPE_WATER,
TYPE_GRASS,
TYPE_ICE)
resistance = list(TYPE_ROCK,
TYPE_POISON)
immunity = list(TYPE_ELECTRIC)
/datum/mob_type/rock
name = "Rock"
weakness = list(TYPE_WATER,
TYPE_GRASS,
TYPE_GROUND,
TYPE_FIGHTING,
TYPE_STEEL)
resistance = list(TYPE_FIRE,
TYPE_FLYING,
TYPE_POISON,
TYPE_NORMAL)
area_whitelist = list(/area/quartermaster,
/area/maintenance/disposal)
turf_whitelist = list(/turf/simulated/wall,
/turf/simulated/floor/mineral)
/datum/mob_type/bug
name = "Bug"
weakness = list(TYPE_FIRE,
TYPE_ROCK,
TYPE_FLYING)
resistance = list(TYPE_GRASS,
TYPE_GROUND,
TYPE_FIGHTING)
area_blacklist = list(/area/toxins)
area_whitelist = list(/area/hydroponics,
/area/hallway/secondary/construction)
turf_whitelist = list(/turf/simulated/floor/grass)
/datum/mob_type/poison
name = "Poison"
weakness = list(TYPE_GROUND,
TYPE_PSYCHIC)
resistance = list(TYPE_GRASS,
TYPE_BUG,
TYPE_POISON,
TYPE_FIGHTING)
area_blacklist = list(/area/medical,
/area/security/medbay,
/area/janitor)
area_whitelist = list(/area/medical/virology,
/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2)
/datum/mob_type/normal
name = "Normal"
weakness = list(TYPE_FIGHTING)
immunity = list(TYPE_GHOST)
/datum/mob_type/fighting
name = "Fighting"
weakness = list(TYPE_PSYCHIC,
TYPE_FLYING)
resistance = list(TYPE_ROCK,
TYPE_BUG,
TYPE_DARK)
area_blacklist = list(/area/medical)
area_whitelist = list(/area/crew_quarters/bar,
/area/crew_quarters/fitness,
/area/security)
turf_whitelist = list(/turf/simulated/floor/wood)
/datum/mob_type/psychic
name = "Psychic"
weakness = list(TYPE_BUG,
TYPE_GHOST,
TYPE_DARK)
resistance = list(TYPE_FIGHTING,
TYPE_PSYCHIC)
area_blacklist = list(/area/toxins,
/area/medical/research,
/area/medical/research_shuttle_dock,
/area/crew_quarters/hor,
/area/maintenance/asmaint2,
/area/teleporter,
/area/gateway)
area_whitelist = list(/area/library,
/area/chapel,
/area/medical/psych)
/datum/mob_type/ghost
name = "Ghost"
weakness = list(TYPE_GHOST,
TYPE_DARK)
resistance = list(TYPE_BUG,
TYPE_POISON)
immunity = list(TYPE_NORMAL,
TYPE_FIGHTING)
area_whitelist = list(/area/medical/morgue,
/area/chapel,
/area/medical/genetics_cloning)
/datum/mob_type/ice
name = "Ice"
weakness = list(TYPE_FIRE,
TYPE_ROCK,
TYPE_FIGHTING,
TYPE_STEEL)
resistance = list(TYPE_ICE)
area_blacklist = list(/area/maintenance/turbine,
/area/maintenance/incinerator,
/area/crew_quarters/kitchen)
area_whitelist = list(/area/toxins/server_coldroom)
/datum/mob_type/flying
name = "Flying"
weakness = list(TYPE_ELECTRIC,
TYPE_ROCK,
TYPE_ICE)
resistance = list(TYPE_GRASS,
TYPE_BUG,
TYPE_FIGHTING)
immunity = list(TYPE_GROUND)
area_blacklist = list(/area/maintenance)
area_whitelist = list(/area/hallway,
/area/escapepodbay,
/area/engine/mechanic_workshop,
/area/security/podbay)
/datum/mob_type/bluespace
name = "Bluespace"
weakness = list(TYPE_ICE,
TYPE_BLUESPACE)
resistance = list(TYPE_FIRE,
TYPE_WATER,
TYPE_GRASS,
TYPE_ELECTRIC)
/datum/mob_type/dark
name = "Dark"
weakness = list(TYPE_BUG,
TYPE_FIGHTING)
resistance = list(TYPE_GHOST,
TYPE_DARK)
immunity = list(TYPE_PSYCHIC)
area_blacklist = list(/area/solar,
/area/maintenance/auxsolarport,
/area/maintenance/starboardsolar,
/area/maintenance/portsolar,
/area/maintenance/auxsolarstarboard,
/area/clownoffice)
area_whitelist = list(/area/maintenance,
/area/assembly/assembly_line,
/area/mimeoffice)
turf_blacklist = list(/turf/simulated/floor/light)
/datum/mob_type/steel
name = "Steel"
weakness = list(TYPE_FIRE,
TYPE_GROUND,
TYPE_FIGHTING)
resistance = list(TYPE_GRASS,
TYPE_ROCK,
TYPE_BUG,
TYPE_NORMAL,
TYPE_PSYCHIC,
TYPE_ICE,
TYPE_FLYING,
TYPE_BLUESPACE,
TYPE_STEEL)
immunity = list(TYPE_POISON)
+6
View File
@@ -190,6 +190,12 @@ var/global/datum/prizes/global_prizes = new
typepath = /obj/item/weapon/spellbook/oneuse/fake_gib
cost = 100
/datum/prize_item/nanomob_booster
name = "Nano-Mob Hunter Trading Card Booster Pack"
desc = "Contains 6 random Nano-Mob Hunter Trading Cards. May contain a holographic card!"
typepath = /obj/item/weapon/storage/box/nanomob_booster_pack
cost = 100
/datum/prize_item/capgun
name = "Capgun Revolver"
desc = "Do you feel lucky... punk?"
@@ -24,7 +24,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/artillerycontrol/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/artillerycontrol/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/time_to_wait = round(reload_cooldown - ((world.time / 10) - last_fire), 1)
+24 -16
View File
@@ -9,14 +9,14 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
unacidable = 1
var/active = 0
/obj/machinery/gateway/initialize()
..()
update_icon()
update_density_from_dir()
/obj/machinery/gateway/New()
if(!the_gateway)
the_gateway = src
spawn(25)
update_icon()
if(dir == 2)
density = 0
/obj/machinery/gateway/proc/update_density_from_dir()
if(dir == 2)
density = 0
/obj/machinery/gateway/update_icon()
@@ -40,13 +40,18 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
var/obj/machinery/gateway/centeraway/awaygate = null
/obj/machinery/gateway/centerstation/New()
..()
if(!the_gateway)
the_gateway = src
spawn(25)
update_icon()
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway) in world
/obj/machinery/gateway/centerstation/initialize()
..()
update_icon()
wait = world.time + config.gateway_delay //+ thirty minutes default
awaygate = locate(/obj/machinery/gateway/centeraway) in world
/obj/machinery/gateway/centerstation/update_density_from_dir()
return
/obj/machinery/gateway/centerstation/Destroy()
if(the_gateway == src)
@@ -61,7 +66,7 @@ var/obj/machinery/gateway/centerstation/the_gateway = null
obj/machinery/gateway/centerstation/process()
/obj/machinery/gateway/centerstation/process()
if(stat & (NOPOWER))
if(active) toggleoff()
return
@@ -165,12 +170,15 @@ obj/machinery/gateway/centerstation/process()
var/obj/machinery/gateway/centeraway/stationgate = null
/obj/machinery/gateway/centeraway/New()
spawn(25)
update_icon()
stationgate = locate(/obj/machinery/gateway/centerstation) in world
/obj/machinery/gateway/centeraway/initialize()
..()
update_icon()
stationgate = locate(/obj/machinery/gateway/centerstation) in world
/obj/machinery/gateway/centeraway/update_density_from_dir()
return
/obj/machinery/gateway/centeraway/update_icon()
if(active)
icon_state = "oncenter"
+20 -1
View File
@@ -261,6 +261,8 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
send_asset_list(client, uncommon)
send_asset_list(client, common)
//Pill sprites for UIs
/datum/asset/chem_master
var/assets = list()
var/verify = FALSE
@@ -274,4 +276,21 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
register_asset(asset_name, assets[asset_name])
/datum/asset/chem_master/send(client)
send_asset_list(client,assets,verify)
send_asset_list(client, assets, verify)
//Mob Hunt sprites for UIs
/datum/asset/mob_hunt
var/assets = list()
var/verify = FALSE
/datum/asset/mob_hunt/register()
for(var/state in icon_states('icons/effects/mob_hunt.dmi'))
if(state == "Placeholder")
continue
assets["[state].png"] = icon('icons/effects/mob_hunt.dmi', state)
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/mob_hunt/send(client)
send_asset_list(client, assets, verify)
+146 -24
View File
@@ -111,49 +111,49 @@
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Barber",5)
DB_job_unlock("Barber",5)
return
if("2")
if(karma <5)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Brig Physician",5)
DB_job_unlock("Brig Physician",5)
return
if("3")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Nanotrasen Representative",30)
DB_job_unlock("Nanotrasen Representative",30)
return
if("5")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Blueshield",30)
DB_job_unlock("Blueshield",30)
return
if("6")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Mechanic",30)
DB_job_unlock("Mechanic",30)
return
if("7")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Magistrate",45)
DB_job_unlock("Magistrate",45)
return
if("9")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_job_unlock("Security Pod Pilot",30)
DB_job_unlock("Security Pod Pilot",30)
return
if(href_list["KarmaBuy2"])
var/karma=verify_karma()
@@ -163,55 +163,55 @@
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Machine",15)
DB_species_unlock("Machine",15)
return
if("2")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Kidan",30)
DB_species_unlock("Kidan",30)
return
if("3")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Grey",30)
DB_species_unlock("Grey",30)
return
if("4")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Vox",45)
DB_species_unlock("Vox",45)
return
if("5")
if(karma <45)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Slime People",45)
DB_species_unlock("Slime People",45)
return
if("6")
if(karma <100)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Plasmaman",100)
DB_species_unlock("Plasmaman",100)
return
if("7")
if(karma <30)
to_chat(usr, "You do not have enough karma!")
return
else
src.DB_species_unlock("Drask",30)
DB_species_unlock("Drask",30)
return
if(href_list["KarmaRefund"])
var/type = href_list["KarmaRefundType"]
var/job = href_list["KarmaRefund"]
var/cost = href_list["KarmaRefundCost"]
src.karmarefund(type,job,cost)
karmarefund(type,job,cost)
return
switch(href_list["_src_"])
@@ -273,6 +273,7 @@
//CONNECT//
///////////
/client/New(TopicData)
var/tdata = TopicData //save this for later use
chatOutput = new /datum/chatOutput(src) // Right off the bat.
TopicData = null //Prevent calls to client.Topic from connect
@@ -290,8 +291,8 @@
// Change the way they should download resources.
if(config.resource_urls)
src.preload_rsc = pick(config.resource_urls)
else src.preload_rsc = 1 // If config.resource_urls is not set, preload like normal.
preload_rsc = pick(config.resource_urls)
else preload_rsc = 1 // If config.resource_urls is not set, preload like normal.
to_chat(src, "\red If the title screen is black, resources are still downloading. Please be patient until the title screen appears.")
@@ -340,7 +341,7 @@
winset(src, null, "command=\".configure graphics-hwmode off\"")
winset(src, null, "command=\".configure graphics-hwmode on\"")
log_client_to_db()
log_client_to_db(tdata)
if(ckey in clientmessages)
for(var/message in clientmessages[ckey])
@@ -399,7 +400,7 @@
donator_level = text2num(query_donor_select.item[2])
break
/client/proc/log_client_to_db()
/client/proc/log_client_to_db(connectiontopic)
if(IsGuestKey(key))
return
@@ -430,6 +431,14 @@
if(ckey != query_cid.item[1])
related_accounts_cid.Add("[query_cid.item[1]]")
var/admin_rank = "Player"
if(holder)
admin_rank = holder.rank
// Admins don't get slammed by this, I guess
else
if(check_randomizer(connectiontopic))
return
//Log all the alts
if(related_accounts_cid.len)
log_access("Alts: [key_name(src)]:[jointext(related_accounts_cid, " - ")]")
@@ -446,10 +455,6 @@
if(!isnum(sql_id))
return
var/admin_rank = "Player"
if(src.holder)
admin_rank = src.holder.rank
var/sql_ip = sanitizeSQL(address)
var/sql_computerid = sanitizeSQL(computer_id)
var/sql_admin_rank = sanitizeSQL(admin_rank)
@@ -469,11 +474,128 @@
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[ckey]','[sql_ip]','[sql_computerid]');")
query_accesslog.Execute()
#undef TOPIC_SPAM_DELAY
#undef UPLOAD_LIMIT
#undef MIN_CLIENT_VERSION
// Returns true if a randomizer is being used
/client/proc/check_randomizer(topic)
. = FALSE
if(connection != "seeker") //Invalid connection type.
return null
topic = params2list(topic)
if(!config.check_randomizer)
return
// Stash o' ckeys
var/static/cidcheck = list()
var/static/tokens = list()
// Ckeys that failed the test, stored to send acceptance messages only for atoners
var/static/cidcheck_failedckeys = list()
var/static/cidcheck_spoofckeys = list()
var/oldcid = cidcheck[ckey]
if(!oldcid)
var/DBQuery/query_cidcheck = dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'")
query_cidcheck.Execute()
var/lastcid = computer_id
if(query_cidcheck.NextRow())
lastcid = query_cidcheck.item[1]
if(computer_id != lastcid)
// Their current CID does not match what the DB says - OFF WITH THEIR HEAD
cidcheck[ckey] = computer_id
// Disable the reconnect button to force a CID change
winset(src, "reconnectbutton", "is-disable=true")
tokens[ckey] = cid_check_reconnect()
sleep(10) // Since browse is non-instant, and kinda async
to_chat(src, "<pre class=\"system system\">you're a huge nerd. wakka wakka doodle doop nobody's ever gonna see this, the chat system shouldn't be online by this point</pre>")
del(src)
return TRUE
else
if (!topic || !topic["token"] || !tokens[ckey] || topic["token"] != tokens[ckey])
if (!cidcheck_spoofckeys[ckey])
message_admins("<span class='adminnotice'>[key_name(src)] appears to have attempted to spoof a cid randomizer check.</span>")
cidcheck_spoofckeys[ckey] = TRUE
cidcheck[ckey] = computer_id
tokens[ckey] = cid_check_reconnect()
sleep(10) //browse is queued, we don't want them to disconnect before getting the browse() command.
del(src)
return TRUE
// We DO have their cached CID handy - compare it, now
if(oldcid != computer_id)
// Change detected, they are randomizing
cidcheck -= ckey // To allow them to try again after removing CID randomization
to_chat(src, "<span class='userdanger'>Connection Error:</span>")
to_chat(src, "<span class='danger'>Invalid ComputerID(spoofed). Please remove the ComputerID spoofer from your BYOND installation and try again.</span>")
if(!cidcheck_failedckeys[ckey])
message_admins("<span class='adminnotice'>[key_name(src)] has been detected as using a CID randomizer. Connection rejected.</span>")
send2irc(config.cidrandomizer_irc, "[key_name(src)] has been detected as using a CID randomizer. Connection rejected.")
cidcheck_failedckeys[ckey] = TRUE
note_randomizer_user()
log_access("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])")
del(src)
return TRUE
else
// don't shoot, I'm innocent
if(cidcheck_failedckeys[ckey])
// Atonement
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after showing they removed their cid randomizer</span>")
send2irc(config.cidrandomizer_irc, "[key_name(src)] has been allowed to connect after showing they removed their cid randomizer.")
cidcheck_failedckeys -= ckey
if (cidcheck_spoofckeys[ckey])
message_admins("<span class='adminnotice'>[key_name_admin(src)] has been allowed to connect after appearing to have attempted to spoof a cid randomizer check because it <i>appears</i> they aren't spoofing one this time</span>")
cidcheck_spoofckeys -= ckey
cidcheck -= ckey
/client/proc/note_randomizer_user()
var/const/adminckey = "CID-Error"
// Check for notes in the last day - only 1 note per 24 hours
var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()")
if(!query_get_notes.Execute())
var/err = query_get_notes.ErrorMsg()
log_game("SQL ERROR obtaining id from notes table. Error : \[[err]\]\n")
return
if(query_get_notes.NextRow())
return
// Only add a note if their most recent note isn't from the randomizer blocker, either
query_get_notes = dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1")
if(!query_get_notes.Execute())
var/err = query_get_notes.ErrorMsg()
log_game("SQL ERROR obtaining adminckey from notes table. Error : \[[err]\]\n")
return
if(query_get_notes.NextRow())
if(query_get_notes.item[1] == adminckey)
return
add_note(ckey, "Detected as using a cid randomizer.", null, adminckey, logged = 0)
/client/proc/cid_check_reconnect()
var/token = md5("[rand(0,9999)][world.time][rand(0,9999)][ckey][rand(0,9999)][address][rand(0,9999)][computer_id][rand(0,9999)]")
. = token
log_access("Failed Login: [key] [computer_id] [address] - CID randomizer check")
var/url = winget(src, null, "url")
//special javascript to make them reconnect under a new window.
src << browse("<a id='link' href='byond://[url]?token=[token]'>\
byond://[url]?token=[token]\
</a>\
<script type='text/javascript'>\
document.getElementById(\"link\").click();\
window.location=\"byond://winset?command=.quit\"\
</script>",
"border=0;titlebar=0;size=1x1")
to_chat(src, "<a href='byond://[url]?token=[token]'>You will be automatically taken to the game, if not, click here to be taken manually</a>. Except you can't, since the chat window doesn't exist yet.")
//checks if a client is afk
//3000 frames = 5 minutes
/client/proc/is_afk(duration=3000)
@@ -74,3 +74,8 @@
/datum/gear/skullbandana
display_name = "bandana, skull"
path = /obj/item/clothing/mask/bandana/skull
/datum/gear/mob_hunt_game
display_name = "Nano-Mob Hunter GO! Cartridge"
path = /obj/item/weapon/cartridge/mob_hunt_game
cost = 2
@@ -82,6 +82,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
//game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
var/exp
var/ooccolor = "#b82e00"
var/be_special = list() //Special role selection
var/UI_style = "Midnight"
@@ -593,6 +594,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if(jobban_isbanned(user, rank))
HTML += "<del>[rank]</del></td><td><b> \[BANNED]</b></td></tr>"
continue
var/available_in_playtime = job.available_in_playtime(user.client)
if(available_in_playtime)
HTML += "<del>[rank]</del></td><td> \[ " + get_exp_format(available_in_playtime) + " as " + job.get_exp_req_type() + "</td></tr>"
continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
HTML += "<del>[rank]</del></td><td> \[IN [(available_in_days)] DAYS]</td></tr>"
@@ -13,7 +13,8 @@
volume,
nanoui_fancy,
show_ghostitem_attack,
lastchangelog
lastchangelog,
exp
FROM [format_table_name("player")]
WHERE ckey='[C.ckey]'"}
)
@@ -40,6 +41,7 @@
nanoui_fancy = text2num(query.item[11])
show_ghostitem_attack = text2num(query.item[12])
lastchangelog = query.item[13]
exp = query.item[14]
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
@@ -54,6 +56,7 @@
nanoui_fancy = sanitize_integer(nanoui_fancy, 0, 1, initial(nanoui_fancy))
show_ghostitem_attack = sanitize_integer(show_ghostitem_attack, 0, 1, initial(show_ghostitem_attack))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
exp = sanitize_text(exp, initial(exp))
return 1
/datum/preferences/proc/save_preferences(client/C)
+1 -1
View File
@@ -154,7 +154,7 @@
var/invis_override = 0
var/emagged = 0
var/color_view = null//overrides client.color while worn
var/list/color_view = null//overrides client.color while worn
var/prescription = 0
var/prescription_upgradable = 0
strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets
+15 -6
View File
@@ -243,26 +243,35 @@
return 1
/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir()
var/list/difference = difflist(usr.client.color, color_view)
if(!noir_mode)
if(color_view && usr.client && !usr.client.color)
if(color_view && usr.client && (!usr.client.color || difference))
animate(usr.client, color = color_view, time = 10)
noir_mode = 1
else
if(usr.client && usr.client.color)
animate(usr.client, color = null, time = 10)
if(usr.client && usr.client.color && !difference)
animate(usr.client, color = initial(usr.client.color), time = 10)
noir_mode = 0
/obj/item/clothing/glasses/sunglasses/noir/equipped(mob/user, slot)
var/list/difference = difflist(user.client.color, color_view)
if(slot == slot_glasses)
if(noir_mode)
if(color_view && user.client && !user.client.color)
if(color_view && user.client && (!user.client.color || difference.len))
animate(user.client, color = color_view, time = 10)
else
if(user.client && user.client.color && !difference.len)
animate(user.client, color = initial(user.client.color), time = 10)
..(user, slot)
/obj/item/clothing/glasses/sunglasses/noir/dropped(mob/living/carbon/human/user)
var/list/difference = difflist(user.client.color, color_view)
if(istype(user) && user.glasses == src)
if(user.client && user.client.color)
animate(user.client, color = null, time = 10)
if(user.client && user.client.color && !difference.len)
animate(user.client, color = initial(user.client.color), time = 10)
..(user)
/obj/item/clothing/glasses/sunglasses/yeah
+1 -1
View File
@@ -514,7 +514,7 @@
ui.open()
ui.set_auto_update(1)
/obj/item/weapon/rig/ui_data(mob/user, datum/topic_state/state = inventory_state)
/obj/item/weapon/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state)
var/data[0]
data["primarysystem"] = null
+5
View File
@@ -313,6 +313,11 @@
icon_state = "eyepro"
item_state = "eyepro"
/obj/item/clothing/glasses/hud/security/sunglasses/fluff/voxxyhud //LP Spartan: Kaskreyarawkta
name = "VoxxyHUD"
desc = "A worn down visor from a vox raider's gear, crudely ripped from its helmet and linked into the security systems of the station. The word 'Kask' is scratched into the side."
icon_state = "hud-spartan"
//////////// Hats ////////////
/obj/item/clothing/head/fluff/heather_winceworth // Regens: Heather Winceworth
name= "Heather's rose"
+1 -1
View File
@@ -83,7 +83,7 @@
ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640)
ui.open()
/obj/machinery/computer/account_database/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["src"] = UID()
@@ -322,7 +322,7 @@
ui = new(user, src, ui_key, "smartfridge.tmpl", name, 400, 500)
ui.open()
/obj/machinery/smartfridge/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/smartfridge/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["contents"] = null
+9 -3
View File
@@ -634,14 +634,20 @@
chems[rid] = gene_chem.Copy()
continue
// Normally a length 2 list - but sometimes, it's length 1 - poisonberries, etc.
// This means the reagent does not scale with potency.
// Index 1 is the base value, index 2 is the potency per u of reagent
var/list/rgnt_list = chems[rid]
rgnt_list.len = max(gene_chem.len, rgnt_list.len)
for(var/i=1;i<=gene_chem.len;i++)
if(isnull(gene_chem[i])) gene_chem[i] = 0
if(chems[rid][i])
chems[rid][i] = max(1,round((gene_chem[i] + chems[rid][i])/2))
if(rgnt_list[i])
rgnt_list[i] = max(1,round((gene_chem[i] + rgnt_list[i])/2))
else
chems[rid][i] = gene_chem[i]
rgnt_list[i] = gene_chem[i]
var/list/new_gasses = gene.values["[TRAIT_EXUDE_GASSES]"]
if(islist(new_gasses))
+2 -2
View File
@@ -166,7 +166,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/botany/extractor/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/botany/extractor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/list/geneMasks[0]
@@ -318,7 +318,7 @@
ui.open()
ui.set_auto_update(1)
/obj/machinery/botany/editor/ui_data(mob/user, datum/topic_state/state = default_state)
/obj/machinery/botany/editor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["activity"] = active
+14 -1
View File
@@ -428,6 +428,19 @@
flags = RESTRICTED | HIVEMIND
follow = 1
/datum/language/terrorspider
name = "Spider Hivemind"
desc = "Terror spiders have a limited ability to commune over a psychic hivemind, similar to xenomorphs."
speech_verb = "chitters"
ask_verb = "chitters"
exclaim_verb = "chitters"
colour = "terrorspider"
key = "ts"
flags = RESTRICTED | HIVEMIND
follow = 1
/datum/language/ling
name = "Changeling"
desc = "Although they are normally wary and suspicious of each other, changelings can commune over a distance."
@@ -559,7 +572,7 @@
flags = RESTRICTED | HIVEMIND
drone_only = 1
follow = 1
/datum/language/drone
name = "Drone"
desc = "An encrypted stream of data converted to speech patterns."
+1 -2
View File
@@ -454,8 +454,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
totalMembers |= pipeline.other_atmosmch
for(var/obj/machinery/atmospherics/A in totalMembers)
if(!A.pipe_image)
A.pipe_image = image(A, A.loc, layer = 20, dir = A.dir) //the 20 puts it above Byond's darkness (not its opacity view)
A.pipe_image.plane = HUD_PLANE
A.update_pipe_image()
pipes_shown += A.pipe_image
client.images += A.pipe_image
@@ -47,10 +47,12 @@
to_chat(src, "Alert cancelled. Power has been restored without our assistance.")
aiRestorePowerRoutine = 0
clear_fullscreen("blind")
update_sight()
else if(aiRestorePowerRoutine == 3)
to_chat(src, "Alert cancelled. Power has been restored.")
aiRestorePowerRoutine = 0
clear_fullscreen("blind")
update_sight()
else
@@ -114,8 +114,6 @@
C.toff = 1
..()
/mob/living/silicon/pai/Login()
..()
// this function shows the information about being silenced as a pAI in the Status panel
/mob/living/silicon/pai/proc/show_silenced()
@@ -41,7 +41,12 @@ var/global/list/default_pai_software = list()
if(ui_key != "main")
var/datum/pai_software/S = software[ui_key]
if(S && !S.toggle)
S.on_ui_interact(src, ui, force_open)
ui = nanomanager.try_update_ui(user, src, S.id, ui, force_open)
if(!ui)
ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state)
ui.open()
if(S.autoupdate)
ui.set_auto_update(1)
else
if(ui)
ui.set_status(STATUS_CLOSE, 0)
@@ -53,9 +58,15 @@ var/global/list/default_pai_software = list()
ui.open()
ui.set_auto_update(1)
/mob/living/silicon/pai/ui_data(mob/user, datum/topic_state/state = self_state)
/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state)
var/data[0]
if(ui_key != "main")
var/datum/pai_software/S = software[ui_key]
if(S && !S.toggle)
return S.on_ui_data(user, state)
log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src)
return
// Software we have bought
var/bought_software[0]
// Software we have not bought
File diff suppressed because it is too large Load Diff
@@ -285,6 +285,7 @@
if(player.mob && player.mob.mind)
player.mob.mind.transfer_to(src)
player.mob.mind.assigned_role = "Drone"
lawupdate = 0
to_chat(src, "<b>Systems rebooted</b>. Loading base pattern maintenance protocol... <b>loaded</b>.")
@@ -0,0 +1,11 @@
#define SPINNING_WEB 1
#define LAYING_EGGS 2
#define MOVING_TO_TARGET 3
#define SPINNING_COCOON 4
#define TS_TIER_1 1
#define TS_TIER_2 2
#define TS_TIER_3 3
#define TS_TIER_4 4
#define TS_TIER_5 5
@@ -0,0 +1,127 @@
/datum/action/innate/terrorspider/web
name = "Web"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "stickyweb1"
/datum/action/innate/terrorspider/web/Activate()
var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner
user.Web()
/datum/action/innate/terrorspider/wrap
name = "Wrap"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "cocoon_large1"
/datum/action/innate/terrorspider/wrap/Activate()
var/mob/living/simple_animal/hostile/poison/terror_spider/user = owner
user.FindWrapTarget()
user.DoWrap()
// ---------- WEB
/mob/living/simple_animal/hostile/poison/terror_spider/proc/Web()
visible_message("<span class='notice'>[src] begins to secrete a sticky substance.</span>")
if(do_after(src, 40, target = loc))
var/obj/effect/spider/terrorweb/T = locate() in get_turf(src)
if(T)
to_chat(src, "<span class='danger'>There is already a web here.</span>")
else
new /obj/effect/spider/terrorweb(loc)
/obj/effect/spider/terrorweb
name = "terror web"
desc = "it's stringy and sticky"
icon = 'icons/effects/effects.dmi'
anchored = 1 // prevents people dragging it
density = 0 // prevents it blocking all movement
health = 20 // two welders, or one laser shot (15 for the normal spider webs)
icon_state = "stickyweb1"
/obj/effect/spider/terrorweb/New()
..()
if(prob(50))
icon_state = "stickyweb2"
/obj/effect/spider/terrorweb/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/poison/terror_spider))
return 1
if(isliving(mover))
if(prob(80))
to_chat(mover, "<span class='danger'>You get stuck in [src] for a moment.</span>")
var/mob/living/M = mover
M.Stun(4) // 8 seconds.
M.Weaken(4) // 8 seconds.
return 1
else
return 0
if(istype(mover, /obj/item/projectile))
return prob(20)
return ..()
// ---------- WRAP
/mob/living/simple_animal/hostile/poison/terror_spider/proc/FindWrapTarget()
if(!cocoon_target)
var/list/choices = list()
for(var/mob/living/L in oview(1,src))
if(Adjacent(L))
if(L.stat == DEAD)
choices += L
for(var/obj/O in oview(1,src))
if(Adjacent(O) && !O.anchored)
if(!istype(O, /obj/effect/spider/terrorweb) && !istype(O, /obj/effect/spider/cocoon))
choices += O
if(choices.len)
cocoon_target = input(src,"What do you wish to cocoon?") in null|choices
else
to_chat(src, "<span class='danger'>There is nothing nearby you can wrap.</span>")
/mob/living/simple_animal/hostile/poison/terror_spider/proc/DoWrap()
if(cocoon_target && busy != SPINNING_COCOON)
busy = SPINNING_COCOON
visible_message("<span class='notice'>[src] begins to secrete a sticky substance around [cocoon_target].</span>")
stop_automated_movement = 1
walk(src,0)
if(do_after(src, 40, target = cocoon_target.loc))
if(busy == SPINNING_COCOON)
if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src,cocoon_target) <= 1)
var/obj/effect/spider/cocoon/C = new(cocoon_target.loc)
var/large_cocoon = 0
C.pixel_x = cocoon_target.pixel_x
C.pixel_y = cocoon_target.pixel_y
for(var/obj/O in C.loc)
if(istype(O, /obj/item))
O.loc = C
else if(istype(O, /obj/machinery) || istype(O, /obj/structure))
O.loc = C
large_cocoon = 1
for(var/mob/living/L in C.loc)
if(istype(L, /mob/living/simple_animal/hostile/poison/terror_spider))
continue
if(L.stat != DEAD)
continue
if(iscarbon(L))
regen_points += regen_points_per_kill
fed++
large_cocoon = 1
last_cocoon_object = 0
L.loc = C
C.pixel_x = L.pixel_x
C.pixel_y = L.pixel_y
visible_message("<span class='danger'>[src] sticks a proboscis into [L] and sucks a viscous substance out.</span>")
break
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
cocoon_target = null
busy = 0
stop_automated_movement = 0
@@ -0,0 +1,38 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T2 BLACK TERROR ------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: assassin, poisoner, DoT expert
// -------------: AI: attacks to inject its venom, then retreats. Will inject its enemies multiple times then hang back to ensure they die.
// -------------: SPECIAL: venom that does more damage the more of it is in you
// -------------: TO FIGHT IT: if bitten once, retreat, get charcoal/etc treatment, and come back with a gun.
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/black
name = "Black Terror spider"
desc = "An ominous-looking spider, black as the darkest night, and with merciless eyes and a blood-red hourglass pattern on its back."
spider_role_summary = "Hit-and-run attacker with extremely venomous bite."
icon_state = "terror_widow"
icon_living = "terror_widow"
icon_dead = "terror_widow_dead"
maxHealth = 120 // same health as hunter spider, aka, pretty weak.. but its bite will kill you!
health = 120
melee_damage_lower = 5
melee_damage_upper = 10
move_to_delay = 5
stat_attack = 1 // ensures they will target people in crit, too!
spider_tier = TS_TIER_2
/mob/living/simple_animal/hostile/poison/terror_spider/black/spider_specialattack(mob/living/carbon/human/L, poisonable)
if(!poisonable)
return ..()
if(L.reagents.has_reagent("terror_black_toxin", 50))
return ..()
var/inject_target = pick("chest", "head")
if(L.stunned || L.can_inject(null, 0, inject_target, 0))
L.reagents.add_reagent("terror_black_toxin", 15) // inject our special poison
visible_message("<span class='danger'>[src] buries its long fangs deep into the [inject_target] of [target]!</span>")
else
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
L.attack_animal(src)
@@ -0,0 +1,33 @@
// Terror Spider, Black, Deadly Venom
/datum/reagent/terror_black_toxin
name = "Black Widow venom"
id = "terror_black_toxin"
description = "An incredibly toxic venom injected by the Black Widow spider."
color = "#CF3600"
metabolization_rate = 0.1
/datum/reagent/terror_black_toxin/on_mob_life(mob/living/M)
if(volume < 15)
// bitten once, die slowly. Easy to survive a single bite - just go to medbay.
// total damage: 2/tick, human health 150 until crit, = 75 ticks, = 150 seconds = 2.5 minutes to get to medbay.
M.adjustToxLoss(2) // same damage/tick as tabun cycle 0 to 60
else if(volume < 30)
// bitten twice, die more quickly, muscle cramps make movement difficult. Call medics immediately.
// total damage: 4, human health 150 until crit, = 37.5 ticks, = 75s = 1m15s until death
M.adjustToxLoss(4)
M.Confused(3)
M.EyeBlurry(3)
else if(volume < 45)
// bitten thrice, die very quickly, severe muscle cramps make movement very difficult. Even calling for help probably won't save you.
// total damage: 8, human health 150 until crit, = 18.75 ticks, = 37s until death
M.adjustToxLoss(8) // a bit worse than coiine
M.Confused(6)
M.EyeBlurry(6)
else
// bitten 4 or more times, whole body goes into shock/death
// total damage: 12, human health 150 until crit, = 12.5 ticks, = 25s until death
M.adjustToxLoss(12)
M.EyeBlurry(6)
M.Paralyse(5)
..()
@@ -0,0 +1,43 @@
/mob/living/simple_animal/hostile/poison/terror_spider/Topic(href, href_list)
if(href_list["activate"])
var/mob/dead/observer/ghost = usr
if(istype(ghost))
humanize_spider(ghost)
/mob/living/simple_animal/hostile/poison/terror_spider/attack_ghost(mob/user)
humanize_spider(user)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/humanize_spider(mob/user)
if(key)//Someone is in it
return
var/error_on_humanize = ""
var/humanize_prompt = "Take direct control of [src]?"
humanize_prompt += "Role: [spider_role_summary]"
if(user.ckey in ts_ckey_blacklist)
error_on_humanize = "You are not able to control any terror spider this round."
else if(!ai_playercontrol_allowingeneral)
error_on_humanize = "Terror spiders cannot currently be player-controlled."
else if(spider_awaymission)
error_on_humanize = "Terror spiders that are part of an away mission cannot be controlled by ghosts."
else if(!ai_playercontrol_allowtype)
error_on_humanize = "This specific type of terror spider is not player-controllable."
else if(stat == DEAD)
error_on_humanize = "Dead spiders are not player-controllable."
if(jobban_isbanned(user, "Syndicate") || jobban_isbanned(user, "alien"))
to_chat(user,"You are jobbanned from role of syndicate and/or alien lifeform.")
return
if(error_on_humanize == "")
var/spider_ask = alert(humanize_prompt, "Join as Terror Spider?", "Yes", "No")
if(spider_ask == "No" || !src || qdeleted(src))
return
else
to_chat(user, "Cannot inhabit spider: [error_on_humanize]")
return
if(key)
to_chat(user, "<span class='notice'>Someone else already took this spider.</span>")
return
key = user.key
for(var/mob/dead/observer/G in player_list)
G.show_message("<i>A ghost has taken control of <b>[src]</b>. ([ghost_follow_link(src, ghost=G)]).</i>")
@@ -0,0 +1,40 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T1 GRAY TERROR -------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: fast, weak terror spider
// -------------: SPECIAL: silences targets
// -------------: TO FIGHT IT: shoot it!
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/gray
name = "Gray Terror spider"
desc = "An ominous-looking gray spider, it seems jittery."
spider_role_summary = "Fast-moving but weak spider."
icon_state = "terror_gray"
icon_living = "terror_gray"
icon_dead = "terror_gray_dead"
maxHealth = 120 // same health as hunter spider, aka, pretty weak.. but silences its targets
health = 120
melee_damage_lower = 10
melee_damage_upper = 20
ventcrawler = 1
move_to_delay = 5 // normal speed
stat_attack = 1 // ensures they will target people in crit, too!
environment_smash = 1
/mob/living/simple_animal/hostile/poison/terror_spider/gray/spider_specialattack(mob/living/carbon/human/L, poisonable)
if(!poisonable)
..()
return
if(L.silent >= 10)
L.attack_animal(src)
else
var/inject_target = pick("chest","head")
if(L.stunned || L.can_inject(null,0,inject_target,0))
L.Silence(20) // instead of having a venom that only lasts seconds, we just add the silence directly.
visible_message("<span class='danger'>[src] buries grey fangs deep into the [inject_target] of [target]!</span>")
else
visible_message("<span class='danger'>[src] bites [target], but cannot inject venom into their [inject_target]!</span>")
L.attack_animal(src)
@@ -0,0 +1,35 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T3 PRINCE OF TERROR --------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: boss
// -------------: AI: no special ai
// -------------: SPECIAL: massive health
// -------------: TO FIGHT IT: a squad of at least 4 people with laser rifles.
// -------------: SPRITES FROM: Travelling Merchant, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=2766
/mob/living/simple_animal/hostile/poison/terror_spider/prince
name = "Prince of Terror spider"
desc = "An enormous, terrifying spider. It looks like it is judging everything it sees. Its hide seems armored, and it bears the scars of many battles."
spider_role_summary = "Boss-level terror spider. Lightning bruiser. Capable of taking on a squad by itself."
icon_state = "terror_allblack"
icon_living = "terror_allblack"
icon_dead = "terror_allblack_dead"
maxHealth = 400 // 20 laser shots.
health = 400
melee_damage_lower = 15
melee_damage_upper = 25
move_to_delay = 5
ventcrawler = 1
loot = list(/obj/item/clothing/accessory/medal)
spider_tier = TS_TIER_3
spider_opens_doors = 2
/mob/living/simple_animal/hostile/poison/terror_spider/prince/spider_specialattack(mob/living/carbon/human/L)
if(prob(15))
visible_message("<span class='danger'>[src] rams into [L], knocking them to the floor!</span>")
L.Weaken(5)
L.Stun(5)
else
..()
@@ -0,0 +1,60 @@
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: T1 RED TERROR --------------------------------
// --------------------------------------------------------------------------------
// -------------: ROLE: generic attack spider
// -------------: AI: uses very powerful fangs to wreck people in melee
// -------------: SPECIAL: the more you hurt it, the harder it bites you
// -------------: TO FIGHT IT: shoot it from range. Kite it.
// -------------: SPRITES FROM: FoS, http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
/mob/living/simple_animal/hostile/poison/terror_spider/red
name = "Red Terror spider"
desc = "An ominous-looking red spider, it has eight beady red eyes, and nasty, big, pointy fangs! It looks like it has a vicious streak a mile wide."
spider_role_summary = "High health, high damage, very slow, melee juggernaut"
icon_state = "terror_red"
icon_living = "terror_red"
icon_dead = "terror_red_dead"
maxHealth = 200
health = 200
melee_damage_lower = 15
melee_damage_upper = 20
move_to_delay = 20
spider_opens_doors = 2
var/enrage = 0
var/melee_damage_lower_rage0 = 15
var/melee_damage_upper_rage0 = 20
var/melee_damage_lower_rage1 = 15
var/melee_damage_upper_rage1 = 35
var/melee_damage_lower_rage2 = 35
var/melee_damage_upper_rage2 = 40
/mob/living/simple_animal/hostile/poison/terror_spider/red/AttackingTarget()
if(enrage == 0)
if(health < maxHealth)
enrage = 1
visible_message("<span class='danger'>[src] growls, flexing its fangs!</span>")
melee_damage_lower = melee_damage_lower_rage1
melee_damage_upper = melee_damage_upper_rage1
else if(enrage == 1)
if(health == maxHealth)
enrage = 0
visible_message("<span class='notice'>[src] retracts its fangs a little.</span>")
melee_damage_lower = melee_damage_lower_rage0
melee_damage_upper = melee_damage_upper_rage0
else if(health < (maxHealth/2))
enrage = 2
visible_message("<span class='danger'>[src] growls, spreading its fangs wide!</span>")
melee_damage_lower = melee_damage_lower_rage2
melee_damage_upper = melee_damage_upper_rage2
else if(enrage == 2)
if(health > (maxHealth/2))
enrage = 1
visible_message("<span class='notice'>[src] retracts its fangs a little.</span>")
melee_damage_lower = melee_damage_lower_rage0
melee_damage_upper = melee_damage_upper_rage0
..()
@@ -0,0 +1,329 @@
var/global/list/ts_ckey_blacklist = list()
var/global/ts_count_dead = 0
var/global/ts_count_alive_awaymission = 0
var/global/ts_count_alive_station = 0
var/global/ts_death_last = 0
var/global/ts_death_window = 9000 // 15 minutes
var/global/list/ts_spiderlist = list()
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: DEFAULTS ---------------------------------
// --------------------------------------------------------------------------------
// Because: http://tvtropes.org/pmwiki/pmwiki.php/Main/SpidersAreScary
/mob/living/simple_animal/hostile/poison/terror_spider
// Name / Description
name = "terror spider"
desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug."
// Icons
icon = 'icons/mob/terrorspider.dmi'
icon_state = "terror_red"
icon_living = "terror_red"
icon_dead = "terror_red_dead"
// Health
maxHealth = 120
health = 120
// Melee attacks
melee_damage_lower = 15
melee_damage_upper = 20
attacktext = "bites"
attack_sound = 'sound/weapons/bite.ogg'
poison_type = "" // we do not use that silly system.
// Movement
move_to_delay = 6
turns_per_move = 5
pass_flags = PASSTABLE
// Ventcrawling
ventcrawler = 1 // allows player ventcrawling
// Speech
speak_chance = 0 // quiet but deadly
speak_emote = list("hisses")
emote_hear = list("hisses")
// Languages are handled in terror_spider/New()
// Interaction keywords
response_help = "pets"
response_disarm = "gently pushes aside"
// regeneration settings - overridable by child classes
var/regen_points = 0 // number of regen points they have by default
var/regen_points_max = 100 // max number of points they can accumulate
var/regen_points_per_tick = 1 // gain one regen point per tick
var/regen_points_per_kill = 90 // gain extra regen points if you kill something
var/regen_points_per_hp = 3 // every X regen points = 1 health point you can regen
// desired: 20hp/minute unmolested, 40hp/min on food boost, assuming one tick every 2 seconds
// 90/kill means bonus 30hp/kill regenerated over the next 1-2 minutes
// Vision
idle_vision_range = 10
aggro_vision_range = 10
see_in_dark = 10
nightvision = 1
vision_type = new /datum/vision_override/nightvision/thermals/ling_augmented_eyesight
see_invisible = 5
// player control by ghosts
var/ai_playercontrol_allowingeneral = 1 // if 0, no spiders are player controllable. Default set in code, can be changed by queens.
var/ai_playercontrol_allowtype = 1 // if 0, this specific class of spider is not player-controllable. Default set in code for each class, cannot be changed.
var/spider_opens_doors = 1 // all spiders can open firedoors (they have no security). 1 = can open depowered doors. 2 = can open powered doors
faction = list("terrorspiders")
var/spider_awaymission = 0 // if 1, limits certain behavior in away missions
var/spider_role_summary = "UNDEFINED"
var/spider_placed = 0
// AI variables designed for use in procs
var/atom/cocoon_target // for queen and nurse
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent // nearby vent they are going to try to get to, and enter
var/obj/machinery/atmospherics/unary/vent_pump/exit_vent // remote vent they intend to come out of
var/obj/machinery/atmospherics/unary/vent_pump/nest_vent // home vent, usually used by queens
var/fed = 0
var/travelling_in_vent = 0
var/busy = 0 // leave this alone!
var/spider_tier = TS_TIER_1 // 1 for red,gray,green. 2 for purple,black,white, 3 for prince, mother. 4 for queen, 5 for empress.
var/hasdied = 0
var/attackstep = 0
var/attackcycles = 0
var/mylocation = null
var/chasecycles = 0
var/last_cocoon_object = 0 // leave this, changed by procs.
var/killcount = 0
// Breathing, Pressure & Fire
// - No breathing / cannot be suffocated (spiders can hold their breath, look it up)
// - No pressure damage either - they have effectively exoskeletons
// - HOWEVER they can be burned to death!
// - Normal SPACE spiders should probably be immune to SPACE too, but meh, we try to leave the base spiders alone.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
heat_damage_per_tick = 5 //amount of damage applied if animal's body temperature is higher than maxbodytemp
// DEBUG OPTIONS & COMMANDS
var/spider_debug = 0
var/datum/action/innate/terrorspider/web/web_action
var/datum/action/innate/terrorspider/wrap/wrap_action
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: SHARED ATTACK CODE -----------------------
// --------------------------------------------------------------------------------
/mob/living/simple_animal/hostile/poison/terror_spider/AttackingTarget()
if(istype(target, /mob/living/simple_animal/hostile/poison/terror_spider))
var/mob/living/simple_animal/hostile/poison/terror_spider/T = target
if(T.spider_tier > spider_tier)
visible_message("<span class='notice'>[src] bows in respect for the terrifying presence of [target].</span>")
else if(T.spider_tier == spider_tier)
visible_message("<span class='notice'>[src] nuzzles [target].</span>")
else if(T.spider_tier < spider_tier && spider_tier >= 4)
visible_message("<span class='notice'>[src] gives [target] a stern look.</span>")
else
visible_message("<span class='notice'>[src] harmlessly nuzzles [target].</span>")
T.CheckFaction()
CheckFaction()
else if(istype(target, /obj/effect/spider/cocoon))
to_chat(src, "Destroying our own cocoons would not help us.")
else if(istype(target, /obj/machinery/door/firedoor))
var/obj/machinery/door/firedoor/F = target
if(F.density)
if(F.blocked)
to_chat(src, "The fire door is welded shut.")
else
visible_message("<span class='danger'>\The [src] pries open the firedoor!</span>")
F.open()
else
to_chat(src, "Closing fire doors does not help.")
else if(istype(target, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/A = target
if(A.density)
try_open_airlock(A)
else if(isliving(target))
var/mob/living/G = target
if(issilicon(G))
G.attack_animal(src)
return
else if(G.reagents && (iscarbon(G)))
var/can_poison = 1
if(ishuman(G))
var/mob/living/carbon/human/H = G
if(!(H.species.reagent_tag & PROCESS_ORG) || (H.species.flags & NO_POISON))
can_poison = 0
spider_specialattack(G,can_poison)
else
G.attack_animal(src)
else
target.attack_animal(src)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_specialattack(mob/living/carbon/human/L, var/poisonable)
L.attack_animal(src)
// --------------------------------------------------------------------------------
// --------------------- TERROR SPIDERS: PROC OVERRIDES ---------------------------
// --------------------------------------------------------------------------------
/mob/living/simple_animal/hostile/poison/terror_spider/examine(mob/user)
..()
var/list/msgs = list()
if(stat == DEAD)
msgs += "<span class='notice'>It appears to be dead.</span>\n"
else
if(key)
msgs += "<span class='warning'>Its eyes regard you with a curious intelligence.</span>"
if(health > (maxHealth*0.95))
msgs += "<span class='notice'>It is in excellent health.</span>"
else if(health > (maxHealth*0.75))
msgs += "<span class='notice'>It has a few injuries.</span>"
else if(health > (maxHealth*0.55))
msgs += "<span class='warning'>It has many injuries.</span>"
else if(health > (maxHealth*0.25))
msgs += "<span class='warning'>It is barely clinging on to life!</span>"
else if(health < maxHealth && regen_points > regen_points_per_kill)
msgs += "<span class='notice'>It appears to be regenerating quickly.</span>"
if(killcount >= 1)
msgs += "<span class='warning'>It has blood dribbling from its mouth.</span>"
to_chat(usr,msgs.Join("<BR>"))
/mob/living/simple_animal/hostile/poison/terror_spider/New()
..()
ts_spiderlist += src
add_language("Spider Hivemind")
add_language("Galactic Common")
default_language = all_languages["Spider Hivemind"]
web_action = new()
web_action.Grant(src)
wrap_action = new()
wrap_action.Grant(src)
name += " ([rand(1, 1000)])"
msg_terrorspiders("[src] has grown in [get_area(src)].")
if(is_away_level(z))
spider_awaymission = 1
ts_count_alive_awaymission++
else
ts_count_alive_station++
// after 30 seconds, assuming nobody took control of it yet, offer it to ghosts.
addtimer(src, "CheckFaction", 150)
addtimer(src, "announcetoghosts", 300)
/mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts()
if(spider_awaymission)
return
if(stat == DEAD)
return
if(ckey)
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
notify_ghosts("[src] has appeared in [get_area(src)]. (already player-controlled)", source = src, alert_overlay = alert_overlay)
else if(ai_playercontrol_allowingeneral && ai_playercontrol_allowtype)
var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state)
notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "<a href=?src=[UID()];activate=1>(Click to control)</a>", source = src, alert_overlay = alert_overlay, attack_not_jump = 1)
/mob/living/simple_animal/hostile/poison/terror_spider/Destroy()
ts_spiderlist -= src
handle_dying()
return ..()
/mob/living/simple_animal/hostile/poison/terror_spider/Life()
..()
if(stat == DEAD)
if(prob(2))
// 2% chance every cycle to decompose
visible_message("<span class='notice'>\The dead body of the [src] decomposes!</span>")
gib()
else
if(regen_points < regen_points_max)
regen_points += regen_points_per_tick
if((bruteloss > 0) || (fireloss > 0))
if(regen_points > regen_points_per_hp)
if(bruteloss > 0)
adjustBruteLoss(-1)
regen_points -= regen_points_per_hp
else if(fireloss > 0)
adjustFireLoss(-1)
regen_points -= regen_points_per_hp
if(prob(5))
CheckFaction()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/handle_dying()
if(!hasdied)
hasdied = 1
ts_count_dead++
ts_death_last = world.time
if(spider_awaymission)
ts_count_alive_awaymission--
else
ts_count_alive_station--
/mob/living/simple_animal/hostile/poison/terror_spider/death(gibbed)
if(!gibbed)
msg_terrorspiders("[src] has died in [get_area(src)].")
handle_dying()
..()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/spider_special_action()
return
/mob/living/simple_animal/hostile/poison/terror_spider/Bump(atom/A)
if(istype(A, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/L = A
if(L.density)
try_open_airlock(L)
if(istype(A, /obj/machinery/door/firedoor))
var/obj/machinery/door/firedoor/F = A
if(F.density && !F.blocked)
F.open()
..()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext)
for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist)
if(T.stat != DEAD)
to_chat(T, "<span class='terrorspider'>TerrorSense: [msgtext]</span>")
/mob/living/simple_animal/hostile/poison/terror_spider/proc/CheckFaction()
if(faction.len != 1 || (!("terrorspiders" in faction)) || master_commander != null)
visible_message("<span class='danger'>[src] writhes in pain!</span>")
log_runtime(EXCEPTION("Terror spider created with incorrect faction list at: [atom_loc_line(src)]"))
death()
/mob/living/simple_animal/hostile/poison/terror_spider/proc/try_open_airlock(obj/machinery/door/airlock/D)
if(D.operating)
return
if(!D.density)
to_chat(src, "<span class='warning'>Closing doors does not help us.</span>")
else if(D.welded)
to_chat(src, "<span class='warning'>The door is welded shut.</span>")
else if(D.locked)
to_chat(src, "<span class='warning'>The door is bolted shut.</span>")
else if(D.allowed(src))
D.open(1)
else if(D.arePowerSystemsOn() && (spider_opens_doors != 2))
to_chat(src, "<span class='warning'>The door's motors resist your efforts to force it.</span>")
else if(!spider_opens_doors)
to_chat(src, "<span class='warning'>Your type of spider is not strong enough to force open doors.</span>")
else
visible_message("<span class='danger'>[src] pries open the door!</span>")
playsound(src.loc, "sparks", 100, 1)
D.open(1)
+3 -1
View File
@@ -244,8 +244,10 @@
if(!job.is_position_available()) return 0
if(jobban_isbanned(src,rank)) return 0
if(!is_job_whitelisted(src, rank)) return 0
if(!job.player_old_enough(src.client)) return 0
if(!job.player_old_enough(client)) return 0
if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0
if(job.available_in_playtime(client))
return 0
if(config.assistantlimit)
if(job.title == "Civilian")
+1 -1
View File
@@ -62,7 +62,7 @@
ui.open()
ui.set_auto_update(1)
/datum/nano_module/alarm_monitor/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
var/categories[0]
+1 -1
View File
@@ -39,7 +39,7 @@
ui.set_auto_update(1)
ui_ref = ui
/datum/nano_module/atmos_control/ui_data(mob/user, datum/topic_state/state = default_state)
/datum/nano_module/atmos_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state)
var/data[0]
data["alarms"] = air_alarm_repository.air_alarm_data(monitored_alarms)
return data

Some files were not shown because too many files have changed in this diff Show More