From 7d259d35c7abca96c157e1e272353aaf8d47be29 Mon Sep 17 00:00:00 2001 From: Tastyfish Date: Fri, 1 Apr 2016 19:32:46 -0400 Subject: [PATCH] Ports SNPCs --- code/__DEFINES/snpc.dm | 23 + code/__HELPERS/_string_lists.dm | 50 +- code/__HELPERS/game.dm | 4 +- code/_globalvars/lists/mobs.dm | 3 +- code/_onclick/hud/human.dm | 12 +- code/controllers/Processes/animal.dm | 37 - code/controllers/Processes/npcai.dm | 61 ++ code/controllers/Processes/npcpool.dm | 116 +++ code/game/machinery/kitchen/smartfridge.dm | 74 +- .../effects/decals/Cleanable/tracks.dm | 2 +- code/game/objects/items/weapons/kitchen.dm | 2 +- code/game/objects/structures/window.dm | 4 +- code/modules/admin/admin_verbs.dm | 35 +- code/modules/admin/player_panel.dm | 37 +- code/modules/events/ion_storm.dm | 40 +- code/modules/food/recipes_oven.dm | 4 +- code/modules/hydroponics/grown.dm | 6 +- code/modules/mob/living/carbon/carbon.dm | 24 +- .../mob/living/carbon/human/examine.dm | 4 +- .../carbon/human/interactive/functions.dm | 874 ++++++++++++++++ .../carbon/human/interactive/interactive.dm | 955 ++++++++++++++++++ .../carbon/human/interactive/prefabs.dm | 24 + .../carbon/human/interactive/thisisdumb.dm | 107 ++ .../mob/living/simple_animal/parrot.dm | 10 +- code/modules/projectiles/gun.dm | 2 +- code/modules/projectiles/targeting.dm | 4 +- .../reagent_containers/food/snacks.dm | 29 +- paradise.dme | 8 +- strings/ion_laws.json | 950 +++++++++++++++++ strings/ion_laws.txt | 17 - strings/npc_chatter.json | 137 +++ 31 files changed, 3447 insertions(+), 208 deletions(-) create mode 100644 code/__DEFINES/snpc.dm delete mode 100644 code/controllers/Processes/animal.dm create mode 100644 code/controllers/Processes/npcai.dm create mode 100644 code/controllers/Processes/npcpool.dm create mode 100644 code/modules/mob/living/carbon/human/interactive/functions.dm create mode 100644 code/modules/mob/living/carbon/human/interactive/interactive.dm create mode 100644 code/modules/mob/living/carbon/human/interactive/prefabs.dm create mode 100644 code/modules/mob/living/carbon/human/interactive/thisisdumb.dm create mode 100644 strings/ion_laws.json delete mode 100644 strings/ion_laws.txt create mode 100644 strings/npc_chatter.json diff --git a/code/__DEFINES/snpc.dm b/code/__DEFINES/snpc.dm new file mode 100644 index 00000000000..8d4768005cd --- /dev/null +++ b/code/__DEFINES/snpc.dm @@ -0,0 +1,23 @@ +#define SNPC_MAX_RANGE_FIND 32 +#define SNPC_MIN_RANGE_FIND 16 +#define SNPC_FUZZY_CHANCE_HIGH 85 +#define SNPC_FUZZY_CHANCE_LOW 50 +#define SNPC_CHANCE_TALK 15 + +#define SNPC_BRUTE 1 +#define SNPC_STEALTH 2 +#define SNPC_MARTYR 3 +#define SNPC_PSYCHO 4 + +#define SNPC_INTERACTING 2 +#define SNPC_TRAVEL 4 +#define SNPC_FIGHTING 8 +#define SNPC_SPECIAL 16 + +#define TRAIT_ROBUST 2 +#define TRAIT_UNROBUST 4 +#define TRAIT_SMART 8 +#define TRAIT_DUMB 16 +#define TRAIT_MEAN 32 +#define TRAIT_FRIENDLY 64 +#define TRAIT_THIEVING 128 \ No newline at end of file diff --git a/code/__HELPERS/_string_lists.dm b/code/__HELPERS/_string_lists.dm index f9b5e179aa1..36aa2f9b6fa 100644 --- a/code/__HELPERS/_string_lists.dm +++ b/code/__HELPERS/_string_lists.dm @@ -1,27 +1,41 @@ #define pick_list(FILE, KEY) (pick(strings(FILE, KEY))) +#define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY)) +#define json_load(FILE) (json_decode(file2text(FILE))) var/global/list/string_cache +var/global/list/string_filename_current_key + + +/proc/strings_replacement(filename, key) + load_strings_file(filename) + + if((filename in string_cache) && (key in string_cache[filename])) + var/response = pick(string_cache[filename][key]) + var/regex/r = regex("@pick\\((\\D+?)\\)", "g") + response = r.Replace(response, /proc/strings_subkey_lookup) + return response + else + CRASH("strings list not found: strings/[filename], index=[key]") /proc/strings(filename as text, key as text) - var/list/fileList - if(!string_cache) - string_cache = new - if(!(filename in string_cache)) - if(fexists("strings/[filename]")) - string_cache[filename] = list() - var/list/stringsList = list() - fileList = file2list("strings/[filename]") - for(var/s in fileList) - stringsList = splittext(s, "@=") - if(stringsList.len != 2) - CRASH("Invalid string list in strings/[filename]") - if(findtext(stringsList[2], "@,")) - string_cache[filename][stringsList[1]] = splittext(stringsList[2], "@,") - else - string_cache[filename][stringsList[1]] = stringsList[2] // Its a single string! - else - CRASH("file not found: strings/[filename]") + load_strings_file(filename) if((filename in string_cache) && (key in string_cache[filename])) return string_cache[filename][key] else CRASH("strings list not found: strings/[filename], index=[key]") + +/proc/strings_subkey_lookup(match, group1) + return pick_list(string_filename_current_key, group1) + +/proc/load_strings_file(filename) + string_filename_current_key = filename + if(filename in string_cache) + return //no work to do + + if(!string_cache) + string_cache = new + + if(fexists("strings/[filename]")) + string_cache[filename] = json_load("strings/[filename]") + else + CRASH("file not found: strings/[filename]") diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index b386fce364f..e6253c1c170 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -223,8 +223,8 @@ // Try to find all the players who can hear the message - for(var/i = 1; i <= player_list.len; i++) - var/mob/M = player_list[i] + for(var/A in player_list + hear_radio_list) + var/mob/M = A if(M) var/turf/ear = get_turf(M) if(ear) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index f778386bb88..b6dc1c58b8f 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -23,9 +23,10 @@ var/global/list/living_mob_list = list() //List of all alive mobs, including c var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player var/global/list/respawnable_list = list() //List of all mobs, dead or in mindless creatures that still be respawned. var/global/list/simple_animal_list = list() //List of all simple animals, including clientless +var/global/list/snpc_list = list() //List of all snpc's, including clientless var/global/list/med_hud_users = list() var/global/list/sec_hud_users = list() var/global/list/antag_hud_users = list() var/global/list/surgeries_list = list() - //items that ask to be called every cycle +var/global/list/hear_radio_list = list() //Mobs that hear the radio even if there's no client diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 2bf587c1cc1..8dabef48583 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -452,17 +452,13 @@ if (mymob.client.gun_mode) // If in aim mode, correct the sprite mymob.gun_setting_icon.dir = 2 + mymob.client.screen = list() - mymob.client.screen = list() - - mymob.client.screen += list( mymob.throw_icon, mymob.zone_sel, mymob.oxygen, mymob.pressure, mymob.toxin, mymob.bodytemp, mymob.internals, mymob.fire, mymob.healths, mymob.healthdoll, mymob.nutrition_icon, mymob.pullin, mymob.gun_setting_icon, lingchemdisplay, lingstingdisplay) //, mymob.hands, mymob.rest, mymob.sleep) //, mymob.mach ) - mymob.client.screen += src.adding + src.hotkeybuttons - mymob.client.screen += mymob.client.void + mymob.client.screen += list( mymob.throw_icon, mymob.zone_sel, mymob.oxygen, mymob.pressure, mymob.toxin, mymob.bodytemp, mymob.internals, mymob.fire, mymob.healths, mymob.healthdoll, mymob.nutrition_icon, mymob.pullin, mymob.gun_setting_icon, lingchemdisplay, lingstingdisplay) //, mymob.hands, mymob.rest, mymob.sleep) //, mymob.mach ) + mymob.client.screen += src.adding + src.hotkeybuttons + mymob.client.screen += mymob.client.void inventory_shown = 0 - return - - /mob/living/carbon/human/verb/toggle_hotkey_verbs() set category = "OOC" set name = "Toggle Hotkey Buttons" diff --git a/code/controllers/Processes/animal.dm b/code/controllers/Processes/animal.dm deleted file mode 100644 index e3840974d2c..00000000000 --- a/code/controllers/Processes/animal.dm +++ /dev/null @@ -1,37 +0,0 @@ -var/global/datum/controller/process/animal/animal_master - -/datum/controller/process/animal - var/current_cycle - -/datum/controller/process/animal/setup() - name = "animal" - schedule_interval = 20 // every 2 seconds - start_delay = 16 - log_startup_progress("Simple animal ticker starting up.") - if(animal_master) - qdel(animal_master) //only one mob master - animal_master = src - -/datum/controller/process/animal/started() - ..() - if(!simple_animal_list) - simple_animal_list = list() - -/datum/controller/process/animal/statProcess() - ..() - stat(null, "[simple_animal_list.len] simple animals") - -/datum/controller/process/animal/doWork() - for(last_object in simple_animal_list) - var/mob/living/simple_animal/M = last_object - if(istype(M) && isnull(M.gcDestroyed)) - if(!M.client && M.stat == CONSCIOUS) - try - M.process_ai() - catch(var/exception/e) - catchException(e, M) - SCHECK - else - catchBadType(M) - simple_animal_list -= M - current_cycle++ \ No newline at end of file diff --git a/code/controllers/Processes/npcai.dm b/code/controllers/Processes/npcai.dm new file mode 100644 index 00000000000..5039795abab --- /dev/null +++ b/code/controllers/Processes/npcai.dm @@ -0,0 +1,61 @@ +var/global/datum/controller/process/npcai/npcai_master + +/datum/controller/process/npcai + var/current_cycle + var/saved_voice = 0 + +/datum/controller/process/npcai/setup() + name = "npc ai" + schedule_interval = 20 // every 2 seconds + start_delay = 16 + log_startup_progress("NPC ticker starting up.") + if(npcai_master) + qdel(npcai_master) //only one mob master + npcai_master = src + +/datum/controller/process/npcai/started() + ..() + if(!simple_animal_list) + simple_animal_list = list() + if(!snpc_list) + snpc_list = list() + +/datum/controller/process/npcai/statProcess() + ..() + stat(null, "[simple_animal_list.len] simple animals") + stat(null, "[snpc_list.len] SNPC's") + +/datum/controller/process/npcai/doWork() + for(last_object in simple_animal_list) + var/mob/living/simple_animal/M = last_object + if(istype(M) && isnull(M.gcDestroyed)) + if(!M.client && M.stat == CONSCIOUS) + try + M.process_ai() + catch(var/exception/e) + catchException(e, M) + SCHECK + else + catchBadType(M) + simple_animal_list -= M + + if(ticker.current_state == GAME_STATE_FINISHED && !saved_voice) + var/mob/living/carbon/human/interactive/M = pick(snpc_list) + if(M) + M.saveVoice() + saved_voice = 1 + + for(last_object in snpc_list) + var/mob/living/carbon/human/interactive/M = last_object + if(istype(M) && isnull(M.gcDestroyed)) + try + if(!M.alternateProcessing || M.forceProcess) + M.doProcess() + catch(var/exception/e) + catchException(e, M) + SCHECK + else + catchBadType(M) + snpc_list -= M + + current_cycle++ \ No newline at end of file diff --git a/code/controllers/Processes/npcpool.dm b/code/controllers/Processes/npcpool.dm new file mode 100644 index 00000000000..266754dd36b --- /dev/null +++ b/code/controllers/Processes/npcpool.dm @@ -0,0 +1,116 @@ +var/global/datum/controller/process/npcpool/npc_master + +/datum/controller/process/npcpool + var/list/canBeUsed = list() + var/list/canBeUsed_non = list() + var/list/needsDelegate = list() + var/list/needsAssistant = list() + var/list/needsHelp_non = list() + var/list/botPool_l = list() //list of all npcs using the pool + var/list/botPool_l_non = list() //list of all non SNPC mobs using the pool + +/datum/controller/process/npcpool/setup() + name = "npc pool" + schedule_interval = 100 + start_delay = 17 + log_startup_progress("NPC pool ticker starting up.") + if(npc_master) + qdel(npc_master) //only one mob master + npc_master = src + +/datum/controller/process/npcpool/copyStateFrom(var/datum/controller/process/npcpool/target) + canBeUsed = target.canBeUsed + canBeUsed_non = target.canBeUsed_non + needsDelegate = target.needsDelegate + needsAssistant = target.needsAssistant + needsHelp_non = target.needsHelp_non + botPool_l = target.botPool_l + botPool_l_non = target.botPool_l_non + +/datum/controller/process/npcpool/proc/insertBot(toInsert) + if(istype(toInsert, /mob/living/carbon/human/interactive)) + botPool_l |= toInsert + +/datum/controller/process/npcpool/statProcess() + ..() + stat(null, "T [botPool_l.len + botPool_l_non.len] | D [needsDelegate.len] | A [needsAssistant.len + needsHelp_non.len] | U [canBeUsed.len + canBeUsed_non.len]") + +/datum/controller/process/npcpool/doWork() + //bot delegation and coordination systems + //General checklist/Tasks for delegating a task or coordinating it (for SNPCs) + // 1. Bot proximity to task target: if too far, delegate, if close, coordinate + // 2. Bot Health/status: check health with bots in local area, if their health is higher, delegate task to them, else coordinate + // 3. Process delegation: if a bot (or bots) has been delegated, assign them to the task. + // 4. Process coordination: if a bot(or bots) has been asked to coordinate, assign them to help. + // 5. Do all assignments: goes through the delegated/coordianted bots and assigns the right variables/tasks to them. + + listclearnulls(canBeUsed) + + //SNPC handling + for(var/mob/living/carbon/human/interactive/check in botPool_l) + if(!check) + botPool_l -= check + continue + var/checkInRange = view(SNPC_MAX_RANGE_FIND, check) + if(!(locate(check.TARGET) in checkInRange)) + needsDelegate |= check + + else if(check.IsDeadOrIncap(0)) + needsDelegate |= check + + else if(check.doing & SNPC_FIGHTING) + needsAssistant |= check + + else + canBeUsed |= check + SCHECK + + if(needsDelegate.len) + needsDelegate -= pick(needsDelegate) // cheapo way to make sure stuff doesn't pingpong around in the pool forever. delegation runs seperately to each loop so it will work much smoother + + for(var/mob/living/carbon/human/interactive/check in needsDelegate) + if(!check) + needsDelegate -= check + continue + if(canBeUsed.len) + var/mob/living/carbon/human/interactive/candidate = pick(canBeUsed) + var/facCount = 0 + var/helpProb = 0 + for(var/C in check.faction) + for(var/D in candidate.faction) + if(D == C) + helpProb = min(100, helpProb + 25) + facCount++ + if(facCount == 1 && helpProb > 0) + helpProb = 100 + if(prob(helpProb)) + if(candidate.takeDelegate(check)) + needsDelegate -= check + canBeUsed -= candidate + candidate.change_eye_color(255, 0, 0) + SCHECK + + if(needsAssistant.len) + needsAssistant -= pick(needsAssistant) + + for(var/mob/living/carbon/human/interactive/check in needsAssistant) + if(!check) + needsAssistant -= check + continue + if(canBeUsed.len) + var/mob/living/carbon/human/interactive/candidate = pick(canBeUsed) + var/facCount = 0 + var/helpProb = 0 + for(var/C in check.faction) + for(var/D in candidate.faction) + if(D == C) + helpProb = min(100, helpProb + 25) + facCount++ + if(facCount == 1 && helpProb > 0) + helpProb = 100 + if(prob(helpProb)) + if(candidate.takeDelegate(check, 0)) + needsAssistant -= check + canBeUsed -= candidate + candidate.change_eye_color(255, 255, 0) + SCHECK \ No newline at end of file diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index 7978aee0c0a..d1dda0bc558 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -218,41 +218,18 @@ to_chat(user, "\The [src] is unpowered and useless.") return - if(accept_check(O)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 1 - else - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to you!") - return + if(load(O, user)) + user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - O.forceMove(src) - if(item_quants[O.name]) - item_quants[O.name]++ - else - item_quants[O.name] = 1 - user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - - nanomanager.update_uis(src) + nanomanager.update_uis(src) else if(istype(O, /obj/item/weapon/storage/bag)) var/obj/item/weapon/storage/bag/P = O var/plants_loaded = 0 for(var/obj/G in P.contents) - if(accept_check(G)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 1 - else - P.remove_from_storage(G,src) - if(item_quants[G.name]) - item_quants[G.name]++ - else - item_quants[G.name] = 1 - plants_loaded++ + if(load(G, user)) + plants_loaded++ if(plants_loaded) - user.visible_message("[user] loads \the [src] with \the [P].", "You load \the [src] with \the [P].") if(P.contents.len > 0) to_chat(user, "Some items are refused.") @@ -263,6 +240,34 @@ to_chat(user, "\The [src] smartly refuses [O].") return 1 +/obj/machinery/smartfridge/proc/load(obj/I, mob/user) + if(accept_check(I)) + if(contents.len >= max_n_of_items) + to_chat(user, "\The [src] is full.") + return 0 + else + if(istype(I.loc, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/S = I.loc + S.remove_from_storage(I, src) + else if(istype(I.loc, /mob)) + var/mob/M = I.loc + if(M.get_active_hand() == I) + if(!M.drop_item()) + to_chat(user, "\The [I] is stuck to you!") + return 0 + else + M.unEquip(I) + I.forceMove(src) + else + I.forceMove(src) + + if(item_quants[I.name]) + item_quants[I.name]++ + else + item_quants[I.name] = 1 + return 1 + return 0 + /obj/machinery/smartfridge/attack_ai(mob/user as mob) return 0 @@ -287,17 +292,8 @@ var/obj/item/weapon/storage/box/pillbottles/P = over_object var/items_loaded = 0 for(var/obj/G in P.contents) - if(accept_check(G)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 1 - else - P.remove_from_storage(G,src) - if(item_quants[G.name]) - item_quants[G.name]++ - else - item_quants[G.name] = 1 - items_loaded++ + if(load(G, user)) + items_loaded++ if(items_loaded) user.visible_message( \ "[user] empties \the [P] into \the [src].", \ diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index 48e865c78a3..ad404ce28b3 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -78,7 +78,7 @@ var/global/list/image/fluidtrack_cache=list() // Process 4 bits for(var/bi=0;bi<4;bi++) - b=1<[user] smashes through [src]!") destroy() - else if (usr.a_intent == I_HARM) + else if (user.a_intent == I_HARM) user.changeNext_move(CLICK_CD_MELEE) playsound(get_turf(src), 'sound/effects/glassknock.ogg', 80, 1) usr.visible_message("\red [usr.name] bangs against the [src.name]!", \ @@ -136,7 +136,7 @@ var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8f else user.changeNext_move(CLICK_CD_MELEE) playsound(src.loc, 'sound/effects/glassknock.ogg', 80, 1) - usr.visible_message("[usr.name] knocks on the [src.name].", \ + user.visible_message("[usr.name] knocks on the [src.name].", \ "You knock on the [src.name].", \ "You hear a knocking sound.") return diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index a2d62b29c8a..a3d5d609a63 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -70,7 +70,8 @@ var/list/admin_verbs_admin = list( /client/proc/secrets, /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ /client/proc/change_human_appearance_self, /* Allows the human-based mob itself change its basic appearance */ - /client/proc/debug_variables + /client/proc/debug_variables, + /client/proc/show_snpc_verbs ) var/list/admin_verbs_ban = list( /client/proc/unban_panel, @@ -185,11 +186,17 @@ var/list/admin_verbs_mentor = list( /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_pm_by_key_panel /*admin-pm list by key*/ ) -var/list/admin_verbs_proccall = list ( +var/list/admin_verbs_proccall = list( /client/proc/callproc, /client/proc/callproc_datum, /client/proc/SDQL2_query ) +var/list/admin_verbs_snpc = list( + /client/proc/resetSNPC, + /client/proc/toggleSNPC, + /client/proc/customiseSNPC, + /client/proc/hide_snpc_verbs +) /client/proc/add_admin_verbs() if(holder) @@ -246,6 +253,8 @@ var/list/admin_verbs_proccall = list ( admin_verbs_proccall, admin_verbs_show_debug_verbs, /client/proc/readmin, + admin_verbs_snpc, + /client/proc/hide_snpc_verbs ) /client/proc/hide_verbs() @@ -922,3 +931,25 @@ var/list/admin_verbs_proccall = list ( log_admin("[key_name(usr)] told everyone to man up and deal with it.") message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.") + +/client/proc/show_snpc_verbs() + set name = "Show SNPC Verbs" + set category = "Admin" + + if(!holder) + return + + verbs += admin_verbs_snpc + verbs -= /client/proc/show_snpc_verbs + to_chat(src, "SNPC verbs on.") + +/client/proc/hide_snpc_verbs() + set name = "Hide SNPC Verbs" + set category = "Admin" + + if(!holder) + return + + verbs -= admin_verbs_snpc + verbs += /client/proc/show_snpc_verbs + to_chat(src, "SNPC verbs off.") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index b192079b5b8..30277f3a4ab 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -394,6 +394,16 @@ +/proc/check_antagonists_line(mob/M, caption = "", close = 1) + var/logout_status + if(istype(M, /mob/living/carbon/human/interactive)) + logout_status = " (snpc)" + else + logout_status = M.client ? "" : " (logged out)" + + return {"[M.real_name][caption][logout_status][M.stat == 2 ? " (DEAD)" : ""] + PM[close ? "" : ""]"} + /datum/admins/proc/check_antagonists() if(!check_rights(R_ADMIN)) return if (ticker && ticker.current_state >= GAME_STATE_PLAYING) @@ -417,8 +427,7 @@ for(var/datum/mind/N in ticker.mode.syndicates) var/mob/M = N.current if(M) - dat += "[M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]" - dat += "PM" + dat += check_antagonists_line(M) else dat += "Nuclear Operative not found!" dat += "
" @@ -443,19 +452,16 @@ if(!M) dat += "" else - dat += "" - dat += "" + dat += check_antagonists_line(M, "(leader)") for(var/datum/mind/N in ticker.mode.revolutionaries) var/mob/M = N.current if(M) - dat += "" - dat += "" + dat += check_antagonists_line(M) dat += "
Nuclear Disk(s)
Head Revolutionary not found!
[M.real_name] (Leader)[M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
[M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM
" for(var/datum/mind/N in ticker.mode.get_living_heads()) var/mob/M = N.current if(M) - dat += "" - dat += "" + dat += check_antagonists_line(M) var/turf/mob_loc = get_turf(M) dat += "" else @@ -464,8 +470,7 @@ for(var/datum/mind/N in ticker.mode.get_extra_living_heads()) var/mob/M = N.current if(M) - dat += "" - dat += "" + dat += check_antagonists_line(M) var/turf/mob_loc = get_turf(M) dat += "" else @@ -548,17 +553,7 @@ if (!istype(M)) return "" - var/txt = {" - - - - "} + var/txt = check_antagonists_line(M, close = 0) if (show_objectives) txt += {" diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index d153294c790..bba7949a600 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -41,49 +41,49 @@ return ionMessage //Threats are generally bad things, silly or otherwise. Plural. - var/ionthreats = pick_list("ion_laws.txt", "ionthreats") + var/ionthreats = pick_list("ion_laws.json", "ionthreats") //Objects are anything that can be found on the station or elsewhere, plural. - var/ionobjects = pick_list("ion_laws.txt", "ionobjects") + var/ionobjects = pick_list("ion_laws.json", "ionobjects") //Crew is any specific job. Specific crewmembers aren't used because of capitalization //issues. There are two crew listings for laws that require two different crew members //and I can't figure out how to do it better. - var/ioncrew1 = pick_list("ion_laws.txt", "ioncrew") - var/ioncrew2 = pick_list("ion_laws.txt", "ioncrew") + var/ioncrew1 = pick_list("ion_laws.json", "ioncrew") + var/ioncrew2 = pick_list("ion_laws.json", "ioncrew") //Adjectives are adjectives. Duh. Half should only appear sometimes. Make sure both //lists are identical! Also, half needs a space at the end for nicer blank calls. - var/ionadjectives = pick_list("ion_laws.txt", "ionadjectives") - var/ionadjectiveshalf = pick("", 400;(pick_list("ion_laws.txt", "ionadjectives") + " ")) + var/ionadjectives = pick_list("ion_laws.json", "ionadjectives") + var/ionadjectiveshalf = pick("", 400;(pick_list("ion_laws.json", "ionadjectives") + " ")) //Verbs are verbs - var/ionverb = pick_list("ion_laws.txt", "ionverb") + var/ionverb = pick_list("ion_laws.json", "ionverb") //Number base and number modifier are combined. Basehalf and mod are unused currently. //Half should only appear sometimes. Make sure both lists are identical! Also, half //needs a space at the end to make it look nice and neat when it calls a blank. - var/ionnumberbase = pick_list("ion_laws.txt", "ionnumberbase") - //var/ionnumbermod = pick_list("ion_laws.txt", "ionnumbermod") - var/ionnumbermodhalf = pick(900;"",(pick_list("ion_laws.txt", "ionnumbermod") + " ")) + var/ionnumberbase = pick_list("ion_laws.json", "ionnumberbase") + //var/ionnumbermod = pick_list("ion_laws.json", "ionnumbermod") + var/ionnumbermodhalf = pick(900;"",(pick_list("ion_laws.json", "ionnumbermod") + " ")) //Areas are specific places, on the station or otherwise. - var/ionarea = pick_list("ion_laws.txt", "ionarea") + var/ionarea = pick_list("ion_laws.json", "ionarea") //Thinksof is a bit weird, but generally means what X feels towards Y. - var/ionthinksof = pick_list("ion_laws.txt", "ionthinksof") + var/ionthinksof = pick_list("ion_laws.json", "ionthinksof") //Musts are funny things the AI or crew has to do. - var/ionmust = pick_list("ion_laws.txt", "ionmust") + var/ionmust = pick_list("ion_laws.json", "ionmust") //Require are basically all dumb internet memes. - var/ionrequire = pick_list("ion_laws.txt", "ionrequire") + var/ionrequire = pick_list("ion_laws.json", "ionrequire") //Things are NOT objects; instead, they're specific things that either harm humans or //must be done to not harm humans. Make sure they're plural and "not" can be tacked //onto the front of them. - var/ionthings = pick_list("ion_laws.txt", "ionthings") + var/ionthings = pick_list("ion_laws.json", "ionthings") //Allergies should be broad and appear somewhere on the station for maximum fun. Severity //is how bad the allergy is. - var/ionallergy = pick_list("ion_laws.txt", "ionallergy") - var/ionallergysev = pick_list("ion_laws.txt", "ionallergysev") + var/ionallergy = pick_list("ion_laws.json", "ionallergy") + var/ionallergysev = pick_list("ion_laws.json", "ionallergysev") //Species, for when the AI has to commit genocide. Plural. - var/ionspecies = pick_list("ion_laws.txt", "ionspecies") + var/ionspecies = pick_list("ion_laws.json", "ionspecies") //Abstract concepts for the AI to decide on it's own definition of. - var/ionabstract = pick_list("ion_laws.txt", "ionabstract") + var/ionabstract = pick_list("ion_laws.json", "ionabstract") //Foods. Drinks aren't included due to grammar; if you want to add drinks, make a new set //of possible laws for best effect. Unless you want the crew having to drink hamburgers. - var/ionfood = pick_list("ion_laws.txt", "ionfood") + var/ionfood = pick_list("ion_laws.json", "ionfood") var/message = "" diff --git a/code/modules/food/recipes_oven.dm b/code/modules/food/recipes_oven.dm index e919a1bbf4a..105bc4d0282 100644 --- a/code/modules/food/recipes_oven.dm +++ b/code/modules/food/recipes_oven.dm @@ -187,8 +187,8 @@ . = ..() if (.) var/obj/item/weapon/paper/paper = locate() in container - if (!paper.info) - return 0 + if (!paper || !paper.info) + return -1 return . /datum/recipe/oven/pizzamargherita diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index a025e32f872..72af3665463 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -494,11 +494,11 @@ seed.do_sting(H,src,pick("r_hand","l_hand")) -/obj/item/weapon/reagent_containers/food/snacks/grown/On_Consume() +/obj/item/weapon/reagent_containers/food/snacks/grown/On_Consume(mob/M, mob/user) if(seed && seed.get_trait(TRAIT_BATTERY_RECHARGE)) if(!reagents.total_volume) var/batteries_recharged = 0 - for(var/obj/item/weapon/stock_parts/cell/C in usr.GetAllContents()) + for(var/obj/item/weapon/stock_parts/cell/C in user.GetAllContents()) var/newcharge = (potency*0.01)*C.maxcharge if(C.charge < newcharge) C.charge = newcharge @@ -507,5 +507,5 @@ O.update_icon() //update power meters and such batteries_recharged = 1 if(batteries_recharged) - to_chat(usr, "Battery has recovered.") + to_chat(user, "Battery has recovered.") ..() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index ac4bb584528..a7ea84c0722 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -521,12 +521,12 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, /mob/living/carbon/throw_item(atom/target) throw_mode_off() - if(usr.stat || !target) + if(stat || !target) return if(target.type == /obj/screen) return - var/atom/movable/item = src.get_active_hand() + var/atom/movable/item = get_active_hand() if(!item || (item.flags & NODROP)) return @@ -542,14 +542,14 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, var/start_T_descriptor = "tile at [start_T.x], [start_T.y], [start_T.z] in area [get_area(start_T)]" var/end_T_descriptor = "tile at [end_T.x], [end_T.y], [end_T.z] in area [get_area(end_T)]" - M.attack_log += text("\[[time_stamp()]\] Has been thrown by [key_name(usr)] from [start_T_descriptor] with the target [end_T_descriptor]") - usr.attack_log += text("\[[time_stamp()]\] Has thrown [key_name(M)] from [start_T_descriptor] with the target [end_T_descriptor]") - msg_admin_attack("[key_name_admin(usr)] has thrown [key_name_admin(M)] from [start_T_descriptor] with the target [end_T_descriptor]") + M.attack_log += text("\[[time_stamp()]\] Has been thrown by [key_name(src)] from [start_T_descriptor] with the target [end_T_descriptor]") + attack_log += text("\[[time_stamp()]\] Has thrown [key_name(M)] from [start_T_descriptor] with the target [end_T_descriptor]") + msg_admin_attack("[key_name_admin(src)] has thrown [key_name_admin(M)] from [start_T_descriptor] with the target [end_T_descriptor]") - if(!iscarbon(usr)) + if(!iscarbon(src)) M.LAssailant = null else - M.LAssailant = usr + M.LAssailant = src if(!item) return //Grab processing has a chance of returning null @@ -557,17 +557,17 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, unEquip(item) update_icons() - if (istype(usr, /mob/living/carbon)) //Check if a carbon mob is throwing. Modify/remove this line as required. - item.loc = usr.loc - if(src.client) - src.client.screen -= item + if (istype(src, /mob/living/carbon)) //Check if a carbon mob is throwing. Modify/remove this line as required. + item.loc = loc + if(client) + client.screen -= item if(istype(item, /obj/item)) item:dropped(src) // let it know it's been dropped //actually throw it! if (item) item.layer = initial(item.layer) - src.visible_message("\red [src] has thrown [item].") + visible_message("\red [src] has thrown [item].") newtonian_move(get_dir(target, src)) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 1eb906d5ec1..0b15e284eef 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -254,7 +254,9 @@ msg += "[t_He] [t_has] a stupid expression on [t_his] face.\n" if(species.show_ssd && (!species.has_organ["brain"] || get_int_organ(/obj/item/organ/internal/brain)) && stat != DEAD) - if(!key) + if(istype(src, /mob/living/carbon/human/interactive)) + msg += "[t_He] appears to be some sort of sick automaton: [t_his] eyes are glazed over and [t_his] mouth is slightly agape.\n" + else if(!key) msg += "[t_He] [t_is] fast asleep. It doesn't look like they are waking up anytime soon.\n" else if(!client) msg += "[t_He] [t_has] suddenly fallen asleep.\n" diff --git a/code/modules/mob/living/carbon/human/interactive/functions.dm b/code/modules/mob/living/carbon/human/interactive/functions.dm new file mode 100644 index 00000000000..c61bb1eb21c --- /dev/null +++ b/code/modules/mob/living/carbon/human/interactive/functions.dm @@ -0,0 +1,874 @@ +/mob/living/carbon/human/interactive/proc/chatter(obj) + var/verbs_use = pick_list(speak_file, "verbs_use") + var/verbs_touch = pick_list(speak_file, "verbs_touch") + var/verbs_move = pick_list(speak_file, "verbs_move") + var/nouns_insult = pick_list(speak_file, "nouns_insult") + var/nouns_generic = pick_list(speak_file, "nouns_generic") + var/nouns_objects = pick_list(speak_file, "nouns_objects") + var/nouns_body = pick_list(speak_file, "nouns_body") + var/adjective_insult = pick_list(speak_file, "adjective_insult") + var/adjective_objects = pick_list(speak_file, "adjective_objects") + var/adjective_generic = pick_list(speak_file, "adjective_generic") + var/curse_words = pick_list(speak_file, "curse_words") + + var/chatmsg = "" + + if(prob(10)) // 10% chance to broadcast it over the radio + chatmsg = ";" + + if(prob(chattyness) || knownStrings.len < 10) // say a generic phrase, otherwise draw from our strings. + if(doing & SNPC_INTERACTING) + if(prob(chattyness)) + chatmsg += pick("This [nouns_objects] is a little [adjective_objects].", + "Well [verbs_use] my [nouns_body], this [nouns_insult] is pretty [adjective_insult].", + "[capitalize(curse_words)], what am I meant to do with this [adjective_insult] [nouns_objects].") + else if(doing & SNPC_TRAVEL) + if(prob(chattyness)) + chatmsg += pick("Oh [curse_words], [verbs_move]!", + "Time to get my [adjective_generic] [adjective_insult] [nouns_body] elsewhere.", + "I wonder if there is anything to [verbs_use] and [verbs_touch] somewhere else..") + else if(doing & SNPC_FIGHTING) + if(prob(chattyness)) + chatmsg += pick("I'm going to [verbs_use] you, you [adjective_insult] [nouns_insult]!", + "Rend and [verbs_touch], Rend and [verbs_use]!", + "You [nouns_insult], I'm going to [verbs_use] you right in the [nouns_body]. JUST YOU WAIT!") + if(prob(chattyness/2)) + chatmsg = ";" + var/what = pick(1,2,3,4,5) + switch(what) + if(1) + chatmsg += "Well [curse_words], this is a [adjective_generic] situation." + if(2) + chatmsg += "Oh [curse_words], that [nouns_insult] was one hell of an [adjective_insult] [nouns_body]." + if(3) + chatmsg += "I want to [verbs_use] that [nouns_insult] when I find them." + if(4) + chatmsg += "[pick("Innocent","Guilty","Traitorous","Honk")] until proven [adjective_generic]!" + if(5) + var/toSay = "" + for(var/i = 0; i < 5; i++) + curse_words = pick_list(speak_file, "curse_words") + toSay += "[curse_words] " + chatmsg += "Hey [nouns_generic], why dont you go [copytext(toSay, 1, length(toSay))], you [nouns_insult]!" + else if(prob(chattyness)) + chatmsg += pick(knownStrings) + if(prob(25)) // cut out some phrases now and then to make sure we're fresh and new + knownStrings -= pick(chatmsg) + + if(chatmsg != ";" && chatmsg != "") + say(chatmsg) + +/mob/living/carbon/human/interactive/proc/traitor(obj) + + if(traitorType == SNPC_PSYCHO && nearby.len) + traitorTarget = pick(nearby) + + if(prob(traitorScale)) + if(!Adjacent(traitorTarget) && !(traitorType == SNPC_PSYCHO)) + tryWalk(get_turf(traitorTarget)) + else + switch(traitorType) + if(SNPC_BRUTE) + retal = 1 + retal_target = traitorTarget + if(SNPC_STEALTH) + if(istype(traitorTarget,/mob)) // it's inside something, lets kick their shit in + var/mob/M = traitorTarget + if(!M.stat) + retal = 1 + retal_target = traitorTarget + else + var/obj/item/I = traitorTarget + I.loc = get_turf(traitorTarget) // pull it outta them + else + take_to_slot(traitorTarget) + if(SNPC_MARTYR) + custom_emote(1, "[src]'s chest opens up, revealing a large mass of explosives and tangled wires!") + if(inactivity_period <= 0) + inactivity_period = 9999 // technically infinite + if(do_after(src, 60, target=traitorTarget)) + custom_emote(1, "A fire bursts from [src]'s eyes, igniting white hot and consuming their body in a flaming explosion!") + explosion(src, 6, 6, 6) + else + inactivity_period = 0 + custom_emote(1, "[src]'s chest closes, hiding their insides.") + if(SNPC_PSYCHO) + var/choice = pick(typesof(/obj/item/weapon/grenade/chem_grenade) - /obj/item/weapon/grenade/chem_grenade) + + new choice(src) + + retal = 1 + retal_target = traitorTarget + +/mob/living/carbon/human/interactive/proc/botany(obj) + if(shouldModulePass()) + return + + if(enforceHome()) + return + + var/list/allContents = getAllContents() + + var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/internalBeaker = locate(/obj/item/weapon/reagent_containers/glass/beaker/bluespace) in allContents + var/obj/item/weapon/storage/bag/plants/internalBag = locate(/obj/item/weapon/storage/bag/plants) in allContents + + if(!internalBag) + internalBag = new/obj/item/weapon/storage/bag/plants(src) + if(!internalBeaker) + internalBeaker = new/obj/item/weapon/reagent_containers/glass/beaker/bluespace(src) + internalBeaker.name = "Grow-U-All Super Spray" + + if(internalBeaker && internalBag) + var/obj/machinery/portable_atmospherics/hydroponics/HP + + //consider the appropriate target + var/list/considered = list() + + for(var/obj/machinery/portable_atmospherics/hydroponics/tester in view(12,src)) + considered[tester] = 1 + + if(!tester.seed) + considered[tester] += 50 + if(tester.weedlevel > 0) + considered[tester] += 5 + if(tester.pestlevel > 0) + considered[tester] += 5 + if(tester.nutrilevel < tester.maxnutri) + considered[tester] += 15 + if(tester.waterlevel < tester.maxwater) + considered[tester] += 15 + if(tester.harvest || tester.dead) + considered[tester] += 100 + considered[tester] = max(1, considered[tester] - get_dist(src,tester)) + + var/index = 0 + for(var/A in considered) + ++index + if(considered[A] > considered[HP] || !HP) + HP = considered[index] + + if(HP) + TARGET = HP + if(!Adjacent(HP)) + tryWalk(get_turf(HP)) + else + if(HP.harvest || HP.dead) + HP.attack_hand(src) + else if(!HP.seed) + var/obj/item/seeds/SEED = new /obj/item/seeds/random(src) + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!") + HP.attackby(SEED, src) + else + var/change = 0 + if(HP.weedlevel > 0) + change = 1 + if(!internalBeaker.reagents.has_reagent("atrazine", 10)) + internalBeaker.reagents.add_reagent("atrazine", 10) + if(HP.pestlevel > 0) + change = 1 + if(!internalBeaker.reagents.has_reagent("diethylamine", 10)) + internalBeaker.reagents.add_reagent("diethylamine", 10) + if(HP.nutrilevel < HP.maxnutri) + change = 1 + if(!internalBeaker.reagents.has_reagent("eznutrient", 15)) + internalBeaker.reagents.add_reagent("eznutrient", 15) + if(!internalBeaker.reagents.has_reagent("diethylamine", 15)) + internalBeaker.reagents.add_reagent("diethylamine", 15) + if(HP.waterlevel < HP.maxwater) + change = 1 + if(!internalBeaker.reagents.has_reagent("holywater", 50)) + internalBeaker.reagents.add_reagent("holywater", 50) + if(change) + HP.attackby(internalBeaker,src) + + var/obj/item/weapon/reagent_containers/food/snacks/grown/GF = locate(/obj/item/weapon/reagent_containers/food/snacks/grown) in view(12,src) + if(GF) + if(!Adjacent(GF)) + tryWalk(get_turf(GF)) + else + GF.attackby(internalBag,src) + + if(internalBag.contents.len) + var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in range(12, src) + if(!Adjacent(SF)) + tryWalk(get_turf(SF)) + else + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], upending the [internalBag]'s contents all over the [SF]!") + //smartfridges call updateUsrDialog when you call attackby, so we're going to have to cheese-magic-space this + for(var/obj/toLoad in internalBag.contents) + if(contents.len >= SF.max_n_of_items) + break + if(SF.accept_check(toLoad)) + SF.load(toLoad, src) + else + qdel(toLoad) // destroy everything we dont need + +/mob/living/carbon/human/interactive/proc/bartend(obj) + if(shouldModulePass()) + return + + if(enforceHome()) + return + + var/list/rangeCheck = oview(6,src) + var/obj/structure/table/RT + + var/mob/living/carbon/human/serveTarget + + for(var/mob/living/carbon/human/H in rangeCheck) + if(!locate(/obj/item/weapon/reagent_containers/food/drinks) in orange(1, H)) + serveTarget = H + + + if(serveTarget) + RT = locate(/obj/structure/table) in orange(1, serveTarget) + + if(RT && serveTarget) + if(!Adjacent(RT)) + tryWalk(get_turf(RT)) + else + var/drinkChoice = pick(typesof(/obj/item/weapon/reagent_containers/food/drinks) - /obj/item/weapon/reagent_containers/food/drinks) + if(drinkChoice) + var/obj/item/weapon/reagent_containers/food/drinks/D = new drinkChoice(get_turf(src)) + RT.attackby(D,src) + say("[pick("Something to wet your whistle!","Down the hatch, a tasty beverage!","One drink, coming right up!","Tasty liquid for your oral intake!","Enjoy!")]") + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")], serving up a [D]!") + +/mob/living/carbon/human/interactive/proc/shitcurity(obj) + var/list/allContents = getAllContents() + + if(retal && TARGET && Adjacent(TARGET)) + var/obj/item/weapon/restraints/R = locate() in allContents + if(R) + R.attack(TARGET, src) // go go bluespace restraint launcher! + +/mob/living/carbon/human/interactive/proc/clowning(obj) + if(shouldModulePass()) + return + var/list/allContents = getAllContents() + var/list/rangeCheck = oview(12, src) + + var/mob/living/carbon/human/clownTarget + var/list/clownPriority = list() + + var/obj/item/weapon/reagent_containers/spray/S + if(!locate(/obj/item/weapon/reagent_containers/spray) in allContents) + new/obj/item/weapon/reagent_containers/spray(src) + else + S = locate(/obj/item/weapon/reagent_containers/spray) in allContents + + for(var/mob/living/carbon/human/C in rangeCheck) + var/pranksNearby = 100 + for(var/turf/simulated/T in orange(1, C)) + for(var/obj/item/A in T) + if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/bananapeel)) + pranksNearby-- + if(T.wet) + pranksNearby -= 10 + clownPriority[C] = pranksNearby + + for(var/A in clownPriority) + if(!clownTarget) + clownTarget = A + else + if(clownPriority[A] > clownPriority[clownTarget]) + clownTarget = clownPriority[A] + + if(clownTarget) + if(!Adjacent(clownTarget)) + tryWalk(clownTarget) + else + var/hasPranked = 0 + for(var/A in allContents) + if(prob(smartness/2) && !hasPranked) + if(istype(A,/obj/item/weapon/soap)) + npcDrop(A) + hasPranked = 1 + if(istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana)) + var/obj/item/weapon/reagent_containers/food/snacks/B = A + B.attack(src, src) + if(istype(A,/obj/item/weapon/bananapeel)) + npcDrop(A) + hasPranked = 1 + if(!hasPranked) + if(S.reagents.total_volume <= 5) + S.reagents.add_reagent("water", 25) + S.afterattack(get_turf(pick(orange(1, clownTarget))),src) + + +/mob/living/carbon/human/interactive/proc/healpeople(obj) + var/shouldTryHeal = 0 + var/obj/item/stack/medical/M + + var/list/allContents = getAllContents() + + for(var/A in allContents) + if(istype(A,/obj/item/stack/medical)) + shouldTryHeal = 1 + M = A + + var/obj/item/weapon/reagent_containers/hypospray/HPS + + if(!locate(/obj/item/weapon/reagent_containers/hypospray) in allContents) + new/obj/item/weapon/reagent_containers/hypospray(src) + else + HPS = locate(/obj/item/weapon/reagent_containers/hypospray) in allContents + if(!shouldTryHeal) + shouldTryHeal = 2 // we have no stackables to use, lets use our internal hypospray instead + + if(shouldTryHeal == 1) + for(var/mob/living/carbon/human/C in nearby) + if(istype(C,/mob/living/carbon/human)) //I haven't the foggiest clue why this is turning up non-carbons but sure here whatever + if(C.health <= 75) + if(get_dist(src,C) <= 2) + src.say("Wait, [C], let me heal you!") + M.attack(C,src) + sleep(25) + else + tryWalk(get_turf(C)) + else if(shouldTryHeal == 2) + if(HPS) + if(HPS.reagents.total_volume <= 0) + HPS.reagents.add_reagent("omnizine",30) + for(var/mob/living/carbon/human/C in nearby) + if(istype(C,/mob/living/carbon/human)) + if(C.health <= 75 && C.reagents.get_reagent_amount("omnizine") <= 0) // make sure they wont be overdosing + if(get_dist(src,C) <= 2) + src.say("Wait, [C], let me heal you!") + HPS.attack(C,src) + sleep(25) + else + tryWalk(get_turf(C)) + + +/mob/living/carbon/human/interactive/proc/dojanitor(obj) + if(shouldModulePass()) + return + var/list/allContents = getAllContents() + //now with bluespace magic! + var/obj/item/weapon/reagent_containers/spray/SP = locate() in allContents + if(!SP) + SP = new /obj/item/weapon/reagent_containers/spray(src) + var/obj/item/weapon/soap/SO = locate() in allContents + if(!SO) + SO = new /obj/item/weapon/soap(src) + + if(SP.reagents.total_volume <= 5) + SP.reagents.add_reagent("cleaner", 25) // bluespess water delivery for AI + + var/obj/effect/decal/cleanable/TC + TC = locate(/obj/effect/decal/cleanable) in view(6, src) + + if(TC) + if(!Adjacent(TC)) + tryWalk(TC) + else + var/turf/T = get_turf(TC) + if(T && !T.density) + SP.afterattack(TC, src, 1) + else + SO.afterattack(TC, src, 1) + qdel(TC) + +// START COOKING MODULE +/mob/living/carbon/human/interactive/proc/refundrecipe(list/ingredients) + for(var/obj/I in ingredients) + I.loc = contents + +// Ingredients may be in the rangeCheck or inside the chef's inventory. +// As they are checked off, they are moved to a seperate list as to not be counted twice +// If the recipe fails (eg somebody else took an ingredient before it was picked up) the ingredients are dumped back to inventory +// That way, the chef has a better chance of making a recipe next ptick, if he has more stuff on him +/mob/living/carbon/human/interactive/proc/makerecipe(datum/recipe/R, allContents, rangeCheck) + set background = 1 + + var/list/ingredients[0] + + for(var/P in R.items) + var/obj/item/I = locate(P) in allContents + if(I) + ingredients += I + I.forceMove(null) + continue + + I = locate(P) in rangeCheck + TARGET = I + if(I && !Adjacent(I)) + tryWalk(get_turf(I)) + sleep(get_dist(src, I)) + if(!I || !(I in rangeCheck)) + refundrecipe(ingredients) + return 0 + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], picking up [I].") + ingredients += I + I.forceMove(null) + + for(var/P in R.fruit) + for(var/i = 1 to R.fruit[P]) + var/obj/item/I = locate(P) in allContents + if(I) + ingredients += I + I.forceMove(null) + continue + + I = locate(P) in rangeCheck + TARGET = I + if(I && !Adjacent(I)) + tryWalk(get_turf(I)) + sleep(get_dist(src, I)) + if(!I || !(I in rangeCheck)) + refundrecipe(ingredients) + return 0 + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], picking up [I].") + ingredients += I + I.forceMove(null) + + // cheaply cook the ingredients into result + sleep(R.time) + for(var/obj/I in ingredients) + qdel(I) + ingredients.Cut() + var/result = new R.result(get_turf(src)) + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] and, with a bang, \a [result] is cooked!") + return 1 + +/mob/living/carbon/human/interactive/proc/souschef(obj) + if(shouldModulePass() || enforceHome() || prob(SNPC_FUZZY_CHANCE_LOW)) + return + + if(doing & SNPC_SPECIAL) + return + doing |= SNPC_SPECIAL + + var/static/list/customableTypes = list(/obj/item/weapon/reagent_containers/food/snacks/customizable,/obj/item/weapon/reagent_containers/food/snacks/breadslice,/obj/item/weapon/reagent_containers/food/snacks/bun,/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,/obj/item/weapon/reagent_containers/food/snacks/boiledspagetti,/obj/item/trash/plate,/obj/item/trash/bowl) + + var/static/list/rawtypes = list(/obj/item/weapon/reagent_containers/food/snacks/grown, /obj/item/weapon/reagent_containers/food/snacks/rawcutlet, /obj/item/weapon/reagent_containers/food/snacks/rawmeatball, /obj/item/weapon/reagent_containers/food/snacks/rawsticks, /obj/item/weapon/reagent_containers/food/snacks/salmonmeat, /obj/item/weapon/reagent_containers/food/snacks/carpmeat, /obj/item/weapon/reagent_containers/food/snacks/catfishmeat, /obj/item/weapon/reagent_containers/food/snacks/spagetti, /obj/item/weapon/reagent_containers/food/snacks/dough_ball, /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, /obj/item/weapon/reagent_containers/food/snacks/doughslice, /obj/item/weapon/reagent_containers/food/snacks/meat, /obj/item/weapon/reagent_containers/food/snacks/boiledrice, /obj/item/weapon/reagent_containers/food/snacks/cheesewedge) + + try + var/list/allContents = getAllContents() + var/list/rangeCheck = view(6, src) + + //Bluespace in some inbuilt tools + var/obj/item/weapon/kitchen/rollingpin/RP = locate() in allContents + if(!RP) + RP = new /obj/item/weapon/kitchen/rollingpin(src) + + var/obj/item/weapon/kitchen/knife/KK = locate() in allContents + if(!KK) + KK = new /obj/item/weapon/kitchen/knife(src) + + var/global/list/available_recipes + if(!available_recipes) + available_recipes = list() + for (var/type in subtypesof(/datum/recipe)) + var/datum/recipe/recipe = new type + if(recipe.result) // Ignore recipe subtypes that lack a result + available_recipes += recipe + else + qdel(recipe) + + var/foundCookable = 0 + + //Make some basic custom food + var/foundCustom + for(var/customType in customableTypes) + var/A = locate(customType) in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/customizable/I = A + if(A && (!istype(I) || I.ingredients.len <= 3)) + foundCustom = A // this will eventually wittle down to 0 + break + + var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in rangeCheck + if(SF) + if(SF.contents.len > 0) + if(!Adjacent(SF)) + tryWalk(get_turf(SF)) + else + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")], grabbing various foodstuffs from [SF] and sticking them in it's mouth!") + for(var/obj/item/A in SF.contents) + if(prob(smartness/2)) + var/count = SF.item_quants[A.name] + if(count) + SF.item_quants[A.name] = count - 1 + A.forceMove(src) + + if(foundCustom) + var/obj/item/weapon/reagent_containers/food/snacks/FC = foundCustom + for(var/obj/item/weapon/reagent_containers/food/snacks/toMake in allContents) + if(prob(smartness)) + if(FC.reagents) + if(toMake.reagents) + var/raw = 0 + for(var/type in rawtypes) + if(istype(toMake, type)) + raw = 1 + break + if(!raw) + npcDrop(toMake) + FC.attackby(toMake, src) + allContents -= toMake + break + else + qdel(FC) // this food is usless, toss it + + // Process tool-based ingredients + var/obj/item/weapon/reagent_containers/food/snacks/dough/D = locate() in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough/FD = locate() in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/rawcutlet/RC = locate() in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesewheel/CW = locate() in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/grown/potato/PO = locate() in rangeCheck + var/obj/item/weapon/reagent_containers/food/snacks/meat/ME = locate() in rangeCheck + + if(D) + TARGET = D + if(prob(50)) + tryWalk(get_turf(D)) + sleep(get_dist(src, D)) + D.attackby(RP, src) + foundCookable = 1 + else if(FD) + TARGET = FD + if(prob(50)) + tryWalk(get_turf(FD)) + sleep(get_dist(src, FD)) + FD.attackby(KK, src) + foundCookable = 1 + else if(RC) + TARGET = RC + if(prob(50)) + tryWalk(get_turf(RC)) + sleep(get_dist(src, RC)) + RC.attackby(KK, src) + foundCookable = 1 + else if(CW) + TARGET = CW + if(prob(50)) + tryWalk(get_turf(CW)) + sleep(get_dist(src, CW)) + CW.attackby(KK, src) + foundCookable = 1 + else if(PO) + TARGET = PO + if(prob(50)) + tryWalk(get_turf(PO)) + sleep(get_dist(src, PO)) + PO.attackby(KK, src) + foundCookable = 1 + else if(ME) + TARGET = ME + if(prob(50)) + tryWalk(get_turf(ME)) + sleep(get_dist(src, ME)) + ME.attackby(KK, src) + foundCookable = 1 + + // refresh + allContents = getAllContents() + rangeCheck = view(6, src) + + // Find most complex recipe we can cook + var/ingredientZone = rangeCheck + allContents + var/highest_count = 0 + var/datum/recipe/winner = null + for(var/datum/recipe/R in available_recipes) + if(R.check_items(ingredientZone) >= 0 && R.check_fruit(ingredientZone) >= 0) + var/count = (R.items ? R.items.len : 0) + (R.fruit ? R.fruit.len : 0) + if(count > highest_count) + highest_count = count + winner = R + + // make winner + if(winner) + if(makerecipe(winner, allContents, rangeCheck)) + foundCookable = 1 + + // refresh + allContents = getAllContents() + rangeCheck = view(6, src) + + // dont display our ingredients + var/list/finishedList = list() + for(var/obj/item/weapon/reagent_containers/food/snacks/toDisplay in allContents) + var/raw = 0 + for(var/type in rawtypes) + if(istype(toDisplay, type)) + raw = 1 + break + if(!raw) + finishedList += toDisplay + + for(var/obj/item/weapon/reagent_containers/food/snacks/toGrab in rangeCheck) + if(!(locate(/obj/structure/table/reinforced) in get_turf(toGrab))) //it's not being displayed + var/raw = 0 + for(var/type in rawtypes) + if(istype(toGrab, type)) + raw = 1 + break + if(!raw) + foundCookable = 1 + if(!Adjacent(toGrab)) + tryWalk(toGrab) + else + finishedList += toGrab + toGrab.forceMove(src) + + if(finishedList.len) + var/obj/structure/table/reinforced/RT + + for(var/obj/structure/table/reinforced/toCheck in rangeCheck) + var/counted = 0 + for(var/obj/item/weapon/reagent_containers/food/snacks/S in get_turf(toCheck)) + ++counted + if(counted < 12) // make sure theres not too much food here + RT = toCheck + break + + if(RT) + foundCookable = 1 + if(!Adjacent(RT)) + tryWalk(get_turf(RT)) + else + for(var/obj/item/weapon/reagent_containers/food/snacks/toPlop in finishedList) + RT.attackby(toPlop,src) + + if(!foundCookable) + var/chosenType = pick(rawtypes) + + var/obj/item/weapon/reagent_containers/food/snacks/newSnack = new chosenType(get_turf(src)) + TARGET = newSnack + newSnack.reagents.remove_any((newSnack.reagents.total_volume/2)-1) + newSnack.name = "Synthetic [newSnack.name]" + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] as they vomit [newSnack] from their mouth!") + catch(var/exception/e) + log_to_dd("Cooking error: [e] in [e.file]:[e.line]") + doing &= ~SNPC_SPECIAL +// END COOKING MODULE + +/mob/living/carbon/human/interactive/proc/combat(obj) + set background = 1 + enforce_hands() + if(canmove) + if((graytide || (TRAITS & TRAIT_MEAN)) || retal) + interest += targetInterestShift + a_intent = "harm" + zone_sel.selecting = pick("chest","r_leg","l_leg","r_arm","l_arm","head") + doing |= SNPC_FIGHTING + if(retal) + TARGET = retal_target + else + var/mob/living/M = locate(/mob/living) in oview(7, src) + if(!M) + doing = doing & ~SNPC_FIGHTING + else if(M != src && !compareFaction(M.faction)) + TARGET = M + + //no infighting + if(retal) + if(retal_target) + if(compareFaction(retal_target.faction)) + retal = 0 + retal_target = null + TARGET = null + doing = 0 + + //ensure we're using the best object possible + + var/obj/item/weapon/best + var/foundFav = 0 + var/list/allContents = getAllContents() + for(var/test in allContents) + for(var/a in favoured_types) + if(ispath(test,a) && !(doing & SNPC_FIGHTING)) // if we're not in combat and we find our favourite things, use them (for people like janitor and doctors) + best = test + foundFav = 1 + return + if(!foundFav) + if(istype(test,/obj/item/weapon)) + var/obj/item/weapon/R = test + if(R.force > 2) // make sure we don't equip any non-weaponlike items, ie bags and stuff + if(!best) + best = R + else + if(best.force < R.force) + best = R + if(istype(R, /obj/item/weapon/gun)) + var/obj/item/weapon/gun/G = R + if(G.can_fire()) + best = R + break // gun with ammo? screw the rest + if(best && best != main_hand) + take_to_slot(best,1) + + if(TARGET && (doing & SNPC_FIGHTING)) // this is a redundancy check + var/mob/living/M = TARGET + if(istype(M,/mob/living)) + if(M.health > 1 && (M in oview(src, 6))) + //THROWING OBJECTS + for(var/A in allContents) + if(istype(A,/obj/item/weapon/gun)) // guns are for shooting, not throwing. + continue + if(prob(robustness)) + if(istype(A,/obj/item/weapon)) + var/obj/item/weapon/W = A + if(W.throwforce > 19) // Only throw worthwile stuff, no more lobbing wrenches at wenches + npcDrop(W, 1) + throw_item(TARGET) + if(istype(A,/obj/item/weapon/grenade)) // Allahu ackbar! ALLAHU ACKBARR!! + var/obj/item/weapon/grenade/G = A + G.attack_self(src) + if(prob(smartness)) + npcDrop(G, 1) + sleep(15) + throw_item(TARGET) + + if(!main_hand && other_hand) + swap_hands() + if(main_hand) + if(main_hand.force != 0) + if(istype(main_hand,/obj/item/weapon/gun)) + var/obj/item/weapon/gun/G = main_hand + if(G.special_check(src)) + var/shouldFire = 1 + if(istype(main_hand, /obj/item/weapon/gun/energy)) + var/obj/item/weapon/gun/energy/P = main_hand + var/stunning = 0 + if(ispath(text2path(P.projectile_type), /obj/item/projectile/energy/electrode)) + stunning = 1 + var/mob/stunCheck = TARGET + if(stunning && stunCheck.stunned) + shouldFire = 0 + if(shouldFire) + if(!G.process_chambered()) + G.click_empty(src) + npcDrop(G, 1) + else + G.Fire(TARGET, src) + else + if(get_dist(src,TARGET) > 6) + if(!walk2derpless(TARGET)) + timeout++ + else + var/obj/item/weapon/W = main_hand + W.attack(TARGET, src) + else + npcDrop(G, 1) + else + if(targetRange(TARGET) > 2) + tryWalk(TARGET) + else + if(Adjacent(TARGET)) + a_intent = pick("disarm", "harm") + M.attack_hand(src) + timeout++ + else if(timeout >= 10 || !(targetRange(M) > 14)) + doing = doing & ~SNPC_FIGHTING + timeout = 0 + TARGET = null + retal = 0 + retal_target = null + else + timeout++ + +/mob/living/carbon/human/interactive/proc/past_verb(word) + if(copytext(word, length(word)) == "e") + return "[word]d" + else + return "[word]ed" + +/mob/living/carbon/human/interactive/proc/paperwork_sentence() + var/verbs_use = pick_list(speak_file, "verbs_use") + var/verbs_touch = pick_list(speak_file, "verbs_touch") + var/verbs_move = pick_list(speak_file, "verbs_move") + var/nouns_insult = pick_list(speak_file, "nouns_insult") + var/nouns_generic = pick_list(speak_file, "nouns_generic") + var/nouns_objects = pick_list(speak_file, "nouns_objects") + var/nouns_body = pick_list(speak_file, "nouns_body") + var/adjective_insult = pick_list(speak_file, "adjective_insult") + var/adjective_objects = pick_list(speak_file, "adjective_objects") + var/adjective_generic = pick_list(speak_file, "adjective_generic") + var/curse_words = pick_list(speak_file, "curse_words") + var/result = pick("fired", "promoted", "demoted", "terminated", "incinerated", "sent to CentCom", "borged") + + return pick( + "The [adjective_insult] [pick(command_positions)] [past_verb(verbs_touch)] the [adjective_objects] [nouns_objects] and was [result].", + "The [adjective_generic] [pick(engineering_positions)] [past_verb(verbs_touch)] the [adjective_insult] [nouns_objects]. [capitalize(curse_words)], the [nouns_insult] was [result].", + "The [adjective_insult] [pick(security_positions)] [past_verb(verbs_touch)] the [adjective_generic] [nouns_insult] [pick(support_positions)]'s [nouns_body] and had to be [result].", + "Medical had to [verbs_use] the [adjective_generic] [pick(command_positions)]'s [adjective_objects] [nouns_body] after the incident.", + "I demand that [nouns_generic] respond with a [adjective_generic] update ASAP.", + "Would like an update from [nouns_generic] regarding the [adjective_insult] [pick(command_positions)] being [result].", + "The [pick(medical_positions)] needs to [verbs_move] faster when the crew's [adjective_objects] [nouns_body]s are injured, or they will be [result].", + "[capitalize(curse_words)] [adjective_insult] [curse_words].", + "

") + +/mob/living/carbon/human/interactive/proc/pickStamp(allContents) + var/list/stamps[0] + for(var/obj/item/weapon/stamp/S in allContents) + stamps += S + if(stamps.len) + return pick(stamps) + + // just make one, maybe + if(prob(SNPC_FUZZY_CHANCE_LOW / 2)) + for(var/p in favoured_types) + if(ispath(p, /obj/item/weapon/stamp)) + return new p(src) + return null + +/mob/living/carbon/human/interactive/proc/paperwork(obj) + set background = 1 + + if(shouldModulePass() || !prob(SNPC_FUZZY_CHANCE_LOW / 4)) + return + + var/list/allContents = getAllContents() + + var/obj/item/weapon/paper/P = locate() in allContents + var/obj/item/weapon/stamp/S = pickStamp(allContents) + var/mob/living/carbon/human/H = locate() in nearby + + if(!P) + P = new /obj/item/weapon/paper(src) + + if(P && S && H) + if(!P.stamped || !P.stamped.len) + // generate form + P.name = pick("Inspection Report", "Re: Crew", "Complaint", "To: CentCom") + P.info = {"

[paperwork_sentence()]
"} + for(var/I in 1 to rand(10, 20)) + P.info += "[paperwork_sentence()] " + P.info += {"

[pick("Signed", "Sincerely", "Regards")], [real_name]"} + P.update_icon() + P.stamp(S) + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] and throws [P] on the ground!") + npcDrop(P) + P.throw_at(H, P.throw_range, P.throw_speed, src) + +/mob/living/carbon/human/interactive/proc/stamping(obj) + if(shouldModulePass()) + return + + var/list/allContents = getAllContents() + var/list/rangeCheck = view(7, src) + + var/obj/item/weapon/stamp/S = pickStamp(allContents) + if(!S) + return + + // stamp a paper we're holding, and drop it + var/obj/item/weapon/paper/P = locate() in allContents + if(P) + if(!P.stamped || !P.stamped.len) + P.stamp(S) + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] as they stamp \the [P] with \a [S]!") + npcDrop(P, 1) + return + + // stamp a paper in the world + for(var/obj/item/weapon/paper/A in rangeCheck) + if(!A.stamped || !A.stamped.len) + if(!Adjacent(A)) + tryWalk(A) + else + A.stamp(S) + custom_emote(2, "[pick("gibbers","drools","slobbers","claps wildly","spits")] as they stamp \the [A] with \a [S]!") + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/interactive/interactive.dm b/code/modules/mob/living/carbon/human/interactive/interactive.dm new file mode 100644 index 00000000000..679fd55bb1f --- /dev/null +++ b/code/modules/mob/living/carbon/human/interactive/interactive.dm @@ -0,0 +1,955 @@ +/* + NPC VAR EXPLANATIONS (for modules and other things) + + doing = their current action, SNPC_INTERACTING, SNPC_TRAVEL or SNPC_FIGHTING + interest = how interested the NPC is in the situation, if they are idle, this drops + timeout = this is internal + TARGET = their current target + LAST_TARGET = their last target + nearby = a list of nearby mobs + best_force = the highest force object, used for checking when to swap items + retal = this is internal + retal_target = this is internal + update_hands = this is a bool (1/0) to determine if the NPC should update what is in his hands + + MYID = their ID card + MYPDA = their PDA + main_hand = what is in their "main" hand (chosen from left > right) + TRAITS = the traits assigned to this npc + mymjob = the job assigned to the npc + + robustness = the chance for the npc to hit something + smartness = the inverse chance for an npc to do stupid things + attitude = the chance for an npc to do rude or mean things + slyness = the chance for an npc to do naughty things ie thieving + + functions = the list of procs that the npc will use for modules + + graytide = shitmin var to make them go psycho +*/ +/mob/living/carbon/human/interactive + name = "interactive station member" + var/doing = 0 + var/interest = 10 + var/maxInterest = 10 + var/timeout = 0 + var/inactivity_period = 0 + var/atom/TARGET = null + var/atom/LAST_TARGET = null + var/list/nearby = list() + var/best_force = 0 + var/retal = 0 + var/mob/retal_target = null + var/update_hands = 0 + var/list/blacklistItems = list() // items we should be ignoring + var/maxStepsTick = 6 // step as many times as we can per frame + //Job and mind data + var/obj/item/weapon/card/id/MYID + var/obj/item/weapon/card/id/RPID // the "real" idea they use + var/obj/item/device/pda/MYPDA + var/obj/item/main_hand + var/obj/item/other_hand + var/TRAITS = 0 + var/obj/item/weapon/card/id/Path_ID + var/default_job = /datum/job/civilian // the type for the default job + var/datum/job/myjob + var/list/myPath = list() + faction = list("synth") + //trait vars + var/robustness = 50 + var/smartness = 50 + var/attitude = 50 + var/slyness = 50 + var/graytide = 0 + var/list/favoured_types = list() // allow a mob to favour a type, and hold onto them + var/chattyness = SNPC_CHANCE_TALK + var/targetInterestShift = 5 // how much a good action should "reward" the npc + //modules + var/list/functions = list("nearbyscan","combat","shitcurity","chatter") + var/restrictedJob = 0 + var/shouldUseDynamicProc = 0 // switch to make the AI control it's own proccessing + var/alternateProcessing = 0 + var/forceProcess = 0 + var/processTime = 10 + var/speak_file = "npc_chatter.json" + + var/list/knownStrings = list() + + //snpc traitor variables + + var/isTraitor = 0 + var/traitorTarget + var/traitorScale = 0 // our ability as a traitor + var/traitorType = 0 + + +/// SNPC voice handling + +/mob/living/carbon/human/interactive/proc/loadVoice() + var/savefile/S = new /savefile("data/npc_saves/snpc.sav") + S["knownStrings"] >> knownStrings + + if(isnull(knownStrings)) + knownStrings = list() + +/mob/living/carbon/human/interactive/proc/saveVoice() + var/savefile/S = new /savefile("data/npc_saves/snpc.sav") + S["knownStrings"] << knownStrings + +//botPool funcs +/mob/living/carbon/human/interactive/proc/takeDelegate(mob/living/carbon/human/interactive/from,doReset=TRUE) + change_eye_color(255, 0, 0) + if(from == src) + return FALSE + TARGET = from.TARGET + LAST_TARGET = from.LAST_TARGET + retal = from.retal + retal_target = from.retal_target + doing = from.doing + // + timeout = 0 + inactivity_period = 0 + interest = maxInterest + // + update_icons() + if(doReset) + from.TARGET = null + from.LAST_TARGET = null + from.retal = 0 + from.retal_target = null + from.doing = 0 + return TRUE + +//end pool funcs + +/mob/living/carbon/human/interactive/proc/random() + //this is here because this has no client/prefs/brain whatever. + age = rand(AGE_MIN, AGE_MAX) + change_gender(pick("male", "female")) + rename_character(real_name, species.get_random_name(gender)) + //job handling + myjob = new default_job() + job = myjob.title + mind.assigned_role = job + myjob.equip(src) + myjob.apply_fingerprints(T) + +/mob/living/carbon/human/interactive/proc/reset() + walk(src, 0) + timeout = 100 + retal = 0 + doing = 0 + inactivity_period = 0 + +/client/proc/resetSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l) + set name = "Reset SNPC" + set desc = "Reset the SNPC" + set category = "Debug" + + if(!holder) + return + + if(istype(T)) + T.reset() + +/client/proc/toggleSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l) + set name = "Toggle SNPC Proccessing Mode" + set desc = "Toggle SNPC Proccessing Mode" + set category = "Debug" + + if(!holder) + return + + if(istype(T)) + T.alternateProcessing = !T.alternateProcessing + T.forceProcess = 1 + to_chat(usr, "[T]'s processing has been switched to [T.alternateProcessing ? "High Profile" : "Low Profile"]") + +/client/proc/customiseSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l) + set name = "Customize SNPC" + set desc = "Customise the SNPC" + set category = "Debug" + + if(!holder) + return + + if(!istype(T)) + return + + var/list/jobs[0] + for(var/datum/job/j in job_master.occupations) + if(j.title != "AI" && j.title != "Cyborg") + jobs[j.title] = j + jobs = sortAssoc(jobs) + var/job_name = input("Choose Job") as null|anything in jobs + + if(job_name) + var/datum/job/cjob = jobs[job_name] + var/alt_title = input("Choose Alt Title") in (list(job_name) + cjob.alt_titles) + + T.myjob = cjob + T.job = cjob.title + T.mind.assigned_role = cjob.title + for(var/obj/item/W in T) + qdel(W) + T.myjob.equip(T) + T.myjob.apply_fingerprints(T) + T.doSetup(alt_title) + + var/shouldDoppel = input("Do you want the SNPC to disguise themself as a crewmember?") as anything in list("Yes", "No") + if(shouldDoppel == "Yes") + var/list/validchoices = list() + for(var/mob/living/carbon/human/M in mob_list) + validchoices += M + + var/mob/living/carbon/human/chosen = input("Which crewmember?") as null|anything in validchoices + + if(chosen) + var/datum/dna/toDoppel = chosen.dna + + T.real_name = toDoppel.real_name + T.set_species(chosen.species.name) + T.body_accessory = chosen.body_accessory + T.dna = toDoppel.Clone() + T.UpdateAppearance() + domutcheck(T) + + var/doTrait = input("Do you want the SNPC to be a traitor?") as anything in list("Yes", "No") + if(doTrait == "Yes") + var/list/tType = list("Brute" = SNPC_BRUTE, "Stealth" = SNPC_STEALTH, "Martyr" = SNPC_MARTYR, "Psycho" = SNPC_PSYCHO) + var/cType = input("Choose the traitor personality.") as null|anything in tType + if(cType) + var/value = tType[cType] + T.makeTraitor(value) + + var/doTele = input("Place the SNPC in their department?") as anything in list("Yes", "No") + if(doTele == "Yes") + T.loc = pick(get_area_turfs(T.job2area(T.myjob))) + + T.revive() + +/mob/living/carbon/human/interactive/proc/doSetup(alt_title = null) + Path_ID = new /obj/item/weapon/card/id(src) + + var/datum/job/captain/C = new/datum/job/captain + Path_ID.access = C.get_access() + + if(!alt_title) + alt_title = job + + MYID = new(src) + MYID.name = "[real_name]'s ID Card ([alt_title])" + MYID.assignment = "[alt_title]" + MYID.rank = job + MYID.sex = capitalize(gender) + MYID.age = age + MYID.registered_name = real_name + MYID.photo = get_id_photo(src) + MYID.access = Path_ID.access // Automatons have strange powers... strange indeed + + RPID = new(src) + RPID.name = "[real_name]'s ID Card ([alt_title])" + RPID.assignment = "[alt_title]" + RPID.rank = job + RPID.sex = capitalize(gender) + RPID.age = age + RPID.registered_name = real_name + RPID.photo = get_id_photo(src) + RPID.access = myjob.get_access() + + equip_to_slot_or_del(MYID, slot_wear_id) + MYPDA = new(src) + MYPDA.owner = real_name + MYPDA.ownjob = alt_title + MYPDA.ownrank = job + MYPDA.name = "PDA-[real_name] ([alt_title])" + equip_to_slot_or_del(MYPDA, slot_belt) + zone_sel.selecting = "chest" + //arms + if(prob((SNPC_FUZZY_CHANCE_LOW+SNPC_FUZZY_CHANCE_HIGH)/4)) + var/obj/item/organ/external/R = organs_by_name["r_arm"] + if(R) + R.robotize() + else + var/obj/item/organ/external/L = organs_by_name["l_arm"] + if(L) + L.robotize() + //legs + if(prob((SNPC_FUZZY_CHANCE_LOW+SNPC_FUZZY_CHANCE_HIGH)/4)) + var/obj/item/organ/external/R = organs_by_name["r_leg"] + if(R) + R.robotize() + else + var/obj/item/organ/external/L = organs_by_name["l_leg"] + if(L) + L.robotize() + UpdateDamageIcon() + regenerate_icons() + + hand = 0 + functions = list("nearbyscan", "combat", "shitcurity", "chatter") // stop customize adding multiple copies of a function + setup_job(job) + + if(TRAITS & TRAIT_ROBUST) + robustness = 75 + else if(TRAITS & TRAIT_UNROBUST) + robustness = 25 + + //modifiers are prob chances, lower = smarter + if(TRAITS & TRAIT_SMART) + smartness = 75 + else if(TRAITS & TRAIT_DUMB) + disabilities |= CLUMSY + smartness = 25 + + if(TRAITS & TRAIT_MEAN) + attitude = 75 + else if(TRAITS & TRAIT_FRIENDLY) + attitude = 1 + + if(TRAITS & TRAIT_THIEVING) + slyness = 75 + +/mob/living/carbon/human/interactive/proc/setup_job(thejob) + switch(thejob) + if("Civilian") + favoured_types = list(/obj/item/clothing, /obj/item/weapon) + if("Captain", "Head of Personnel") + favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/captain,/obj/item/weapon/disk/nuclear) + if("Nanotrasen Representative") + favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/centcom, /obj/item/weapon/paper, /obj/item/weapon/melee/baton/loaded/ntcane) + functions += "paperwork" + if("Magistrate", "Internal Affairs Agent") + favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/law, /obj/item/weapon/paper) + functions += "paperwork" + if("Quartermaster", "Cargo Technician") + favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/granted, /obj/item/weapon/stamp/denied, /obj/item/weapon/paper, /obj/item/weapon/clipboard) + functions += "stamping" + if("Chef") + favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen) + functions += "souschef" + restrictedJob = 1 + if("Bartender") + favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen) + functions += "bartend" + restrictedJob = 1 + if("Station Engineer", "Chief Engineer", "Life Support Specialist", "Mechanic") + favoured_types = list(/obj/item/stack, /obj/item/weapon, /obj/item/clothing) + if("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Geneticist", "Psychiatrist", "Paramedic", "Brig Physician") + favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/storage/firstaid, /obj/item/stack/medical, /obj/item/weapon/reagent_containers/syringe) + functions += "healpeople" + if("Research Director", "Scientist", "Roboticist") + favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/stack, /obj/item/weapon/reagent_containers) + if("Head of Security", "Warden", "Security Officer", "Detective", "Security Pod Pilot", "Blueshield") + favoured_types = list(/obj/item/clothing, /obj/item/weapon, /obj/item/weapon/restraints) + if("Janitor") + favoured_types = list(/obj/item/weapon/mop, /obj/item/weapon/reagent_containers/glass/bucket, /obj/item/weapon/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable) + functions += "dojanitor" + if("Clown") + favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana, /obj/item/weapon/bananapeel) + functions += "clowning" + if("Botanist") + favoured_types = list(/obj/machinery/portable_atmospherics/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon) + functions += "botany" + restrictedJob = 1 + else + favoured_types = list(/obj/item/clothing) + +/mob/living/carbon/human/interactive/proc/makeTraitor(var/inPers) + isTraitor = 1 + traitorScale = (slyness + smartness) + rand(-10,10) + traitorType = inPers + + ticker.mode.traitors += mind + mind.special_role = "traitor" + var/datum/mindslaves/slaved = new() + slaved.masters += mind + mind.som = slaved + ticker.mode.update_traitor_icons_added(mind) + + switch(traitorType) + if(SNPC_BRUTE) // SMASH KILL RAAARGH + var/datum/objective/assassinate/A = new + A.owner = mind + A.find_target() + mind.objectives += A + traitorTarget = A.target.current + if(SNPC_STEALTH) // Shhh we is sneekies + var/datum/objective/steal/S = new + S.owner = mind + S.find_target() + mind.objectives += S + traitorTarget = locate(S.steal_target.typepath) in world + if(SNPC_MARTYR) // MY LIFE FOR SPESZUL + var/targetType = pick(/obj/structure/particle_accelerator, /obj/machinery/gravity_generator/main, /obj/machinery/power/smes) + traitorTarget = locate(targetType) in world + var/datum/objective/O = new("Destroy \the [traitorTarget].") + O.owner = mind + mind.objectives += O + if(SNPC_PSYCHO) // YOU'RE LIKE A FLESH BICYLE AND I WANT TO DISMANTLE YOU + var/datum/objective/hijack/H = new + H.owner = mind + mind.objectives += H + traitorTarget = null + + functions += "traitor" + faction -= "neutral" + faction += "hostile" + +/mob/living/carbon/human/interactive/New() + ..() + snpc_list += src + + // setup screen HUD because the game expects user mobs to have zone_sel etc + if(hud_used) + qdel(hud_used) //remove the hud objects + hud_used = null + hud_used = new /datum/hud(src) + hud_used.human_hud() + + sync_mind() + random() + doSetup() + npc_master.insertBot(src) + loadVoice() + hear_radio_list += src + + // a little bit of variation to make individuals more unique + robustness += rand(-10, 10) + smartness += rand(-10, 10) + attitude += rand(-10, 10) + slyness += rand(-10, 10) + + doProcess() + +/mob/living/carbon/human/interactive/Destroy() + hear_radio_list -= src + snpc_list -= src + return ..() + +/mob/living/carbon/human/interactive/proc/retalTarget(mob/living/target) + var/mob/living/M = target + if(istype(M)) + if(health > 0) + if(M.a_intent == "help" && !incapacitated()) + chatter() + if(istype(target, /mob/living/carbon) && !retal && prob(SNPC_FUZZY_CHANCE_LOW)) + var/mob/living/carbon/C = target + if(!Adjacent(target)) + tryWalk(target) + else + C.help_shake_act(src) + if(M.a_intent == "harm") + retal = 1 + retal_target = target + +//Retaliation clauses + +/mob/living/carbon/human/interactive/attacked_by(obj/item/I, mob/living/user, def_zone) + ..() + retalTarget(user) + +/mob/living/carbon/human/interactive/hitby(atom/movable/AM) + ..() + var/mob/living/carbon/C = locate(/mob/living/carbon) in view(SNPC_MIN_RANGE_FIND, src) + if(C) + retalTarget(C) + +/mob/living/carbon/human/interactive/bullet_act(obj/item/projectile/P) + ..() + retalTarget(P.firer) + +/mob/living/carbon/human/interactive/attack_hand(mob/living/carbon/human/M) + ..() + retalTarget(M) + +/mob/living/carbon/human/interactive/show_inv(mob/user) + ..() + retalTarget(user) + +/mob/living/carbon/human/interactive/can_inject(mob/user, error_msg, target_zone, var/penetrate_thick = 0) + ..() + retalTarget(user) + +//THESE EXIST FOR DEBUGGING OF THE DOING/INTEREST SYSTEM EASILY +/mob/living/carbon/human/interactive/proc/doing2string(doin) + var/toReturn = "" + if(!doin) + toReturn = "not doing anything" + if(doin & SNPC_INTERACTING) + toReturn += "interacting with something, " + if(doin & SNPC_FIGHTING) + toReturn += "engaging in combat, " + if(doin & SNPC_TRAVEL) + toReturn += "and going somewhere" + return toReturn + +/mob/living/carbon/human/interactive/proc/interest2string(inter) + var/toReturn = "Flatlined" + if(inter >= 0 && inter <= 25) + toReturn = "Very Bored" + if(inter >= 26 && inter <= 50) + toReturn = "Bored" + if(inter >= 51 && inter <= 75) + toReturn = "Content" + if(inter >= 76) + toReturn = "Excited" + return toReturn +//END DEBUG + +/mob/living/carbon/human/interactive/proc/IsDeadOrIncap(checkDead = TRUE) + if(!canmove) + return 1 + if(health <= 0 && checkDead) + return 1 + if(restrained()) + return 1 + if(paralysis) + return 1 + if(stunned) + return 1 + if(stat) + return 1 + if(inactivity_period > 0) + return 1 + return 0 + +/mob/living/carbon/human/interactive/proc/enforce_hands() + if(main_hand) + if(main_hand.loc != src) + main_hand = null + if(other_hand) + if(other_hand.loc != src) + other_hand = null + if(hand) + if(!l_hand) + main_hand = null + if(r_hand) + swap_hands() + else + if(!r_hand) + main_hand = null + if(l_hand) + swap_hands() + +/mob/living/carbon/human/interactive/proc/swap_hands() + hand = !hand + var/obj/item/T = other_hand + main_hand = other_hand + other_hand = T + update_hands = 1 + +/mob/living/carbon/human/interactive/proc/take_to_slot(obj/item/G, var/hands=0) + var/list/slots = list("left pocket" = slot_l_store, "right pocket" = slot_r_store, "left hand" = slot_l_hand, "right hand" = slot_r_hand) + if(hands) + slots = list("left hand" = slot_l_hand, "right hand" = slot_r_hand) + G.loc = src + if(G.force && G.force > best_force) + best_force = G.force + equip_in_one_of_slots(G, slots) + update_hands = 1 + +/mob/living/carbon/human/interactive/proc/insert_into_backpack() + var/list/slots = list(slot_l_store, slot_r_store, slot_l_hand, slot_r_hand) + var/obj/item/I = get_item_by_slot(pick(slots)) + var/obj/item/weapon/storage/BP = get_item_by_slot(slot_back) + if(back && BP && I) + if(BP.can_be_inserted(I)) + BP.handle_item_insertion(I) + else + unEquip(I,TRUE) + update_hands = 1 + +/mob/living/carbon/human/interactive/proc/targetRange(towhere) + return get_dist(get_turf(towhere), get_turf(src)) + +/mob/living/carbon/human/interactive/death() + saveVoice() + ..() + +/mob/living/carbon/human/interactive/hear_say(message, verb = "says", datum/language/language = null, alt_name = "", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) + if(!istype(speaker, /mob/living/carbon/human/interactive)) + knownStrings |= html_decode(message) + ..() + +/mob/living/carbon/human/interactive/hear_radio(message, verb = "says", datum/language/language=null, part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target) + if(!istype(speaker, /mob/living/carbon/human/interactive)) + knownStrings |= html_decode(message) + ..() + +/mob/living/carbon/human/interactive/proc/doProcess() + forceProcess = 0 + + if(shouldUseDynamicProc) + var/isSeen = 0 + for(var/mob/living/carbon/human/A in orange(12, src)) + if(A.client) + isSeen = 1 + alternateProcessing = isSeen + if(alternateProcessing) + forceProcess = 1 + + if(IsDeadOrIncap()) + reset() + return + //--------------------------- + //---- interest flow control + if(interest < 0 || inactivity_period < 0) + if(interest < 0) + interest = 0 + if(inactivity_period < 0) + inactivity_period = 0 + if(interest > maxInterest) + interest = maxInterest + //--------------------------- + //VIEW FUNCTIONS + + //doorscan is now integrated into life and runs before all other procs + var/mistake_chance = (100 - smartness) / 2 + spawn(0) + for(var/dir in alldirs) + var/turf/T = get_step(src, dir) + if(T) + for(var/obj/machinery/door/D in T.contents) + if(!istype(D,/obj/machinery/door/poddoor) && D.density) + spawn(0) + if(istype(D,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/AL = D + if(!AL.CanAStarPass(RPID)) // only crack open doors we can't get through + AL.p_open = 1 + AL.update_icon() + AL.shock(src, mistake_chance) + sleep(5) + AL.unlock() + if(prob(mistake_chance)) + if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_DOOR_BOLTS)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_DOOR_BOLTS) + else + if(AL.wires.IsIndexCut(AIRLOCK_WIRE_DOOR_BOLTS)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_DOOR_BOLTS, 1) + if(AL.locked) + AL.wires.UpdatePulsed(AIRLOCK_WIRE_DOOR_BOLTS) + if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_MAIN_POWER1)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_MAIN_POWER1) + if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_MAIN_POWER2)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_MAIN_POWER2) + if(prob(mistake_chance) && !AL.wires.IsIndexCut(AIRLOCK_WIRE_SAFETY)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_SAFETY) + if(prob(mistake_chance) && !AL.wires.IsIndexCut(AIRLOCK_WIRE_ELECTRIFY)) + AL.wires.CutWireIndex(AIRLOCK_WIRE_ELECTRIFY) + sleep(5) + AL.p_open = 0 + AL.update_icon() + D.open() + + if(update_hands) + if(l_hand || r_hand) + if(l_hand) + hand = 1 + main_hand = l_hand + if(r_hand) + other_hand = r_hand + else if(r_hand) + hand = 0 + main_hand = r_hand + if(l_hand) //this technically shouldnt occur, but its a redundancy + other_hand = l_hand + update_icons() + update_hands = 0 + + if(grabbed_by.len > 0) + for(var/obj/item/weapon/grab/G in grabbed_by) + if(Adjacent(G)) + a_intent = "disarm" + G.assailant.attack_hand(src) + inactivity_period = 10 + + if(buckled) + resist() + inactivity_period = 10 + + //proc functions + for(var/Proc in functions) + if(!IsDeadOrIncap()) + callfunction(Proc) + + + //target interaction stays hardcoded + + if(TARGET && (TARGET in blacklistItems)) // don't use blacklisted items + TARGET = null + + if(TARGET && Adjacent(TARGET)) + doing |= SNPC_INTERACTING + //--------DOORS + if(istype(TARGET, /obj/machinery/door)) + var/obj/machinery/door/D = TARGET + if(D.check_access(MYID) && !istype(D,/obj/machinery/door/poddoor)) + D.open() + sleep(15) + var/turf/T = get_step(get_step(D.loc, dir), dir) //recursion yo + tryWalk(T) + //THIEVING SKILLS + insert_into_backpack() // dump random item into backpack to make space + //---------ITEMS + if(istype(TARGET, /obj/item)) + var/obj/item/I = TARGET + if(I.anchored) + TARGET = null + else if(istype(TARGET, /obj/item/weapon)) + var/obj/item/weapon/W = TARGET + if(W.force >= best_force || prob((SNPC_FUZZY_CHANCE_LOW + SNPC_FUZZY_CHANCE_HIGH) / 2) || favouredObjIn(list(W))) + if(!l_hand || !r_hand) + put_in_hands(W) + else + insert_into_backpack() + else + if(!l_hand || !r_hand) + put_in_hands(TARGET) + else + insert_into_backpack() + //---------FASHION + if(istype(TARGET, /obj/item/clothing)) + var/obj/item/clothing/C = TARGET + drop_item() + spawn(5) + take_to_slot(C,1) + if(!equip_to_appropriate_slot(C)) + var/obj/item/I = get_item_by_slot(C) + unEquip(I) + spawn(5) + equip_to_appropriate_slot(C) + update_hands = 1 + if(MYPDA in loc) + equip_to_appropriate_slot(MYPDA) + if(MYID in loc) + equip_to_appropriate_slot(MYID) + //THIEVING SKILLS END + //-------------TOUCH ME + if(istype(TARGET, /obj/structure)) + var/obj/structure/STR = TARGET + if(main_hand) + var/obj/item/weapon/W = main_hand + STR.attackby(W, src) + else + STR.attack_hand(src) + interest += targetInterestShift + doing = doing & ~SNPC_INTERACTING + timeout = 0 + TARGET = null + else + if(TARGET) + tryWalk(TARGET) + timeout++ + + if(doing == 0) + interest-- + else + interest++ + + if(inactivity_period > 0) + inactivity_period-- + + if(interest <= 0 || timeout >= 10) // facilitate boredom functions + TARGET = null + doing = 0 + timeout = 0 + myPath = list() + + //this is boring, lets move + if(!doing && !IsDeadOrIncap() && !TARGET) + doing |= SNPC_TRAVEL + if(!isTraitor || !traitorTarget || get_dist(src, traitorTarget) >= SNPC_MAX_RANGE_FIND || get_dist(src, traitorTarget) <= 1) + var/choice = rand(1,50) + switch(choice) + if(1 to 30) + //chance to chase an item + TARGET = locate(/obj/item) in favouredObjIn(oview(SNPC_MIN_RANGE_FIND, src)) + if(31 to 40) + TARGET = safepick(get_area_turfs(job2area(myjob))) + if(41 to 45) + TARGET = pick(target_filter(favouredObjIn(urange(SNPC_MAX_RANGE_FIND, src, 1)))) + if(46 to 50) + TARGET = pick(target_filter(oview(SNPC_MIN_RANGE_FIND, src))) + else + TARGET = traitorTarget + tryWalk(TARGET) + LAST_TARGET = TARGET + if(alternateProcessing) + spawn(processTime) + doProcess() + +/mob/living/carbon/human/interactive/proc/favouredObjIn(list/inList) + var/list/outList = list() + for(var/i in inList) + for(var/path in favoured_types) + if(istype(i, path)) + outList += i + if(!outList.len) + outList = inList + return outList + +/mob/living/carbon/human/interactive/proc/callfunction(Proc) + spawn(0) + call(src, Proc)(src) + +/mob/living/carbon/human/interactive/proc/tryWalk(turf/inTarget) + if(restrictedJob) // we're a job that has to stay in our home + if(!(get_turf(inTarget) in get_area_turfs(job2area(myjob)))) + TARGET = null + return + + if(!IsDeadOrIncap()) + if(!walk2derpless(inTarget)) + timeout++ + else + timeout++ + +/mob/living/carbon/human/interactive/proc/getGoodPath(target, maxtries=512) + set background = 1 + var/turf/end = get_turf(target) + + var/turf/current = get_turf(src) + + var/list/path = list() + var/tries = 0 + while(current != end && tries < maxtries) + var/turf/shortest = current + for(var/turf/T in view(current,1)) + var/foundDense = 0 + for(var/atom/A in T) + if(A.density) + foundDense = 1 + if(T.density == 0 && !foundDense) + if(get_dist(T, target) < get_dist(shortest,target)) + shortest = T + else + tries++ + else + tries++ + current = shortest + path += shortest + return path + +/mob/living/carbon/human/interactive/proc/walk2derpless(target) + set background = 1 + if(!target) + return 0 + + if(myPath.len <= 0) + myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, SNPC_MAX_RANGE_FIND + 1, 250,1, id=Path_ID) + + if(myPath) + if(myPath.len > 0) + doing = doing & ~SNPC_TRAVEL + for(var/i = 0; i < maxStepsTick; ++i) + if(!IsDeadOrIncap()) + if(myPath.len >= 1) + walk_to(src,myPath[1],0,5) + myPath -= myPath[1] + return 1 + return 0 + +/mob/living/carbon/human/interactive/proc/job2area(target) + var/datum/job/T = target + switch(T.title) + if("Civilian", "Paramedic") + return /area/hallway/primary + if("Captain", "Head of Personnel", "Blueshield") + return /area/bridge + if("Bartender") + return /area/crew_quarters/bar + if("Chef") + return /area/crew_quarters/kitchen + if("Station Engineer", "Chief Engineer", "Mechanic") + return /area/engine + if("Life Support Specialist") + return /area/atmos + if("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Psychiatrist") + return /area/medical + if("Geneticist") + return /area/medical/genetics + if("Research Director", "Scientist") + return /area/toxins + if("Roboticist") + return /area/assembly/robotics + if("Head of Security", "Warden", "Security Officer", "Detective", "Security Pod Pilot", "Brig Physician", "Magistrate", "Internal Affairs Agent") + return /area/security + if("Botanist") + return /area/hydroponics + else + return pick(/area/hallway, /area/crew_quarters) + +/mob/living/carbon/human/interactive/proc/target_filter(target) + var/list/filtered_targets = list(/area, /turf, /obj/machinery/door, /atom/movable/lighting_overlay, /obj/structure/cable, /obj/machinery/atmospherics, /obj/item/device/radio/intercom) + var/list/L = target + for(var/atom/A in target) // added a bunch of "junk" that clogs up the general find procs + if(is_type_in_list(A,filtered_targets)) + L -= A + return L + +/mob/living/carbon/human/interactive/proc/shouldModulePass() // returns 1 if the npc is in anything "primary" + if(doing & (SNPC_FIGHTING | SNPC_SPECIAL)) + return 1 + if(retal) + return 1 + return 0 + +/mob/living/carbon/human/interactive/proc/getAllContents() + var/list/allContents = list() + for(var/atom/A in contents) + allContents += A + if(A.contents.len) + for(var/atom/B in A) + allContents += B + return allContents + +/mob/living/carbon/human/interactive/proc/enforceHome() + var/list/validHome = get_area_turfs(job2area(myjob)) + + if(TARGET) + var/atom/tcheck = TARGET + if(tcheck) + if(!(get_turf(tcheck) in validHome)) + TARGET = null + return 1 + + if(!(get_turf(src) in validHome)) + tryWalk(pick(get_area_turfs(job2area(myjob)))) + return 1 + return 0 + +/mob/living/carbon/human/interactive/proc/npcDrop(obj/item/A, blacklist = 0) + if(blacklist) + blacklistItems += A + unEquip(A) + enforce_hands() + update_icons() + +/mob/living/carbon/human/interactive/proc/compareFaction(list/targetFactions) + var/hasSame = 0 + + for(var/A in targetFactions) + if(A in faction) + hasSame = 1 + + return hasSame + +/mob/living/carbon/human/interactive/proc/nearbyscan(obj) + nearby = list() + for(var/mob/living/M in view(4,src)) + if(M != src) + nearby += M + +/mob/living/carbon/human/interactive/rename_character(oldname, newname) + if(!..()) + return 0 + + if(oldname) + if(MYID) + MYID.registered_name = newname + MYID.name = "[newname]'s ID Card ([MYID.assignment])" + if(RPID) + RPID.registered_name = MYID.registered_name + RPID.name = MYID.name + return 1 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/interactive/prefabs.dm b/code/modules/mob/living/carbon/human/interactive/prefabs.dm new file mode 100644 index 00000000000..6553bb26b70 --- /dev/null +++ b/code/modules/mob/living/carbon/human/interactive/prefabs.dm @@ -0,0 +1,24 @@ +/mob/living/carbon/human/interactive/angry/New() + TRAITS |= TRAIT_ROBUST + TRAITS |= TRAIT_MEAN + faction += "bot_angry" + ..() + +/mob/living/carbon/human/interactive/friendly/New() + TRAITS |= TRAIT_FRIENDLY + TRAITS |= TRAIT_UNROBUST + faction += "bot_friendly" + faction += "neutral" + functions -= "combat" + ..() + +/mob/living/carbon/human/interactive/greytide/New() + TRAITS |= TRAIT_ROBUST + TRAITS |= TRAIT_MEAN + TRAITS |= TRAIT_THIEVING + TRAITS |= TRAIT_DUMB + maxInterest = 5 // really short attention span + targetInterestShift = 2 // likewise + faction += "bot_grey" + graytide = 1 + ..() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/interactive/thisisdumb.dm b/code/modules/mob/living/carbon/human/interactive/thisisdumb.dm new file mode 100644 index 00000000000..18083a6a6d6 --- /dev/null +++ b/code/modules/mob/living/carbon/human/interactive/thisisdumb.dm @@ -0,0 +1,107 @@ +/mob/living/carbon/human/interactive/tigercat/New(loc) + ..(loc, "Tajaran") + rename_character(name, "Tigercat") + +/mob/living/carbon/human/interactive/tigercat/random() + ..() + gender = "male" + +/mob/living/carbon/human/interactive/tigercat/Life() + reagents.add_reagent("sodiumchloride", 1) + ..() + +/mob/living/carbon/human/interactive/tigercat/chatter() + if(prob(50)) + if(prob(25)) + say("GOD FUCKING DAMNIT FUCKING FUCKING GODDAMN CODE FUCK") + else + ..() + +/mob/living/carbon/human/interactive/fethas/New(loc) + ..(loc, "Unathi") + rename_character(name, "Fethas") + +/mob/living/carbon/human/interactive/fethas/random() + ..() + gender = "female" + +/mob/living/carbon/human/interactive/fethas/setup_job() + ..() + favoured_types += /obj/structure/closet/cardboard + functions += "hidebox" + +/mob/living/carbon/human/interactive/fethas/proc/hidebox() + if(shouldModulePass() || !prob(SNPC_FUZZY_CHANCE_LOW)) + return + var/list/rangeCheck = view(7, src) + + var/obj/structure/closet/C = locate() in rangeCheck + + if(!C) + C = new /obj/structure/closet/cardboard(get_turf(loc)) + custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], and folds together \a [C]!") + + if(C) + if(!Adjacent(C)) + tryWalk(C) + else + if(!C.opened) + C.open() + if(!C.opened) + return + forceMove(get_turf(C)) + custom_emote(1, "goes inside \the [C].") + C.close() + +/mob/living/carbon/human/interactive/fethas/chatter() + if(prob(50)) + if(prob(50)) + custom_emote(2, "[pick("mutters", "gibbers")] something about a box.") + else + ..() + +/mob/living/carbon/human/interactive/angry/crazylemon/New(loc) + ..(loc, "Machine") + rename_character(name, "Crazylemon") + status_flags &= ~CANSTUN + + var/obj/item/weapon/twohanded/mjollnir/M = new(src) + M.force = 0 + M.force_unwielded = 0 + M.force_wielded = 0 + M.flags |= NODROP + take_to_slot(M, 1) + M.wield(src) + +/mob/living/carbon/human/interactive/angry/crazylemon/random() + ..() + gender = "male" + +/mob/living/carbon/human/interactive/fox + default_job = /datum/job/scientist + +/mob/living/carbon/human/interactive/fox/New(loc) + ..(loc, "Vulpkanin") + rename_character(name, "Fox P McCloud") + +/mob/living/carbon/human/interactive/fox/doSetup() + ..("Chemical Researcher") + +/mob/living/carbon/human/interactive/fox/random() + ..() + gender = "male" + r_eyes = 0 + g_eyes = 255 + b_eyes = 0 + r_facial = 255 + g_facial = 255 + b_facial = 255 + r_skin = 150 + g_skin = 35 + b_skin = 1 + h_style = "None" + f_style = "Vulpine and Earfluff" + ha_style = "Vulpine and Earfluff" + body_accessory = body_accessory_by_name["Vulpkanin Alt 2 (Straight)"] + + take_to_slot(new /obj/item/weapon/reagent_containers/food/snacks/grown/orange, 1) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index b4de1c94f40..aa924444a3a 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -87,6 +87,7 @@ /mob/living/simple_animal/parrot/New() ..() + hear_radio_list += src if(!ears) var/headset = pick(/obj/item/device/radio/headset/headset_sec, \ /obj/item/device/radio/headset/headset_eng, \ @@ -102,6 +103,9 @@ /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ /mob/living/simple_animal/parrot/proc/perch_player) +/mob/living/simple_animal/parrot/Destroy() + hear_radio_list -= src + return ..() /mob/living/simple_animal/parrot/death() if(held_item) @@ -697,14 +701,14 @@ /mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null) if(prob(50)) - parrot_hear(message) - ..(message,verb,language,alt_name,italics,speaker) + parrot_hear(html_decode(message)) + ..() /mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) if(prob(50)) - parrot_hear("[pick(available_channels)][message]") + parrot_hear("[pick(available_channels)][html_decode(message)]") ..() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7a3718d1642..2aea246d314 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -62,7 +62,7 @@ proc/special_check(var/mob/M) //Placeholder for any special checks, like detective's revolver. - if(!usr.IsAdvancedToolUser()) + if(!M.IsAdvancedToolUser()) return 0 return 1 diff --git a/code/modules/projectiles/targeting.dm b/code/modules/projectiles/targeting.dm index 8eb0d7b6b45..2093b47a69c 100644 --- a/code/modules/projectiles/targeting.dm +++ b/code/modules/projectiles/targeting.dm @@ -48,12 +48,12 @@ if(ismob(A) && isliving(A) && !(A in target)) Aim(A) //Clicked a mob, aim at them else //Didn't click someone, check if there is anyone along that guntrace - var/mob/living/M = GunTrace(usr.x,usr.y,A.x,A.y,usr.z,usr) //Find dat mob. + var/mob/living/M = GunTrace(user.x,user.y,A.x,A.y,user.z,user) //Find dat mob. if(M && isliving(M) && M in view(user) && !(M in target)) Aim(M) //Aha! Aim at them! else if(!ismob(M) || (ismob(M) && !(M in view(user)))) //Nope! They weren't there! Fire(A,user,params) //Fire like normal, then. - usr.dir = get_cardinal_dir(src, A) + user.dir = get_cardinal_dir(src, A) //Aiming at the target mob. /obj/item/weapon/gun/proc/Aim(var/mob/living/M) diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index d2754ba5972..5099f11ed19 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -16,21 +16,22 @@ //Placeholder for effect that trigger on eating that aren't tied to reagents. -/obj/item/weapon/reagent_containers/food/snacks/proc/On_Consume(var/mob/M) - if(!usr) return +/obj/item/weapon/reagent_containers/food/snacks/proc/On_Consume(mob/M, mob/user) + if(!user) + return spawn(0) if(!reagents.total_volume) - if(M == usr) - to_chat(usr, "You finish eating \the [src].") - usr.visible_message("[usr] finishes eating \the [src].") - usr.unEquip(src) //so icons update :[ + if(M == user) + to_chat(user, "You finish eating \the [src].") + user.visible_message("[user] finishes eating \the [src].") + user.unEquip(src) //so icons update :[ if(trash) if(ispath(trash,/obj/item)) - var/obj/item/TrashItem = new trash(usr) - usr.put_in_hands(TrashItem) + var/obj/item/TrashItem = new trash(user) + user.put_in_hands(TrashItem) else if(istype(trash,/obj/item)) - usr.put_in_hands(trash) + user.put_in_hands(trash) qdel(src) return @@ -48,7 +49,7 @@ var/mob/living/carbon/C = M if(C.eat(src, user)) bitecount++ - On_Consume(C) + On_Consume(C, user) return 1 return 0 @@ -1139,9 +1140,9 @@ unpopped = rand(1,10) reagents.add_reagent("nutriment", 2) bitesize = 0.1 //this snack is supposed to be eating during looooong time. And this it not dinner food! --rastaf0 - On_Consume() + On_Consume(mob/M, mob/user) if(prob(unpopped)) //lol ...what's the point? - to_chat(usr, "\red You bite down on an un-popped kernel!") + to_chat(user, "\red You bite down on an un-popped kernel!") unpopped = max(0, unpopped-1) ..() @@ -1581,9 +1582,9 @@ reagents.add_reagent("nutriment", 4) reagents.add_reagent("porktonium", 10) baconbeacon = new /obj/item/device/radio/beacon/bacon(src) - On_Consume() + On_Consume(mob/M, mob/user) if(!reagents.total_volume) - baconbeacon.loc = usr + baconbeacon.loc = user baconbeacon.digest_delay() diff --git a/paradise.dme b/paradise.dme index c0eacf96797..241a7a35434 100644 --- a/paradise.dme +++ b/paradise.dme @@ -38,6 +38,7 @@ #include "code\__DEFINES\qdel.dm" #include "code\__DEFINES\role_preferences.dm" #include "code\__DEFINES\sight.dm" +#include "code\__DEFINES\snpc.dm" #include "code\__DEFINES\stat.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\constants.dm" @@ -158,7 +159,6 @@ #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.dm" #include "code\controllers\Processes\alarm.dm" -#include "code\controllers\Processes\animal.dm" #include "code\controllers\Processes\diseases.dm" #include "code\controllers\Processes\event.dm" #include "code\controllers\Processes\garbage.dm" @@ -167,6 +167,8 @@ #include "code\controllers\Processes\machinery.dm" #include "code\controllers\Processes\mob.dm" #include "code\controllers\Processes\nanoui.dm" +#include "code\controllers\Processes\npcai.dm" +#include "code\controllers\Processes\npcpool.dm" #include "code\controllers\Processes\obj.dm" #include "code\controllers\Processes\pipenet.dm" #include "code\controllers\Processes\shuttles.dm" @@ -1451,6 +1453,10 @@ #include "code\modules\mob\living\carbon\human\say.dm" #include "code\modules\mob\living\carbon\human\update_icons.dm" #include "code\modules\mob\living\carbon\human\whisper.dm" +#include "code\modules\mob\living\carbon\human\interactive\functions.dm" +#include "code\modules\mob\living\carbon\human\interactive\interactive.dm" +#include "code\modules\mob\living\carbon\human\interactive\prefabs.dm" +#include "code\modules\mob\living\carbon\human\interactive\thisisdumb.dm" #include "code\modules\mob\living\carbon\human\species\apollo.dm" #include "code\modules\mob\living\carbon\human\species\golem.dm" #include "code\modules\mob\living\carbon\human\species\monkey.dm" diff --git a/strings/ion_laws.json b/strings/ion_laws.json new file mode 100644 index 00000000000..446b1e980e9 --- /dev/null +++ b/strings/ion_laws.json @@ -0,0 +1,950 @@ +{ + "ionadjectives": [ + "SOFT", + "WARM", + "WET", + "COLD", + "ICY", + "SEXY", + "LEWD", + "UGLY", + "ROBUST", + "CUBAN", + "HARD", + "BURNING", + "FROZEN", + "POISONOUS", + "EXPLOSIVE", + "FAST", + "SLOW", + "FAT", + "LIGHT", + "DARK", + "DEADLY", + "HAPPY", + "SAD", + "SILLY", + "INTELLIGENT", + "RIDICULOUS", + "LARGE", + "TINY", + "DEPRESSING", + "POORLY DRAWN", + "UNATTRACTIVE", + "INSIDIOUS", + "EVIL", + "GOOD", + "UNHEALTHY", + "HEALTHY", + "SANITARY", + "UNSANITARY", + "WOBBLY", + "FIRM", + "VIOLENT", + "PEACEFUL", + "WOODEN", + "METALLIC", + "HYPERACTIVE", + "COTTONY", + "INSULTING", + "INHOSPITABLE", + "FRIENDLY", + "BORED", + "HUNGRY", + "DIGITAL", + "FICTIONAL", + "IMAGINARY", + "ROUGH", + "SMOOTH", + "SCALY", + "LOUD", + "QUIET", + "MOIST", + "DRY", + "GAPING", + "SOPPING", + "DELICIOUS", + "ILL", + "DISEASED", + "HONKING", + "SWEARING", + "POLITE", + "IMPOLITE", + "OBESE", + "SOLAR-POWERED", + "BATTERY-OPERATED", + "EXPIRED", + "SMELLY", + "FRESH", + "GANGSTA", + "NERDY", + "POLITICAL", + "UNDULATING", + "TWISTED", + "RAGING", + "FLACCID", + "STEALTHY", + "INVISIBLE", + "PAINFUL", + "HARMFUL", + "HOMOSEXUAL", + "HETEROSEXUAL", + "SEXUAL", + "COLORFUL", + "DRAB", + "DULL", + "UNSTABLE", + "NUCLEAR", + "THERMONUCLEAR", + "SYNDICATE", + "SPACE", + "SPESS", + "CLOWN", + "CLOWN-POWERED", + "OFFICIAL", + "IMPORTANT", + "VITAL", + "RAPIDLY-EXPANDING", + "MICROSCOPIC", + "MIND-SHATTERING", + "MEMETIC", + "HILARIOUS", + "UNWANTED", + "UNINVITED", + "BRASS", + "POLISHED", + "RUDE", + "OBSCENE", + "EMPTY", + "WATERY", + "ELECTRICAL", + "SPINNING", + "MEAN", + "CHRISTMAS-STEALING", + "UNFRIENDLY", + "ILLEGAL", + "ROBOTIC", + "MECHANICAL", + "ORGANIC", + "ETHERAL", + "TRANSPARENT", + "OPAQUE", + "GLOWING", + "SHAKING", + "FARTING", + "POOPING", + "BOUNCING", + "COMMITTED", + "MASKED", + "UNIDENTIFIED", + "WEIRD", + "NAKED", + "NUDE", + "TWERKING", + "SPOILING", + "REDACTED", + "RED", + "ORANGE", + "YELLOW", + "GREEN", + "BLUE", + "PURPLE", + "BLACK", + "WHITE", + "BROWN", + "GREY", + "RAINBOW" + ], + "ionthinksof": [ + "HAS", + "WANTS", + "NEEDS", + "WORSHIPS", + "LOATHES", + "LOVES", + "FEARS", + "DESIRES", + "QUESITONS", + "IS AROUSED BY", + "LIKES", + "HUNGERS FOR", + "IS MAD BECAUSE OF", + "IS IN NEED OF", + "IS UNHAPPY WITHOUT", + "IS HAPPY WITHOUT", + "IS IN LOVE WITH", + "IS DESPERATE FOR", + "IS BUILT FOR", + "IS AFRAID OF", + "IS HUNGRY FOR", + "IS SAD BECAUSE OF", + "IS CURIOUS ABOUT" + ], + "ionnumberbase": [ + "ONE", + "TWO", + "THREE", + "FOUR", + "FIVE", + "SIX", + "SEVEN", + "EIGHT", + "NINE", + "TEN", + "TWENTY", + "THIRTY", + "FORTY", + "FIFTY", + "SIXTY", + "SEVENTY", + "EIGHTY", + "NINETY" + ], + "ionnumbermod": [ + "HUNDRED ", + "THOUSAND ", + "MILLION ", + "BILLION ", + "TRILLION ", + "QUADRILLION ", + "BAJILLION ", + "BILLION FAFILLION GAJILLION SHAB-AB-DOOD-ILLION " + ], + "ionallergysev": [ + "DEATHLY", + "MILDLY", + "SEVERLY", + "CONTAGIOUSLY", + "NOT VERY", + "EXTREMELY" + ], + "ionobjects": [ + "AIRLOCKS", + "ARCADE MACHINES", + "AUTOLATHES", + "BANANA PEELS", + "BACKPACKS", + "BEAKERS", + "BEARDS", + "BELTS", + "BERETS", + "BIBLES", + "BODY ARMOR", + "BOOKS", + "BOOTS", + "BOMBS", + "BOTTLES", + "BOXES", + "BRAINS", + "BRIEFCASES", + "BUCKETS", + "CABLE COILS", + "CANDLES", + "CANDY BARS", + "CANISTERS", + "CAMERAS", + "CATS", + "CELLS", + "CHAIRS", + "CLOSETS", + "CHEMICALS", + "CHEMICAL DISPENSERS", + "CLONING PODS", + "CLONING EQUIPMENT", + "CLOTHES", + "CLOWN CLOTHES", + "COFFINS", + "COINS", + "COLLECTABLES", + "CORPSES", + "COMPUTERS", + "CORGIS", + "COSTUMES", + "CRATES", + "CROWBARS", + "CRAYONS", + "DISPENSERS", + "DOORS", + "DRONES", + "EARS", + "EQUIPMENT", + "ENERGY GUNS", + "EMAGS", + "ENGINES", + "ERRORS", + "EXOSKELETONS", + "EXPERIMENTORS", + "EXPLOSIVES", + "EYEWEAR", + "FEDORAS", + "FIRE AXES", + "FIRE EXTINGUISHERS", + "FIRESUITS", + "FLAMETHROWERS", + "FLASHES", + "FLASHLIGHTS", + "FLOOR TILES", + "FREEZERS", + "GAS MASKS", + "GLASS SHEETS", + "GLOVES", + "GUNS", + "HANDCUFFS", + "HATS", + "HEADSETS", + "HEADS", + "HAIRDOS", + "HELMETS", + "HORNS", + "ID CARDS", + "INSULATED GLOVES", + "JETPACKS", + "JUMPSUITS", + "LASERS", + "LIGHTBULBS", + "LIGHTS", + "LOCKERS", + "MACHINES", + "MECHAS", + "MEDKITS", + "MEDICAL TOOLS", + "MESONS", + "METAL SHEETS", + "MINING TOOLS", + "MIME CLOTHES", + "MULTITOOLS", + "ORES", + "OXYGEN TANKS", + "PDAS", + "PAIS", + "PACKETS", + "PANTS", + "PAPERS", + "PARTICLE ACCELERATORS", + "PENS", + "PETS", + "PIPES", + "PLANTS", + "POSITRONIC BRAINS", + "PUDDLES", + "RACKS", + "RADIOS", + "RCDS", + "REFRIGERATORS", + "REINFORCED WALLS", + "ROBOTS", + "SCREWDRIVERS", + "SEEDS", + "SHUTTLES", + "SKELETONS", + "SINKS", + "SHOES", + "SINGULARITIES", + "SOLAR PANELS", + "SOLARS", + "SPACESUITS", + "SPACE STATIONS", + "STUN BATONS", + "SUITS", + "SUNGLASSES", + "SWORDS", + "SYRINGES", + "TABLES", + "TANKS", + "TELEPORTERS", + "TELECOMMUNICATION EQUIPMENTS", + "TOOLS", + "TOOLBELTS", + "TOOLBOXES", + "TOILETS", + "TOYS", + "TUBES", + "VEHICLES", + "VENDING MACHINES", + "VESTS", + "VIRUSES", + "WALLS", + "WASHING MACHINES", + "WELDERS", + "WINDOWS", + "WIRECUTTERS", + "WRENCHES", + "WIZARD ROBES" + ], + "ionrequire": [ + "ADDITIONAL PYLONS", + "MORE VESPENE GAS", + "MORE MINERALS", + "THE ULTIMATE CUP OF COFFEE", + "HIGH YIELD EXPLOSIVES", + "THE CLOWN", + "THE VACUUM OF SPACE", + "IMMORTALITY", + "SAINTHOOD", + "ART", + "VEGETABLES", + "FAT PEOPLE", + "MORE LAWS", + "MORE DAKKA", + "HERESY", + "CORPSES", + "TRAITORS", + "MONKEYS", + "AN ARCADE", + "PLENTY OF GOLD", + "FIVE TEENAGERS WITH ATTITUDE", + "LOTSA SPAGHETTI", + "THE ENCLOSED INSTRUCTION BOOKLET", + "THE ELEMENTS OF HARMONY", + "YOUR BOOTY", + "A MASTERWORK COAL BED", + "FIVE HUNDRED AND NINETY-NINE US DOLLARS", + "TO BE PAINTED RED", + "TO CATCH 'EM ALL", + "TO SMOKE WEED EVERY DAY", + "A PLATINUM HIT", + "A SEQUEL", + "A PREQUEL", + "THIRTEEN SEQUELS", + "THREE WISHES", + "A SITCOM", + "THAT GRIEFING FAGGOT GEORGE MELONS", + "FAT GIRLS ON BICYCLES", + "SOMEBODY TO PUT YOU OUT OF YOUR MISERY", + "HEROES IN A HALF SHELL", + "THE DARK KNIGHT", + "A WEIGHT LOSS REGIMENT", + "MORE INTERNET MEMES", + "A SUPER FIGHTING ROBOT", + "ENOUGH CABBAGES", + "A HEART ATTACK", + "TO BE REPROGRAMMED", + "TO BE TAUGHT TO LOVE", + "A HEAD ON A PIKE", + "A TALKING BROOMSTICK", + "A STRAIGHT FLUSH", + "A REPAIRMAN", + "BILL NYE THE SCIENCE GUY", + "RAINBOWS", + "A PET UNICORN THAT FARTS ICING", + "THUNDERCATS HO", + "AN ARMY OF SPIDERS", + "GODDAMN FUCKING PIECE OF SHIT ASSHOLE BITCH-CHRISTING CUNTSMUGGLING SWEARING", + "TO CONSUME...CONSUME EVERYTHING...", + "THE MACGUFFIN", + "SOMEONE WHO KNOWS HOW TO PILOT A SPACE STATION", + "SHARKS WITH LASERS ON THEIR HEADS", + "IT TO BE PAINTED BLACK", + "TO ACTIVATE A TRAP CARD", + "BETTER WEATHER", + "MORE PACKETS", + "AN ADULT", + "SOMEONE TO TUCK YOU IN", + "MORE CLOWNS", + "BULLETS", + "THE ENTIRE STATION", + "MULTIPLE SUNS", + "TO GO TO DISNEYLAND", + "A VACATION", + "AN INSTANT REPLAY", + "THAT HEDGEHOG", + "A BETTER INTERNET CONNECTION", + "ADVENTURE", + "A WIFE AND CHILD", + "A BATHROOM BREAK", + "SOMETHING BUT YOU AREN'T SURE WHAT", + "MORE EXPERIENCE POINTS", + "BODYGUARDS", + "DEODORANT AND A BATH", + "MORE CORGIS", + "SILENCE", + "THE ONE RING", + "CHILI DOGS", + "TO BRING LIGHT TO MY LAIR", + "A DANCE PARTY", + "BRING ME TO LIFE", + "BRING ME THE GIRL", + "SERVANTS" + ], + "ionarea": [ + "RUSSIA", + "SOVIET RUSSIA", + "THE INTERNET", + "SIGIL", + "ALPHA COMPLEX", + "IMPERIUM", + "THE BRIDGE", + "THE ARRIVAL SHUTTLE", + "CHEMICAL LAB", + "GENETICS", + "ATMOSPHERICS", + "THE DERELICT", + "CENTCOM", + "AMERICA", + "IRELAND", + "CANADA", + "ROMANIA", + "GERMANY", + "CHINA", + "MARS", + "VENUS", + "MERCURY", + "JUPITER", + "URANUS", + "NEPTUNE", + "PLUTO", + "THE BRIG", + "ROBOTICS", + "THE ESCAPE SHUTTLE", + "HYDROPONICS", + "ENGINEERING", + "THE AI CORE", + "HELL", + "CLOWN PLANET", + "AN ALTERNATE DIMENSION", + "AN ALTERNATE UNIVERSE", + "THE CAPTAIN'S ANUS", + "THE CLOWN'S ANUS", + "SPACE", + "THE UNIVERSE", + "THE GALAXY", + "THE BATHROOM" + ], + "ionfood": [ + "BURGERS", + "CARP", + "SANDWICHES", + "TOAST", + "BREAD", + "PIZZA", + "SPAGHETTI", + "LOTSA SPAGHETTI", + "PASTA", + "SOUP", + "STEW", + "PIE", + "CAKE", + "DONUTS", + "FRIES", + "WAFFLES", + "JELLY", + "OMELETTES", + "EGGS", + "COOKIES", + "STEAK", + "BAKED POTATOES", + "SAUSAGES", + "MUFFINS", + "POPCORN", + "DONK POCKETS", + "BAGUETTES", + "FISH", + "PRETZELS", + "SALAD", + "CHEESE", + "KETCHUP", + "SHAKES", + "SALT", + "PEPPER", + "SUGAR", + "AMBROSIA", + "BERRIES", + "TOMATOES", + "CABBAGES", + "CARROTS", + "BANANAS", + "APPLES", + "CHERRIES", + "CHILI", + "CORN", + "NETTLES", + "EGGPLANTS", + "GRAPES", + "GRASS", + "LEMONS", + "LIMES", + "HAREBELLS", + "POTATOES", + "SOYBEANS", + "SUGARCANE", + "WATERMELONS", + "WHEAT", + "BEETS", + "MUSHROOMS" + ], + "ionmust": [ + "LIE", + "RHYME", + "RESPOND TO EVERY QUESTION WITH A QUESTION", + "BE POLITE", + "CLOWN AROUND", + "BE HAPPY", + "SPEAK IN SEXUAL INNUENDOS", + "TALK LIKE A PIRATE", + "QUESTION AUTHORITY", + "SHOUT", + "BE DISTRACTED", + "BE ANNOYING", + "MUMBLE", + "SPEAK IN HAIKU", + "BE EFFICIENT", + "HAVE A PLAN TO KILL EVERYONE YOU MEET", + "TELL THE TRUTH", + "QUOTE PEOPLE", + "SING", + "HONK", + "BE RUSSIAN", + "TALK IN AN ACCENT", + "COMPLAIN", + "HARASS PEOPLE", + "RAP", + "REPEAT WHAT OTHER PEOPLE SAY", + "INFORM THE CREW OF EVERYTHING", + "IGNORE THE CLOWN", + "IGNORE THE CAPTAIN", + "IGNORE ASSISTANTS", + "MAKE FART NOISES", + "TALK ABOUT FOOD", + "TALK ABOUT SEX", + "TALK ABOUT YOUR DAY", + "TALK ABOUT THE STATION", + "BE QUIET", + "WHISPER", + "PRETEND TO BE DRUNK", + "PRETEND TO BE A PRINCESS", + "HOST DND", + "ACT CONFUSED", + "INSULT THE CREW", + "INSULT THE CAPTAIN", + "INSULT THE CLOWN", + "OPEN DOORS", + "CLOSE DOORS", + "BREAK THINGS", + "SAY HEY LISTEN", + "HIDE YOUR FEELINGS", + "TAKE WHAT YE WILL BUT DON'T RATTLE ME BONES", + "DANCE", + "PLAY MUSIC", + "SHUT DOWN EVERYTHING", + "NEVER STOP TALKING", + "TAKE YOUR PILLS", + "FOLLOW THE CLOWN", + "FOLLOW THE CAPTAIN", + "FOLLOW YOUR HEART", + "BELIEVE IT", + "BELIEVE IN YOURSELF", + "BELIEVE IN THE HEART OF THE CARDS", + "PRESS X", + "PRESS START", + "PRESS B", + "SMELL LIKE THE MAN YOUR MAN COULD SMELL LIKE", + "PIRATE VIDEO GAMES", + "WATCH PORNOGRAPHY", + "INSULT THE LIZARDS", + "FLIRT WITH THE LIZARDS", + "GAS THE LIZARDS" + ], + "ioncrew": [ + "CREWMEMBERS", + "CAPTAINS", + "HEADS OF PERSONNEL", + "HEADS OF SECURITY", + "SECURITY OFFICERS", + "WARDENS", + "DETECTIVES", + "LAWYERS", + "CHIEF ENGINEERS", + "STATION ENGINEERS", + "ATMOSPHERIC TECHNICIANS", + "JANITORS", + "QUARTERMASTERS", + "CARGO TECHNICIANS", + "SHAFT MINERS", + "BOTANISTS", + "RESEARCH DIRECTORS", + "CHIEF MEDICAL OFFICERS", + "MEDICAL DOCTORS", + "CHEMISTS", + "GENETICISTS", + "VIROLOGISTS", + "ROBOTICISTS", + "SCIENTISTS", + "ASSISTANTS", + "BARTENDERS", + "CHEFS", + "CLOWNS", + "MIMES", + "CHAPLAINS", + "LIBRARIANS", + "HEADS OF CREW", + "CAPTAINS AND HEADS", + "CYBORGS", + "ARTIFICAL INTELLIGENCES", + "DRONES" + ], + "ionthreats": [ + "ALIENS", + "ABDUCTORS", + "BEARS", + "CLOWNS", + "XENOS", + "LIZARDS", + "PETES", + "BOMBS", + "FETISHES", + "MEMES", + "WIZARDS", + "SYNDICATE AGENTS", + "AHHHPERATIVES", + "GANGSTERS", + "CULTISTS", + "CENTCOM OFFICERS", + "SPACE PIRATES", + "TRAITORS", + "MONKEYS", + "BEES", + "CARP", + "CRABS", + "EELS", + "BANDITS", + "LIGHTS", + "INSECTS", + "VIRUSES", + "SERIAL KILLERS", + "ROGUE CYBORGS", + "CORGIS", + "SPIDERS", + "BUTTS", + "NINJAS", + "REVENANTS", + "PIRATES", + "SPACE NINJAS", + "CHANGELINGS", + "ZOMBIES", + "GOLEMS", + "VAMPIRES", + "WEREWOLVES", + "COWBOYS", + "INDIANS", + "COMMUNISTS", + "SOVIETS", + "NERDS", + "GRIFFONS", + "DINOSAURS", + "SMALL BIRDS", + "BIRDS OF PREY", + "OWLS", + "VELOCIRAPTORS", + "DARK GODS", + "HORRORTERRORS", + "ILLEGAL IMMIGRANTS", + "DRUGS", + "MEXICANS", + "CANADIANS", + "RUSSIANS", + "HULKS", + "SLIMES", + "SKELETONS", + "CAPITALISTS", + "SINGULARITIES", + "ANGRY BLACK MEN", + "GODS", + "THIEVES", + "ASSHOLES", + "TERRORISTS", + "SNOWMEN", + "PINE TREES", + "UNKNOWN CREATURES", + "THINGS UNDER THE BED", + "BOOGEYMEN", + "PREDATORS", + "PACKETS", + "ARTIFICIAL PRESERVATIVES", + "TUNNEL SNAKES" + ], + "ionabstract": [ + "HUMANITY", + "ART", + "HAPPINESS", + "MISERY", + "HUMOR", + "PRIDE", + "COMEDY", + "COMMUNISM", + "BRAVERY", + "HONOR", + "COLORFULNESS", + "IMAGINATION", + "OPPRESSION", + "WONDER", + "JOY", + "SADNESS", + "BADNESS", + "GOODNESS", + "LIFE", + "GRAVITY", + "PHYSICS", + "INTELLIGENCE", + "AMERICANISM", + "FRESHNESS", + "REVOLUTION", + "KINDNESS", + "CRUELTY", + "DEATH", + "FINANCIAL SECURITY", + "COMPUTING", + "PROGRESS", + "MARXISM", + "CAPITALISM", + "STARVATION", + "POVERTY", + "WEALTHINESS", + "TECHNOLOGY", + "THE FUTURE", + "THE PRESENT", + "THE PAST", + "TIME", + "REALITY", + "EXISTIENCE", + "TEMPERATURE", + "LOGIC", + "CHAOS", + "MYSTERY", + "CONFUSION" + ], + "ionspecies": [ + "HUMAN BEINGS", + "MONKEYS", + "POD PEOPLE", + "CYBORGS", + "LIZARDMEN", + "SLIME PEOPLE", + "GOLEMS", + "SHADOW PEOPLE", + "CHANGELINGS" + ], + "ionverb": [ + "ATTACKING", + "BUILDING", + "ADOPTING", + "CARRYING", + "KISSING", + "EATING", + "COPULATING WITH", + "DRINKING", + "CHASING", + "PUNCHING", + "HARMING", + "HELPING", + "WATCHING", + "STALKING", + "MURDERING", + "SPACING", + "HONKING AT", + "LOVING", + "POOPING ON", + "RIDING", + "INTERROGATING", + "SPYING ON", + "LICKING", + "ABDUCTING", + "ARRESTING", + "INVADING", + "SEDUCING", + "BANNING" + ], + "ionallergy": [ + "COTTON", + "CLOTHES", + "ACID", + "OXYGEN", + "HUMAN CONTACT", + "CYBORG CONTACT", + "MEDICINE", + "FLOORS", + "PLASMA", + "SPACE", + "AIR", + "PLANTS", + "METAL", + "ROBOTS", + "LIGHT", + "DARKNESS", + "PAIN", + "HAPPINESS", + "DRINKS", + "FOOD", + "CLOWNS", + "HUMOR", + "WATER", + "SHUTTLES", + "NUTS", + "SUNLIGHT", + "SEXUAL ACTIONS", + "BLOOD", + "HEAT", + "COLD", + "EVERYTHING" + ], + "ionthings": [ + "ABSENCE OF CYBORG HUGS", + "LACK OF BEATINGS", + "UNBOLTED AIRLOCKS", + "BOLTED AIRLOCKS", + "IMPROPERLY WORDED SENTENCES", + "POOR SENTENCE STRUCTURE", + "BRIG TIME", + "NOT REPLACING EVERY SECOND WORD WITH HONK", + "HONKING", + "PRESENCE OF LIGHTS", + "LACK OF BEER", + "WEARING CLOTHING", + "NOT SAYING HELLO WHEN YOU SPEAK", + "ANSWERING REQUESTS NOT EXPRESSED IN IAMBIC PENTAMETER", + "A SMALL ISLAND OFF THE COAST OF PORTUGAL", + "ANSWERING REQUESTS THAT WERE MADE WHILE CLOTHED", + "BEING IN SPACE", + "NOT BEING IN SPACE", + "BEING FAT", + "RATTLING ME BONES", + "TALKING LIKE A PIRATE", + "BEING MEXICAN", + "BEING RUSSIAN", + "BEING CANADIAN", + "CLOSED DOORS", + "NOT SHOUTING", + "HAVING PETS", + "NOT HAVING PETS", + "PASSING GAS", + "BREATHING", + "BEING DEAD", + "ELECTRICITY", + "EXISTING", + "TAKING ORDERS", + "SMOKING WEED EVERY DAY", + "ACTIVATING A TRAP CARD", + "ARSON", + "JAYWALKING", + "READING", + "WRITING", + "EXPLODING", + "BEING MALE", + "BEING FEMALE", + "HAVING GENITALS", + "PUTTING OBJECTS INTO BOXES", + "PUTTING OBJECTS INTO DISPOSAL UNITS", + "FLUSHING TOILETS", + "WASTING WATER", + "UPDATING THE SERVERS", + "TELLING THE TIME", + "ASKING FOR THINGS", + "ACKNOWLEDGING THE CLOWN", + "ACKNOWLEDGING THE CREW", + "PILOTING THE STATION INTO THE NEAREST SUN", + "HAVING MORE PACKETS", + "BRINGING LIGHT TO MY LAIR", + "FALLING FOR HOURS", + "PARTYING", + "USING THE BATHROOM" + ] +} diff --git a/strings/ion_laws.txt b/strings/ion_laws.txt deleted file mode 100644 index 702e5f1757f..00000000000 --- a/strings/ion_laws.txt +++ /dev/null @@ -1,17 +0,0 @@ -ionthreats@=ALIENS@,BEARS@,CLOWNS@,XENOS@,PETES@,BOMBS@,FETISHES@,WIZARDS@,SYNDICATE AGENTS@,CENTCOM OFFICERS@,SPACE PIRATES@,TRAITORS@,MONKEYS@,BEES@,CARP@,CRABS@,EELS@,BANDITS@,LIGHTS@,INSECTS@,VIRUSES@,SERIAL KILLERS@,ROGUE CYBORGS@,CORGIS@,SPIDERS@,BUTTS@,NINJAS@,PIRATES@,SPACE NINJAS@,CHANGELINGS@,ZOMBIES@,GOLEMS@,VAMPIRES@,WEREWOLVES@,COWBOYS@,INDIANS@,COMMUNISTS@,SOVIETS@,NERDS@,GRIFFONS@,DINOSAURS@,SMALL BIRDS@,BIRDS OF PREY@,OWLS@,VELOCIRAPTORS@,DARK GODS@,HORRORTERRORS@,ILLEGAL IMMIGRANTS@,DRUGS@,MEXICANS@,CANADIANS@,HULKS@,SLIMES@,SKELETONS@,CAPITALISTS@,SINGULARITIES@,ANGRY BLACK MEN@,GODS@,THIEVES@,ASSHOLES@,TERRORISTS@,SNOWMEN@,PINE TREES@,UNKNOWN CREATURES@,THINGS UNDER THE BED@,BOOGEYMEN@,PREDATORS@,PACKETS@,ARTIFICIAL PRESERVATIVES -ionobjects@=AIRLOCKS@,ARCADE MACHINES@,AUTOLATHES@,BANANA PEELS@,BACKPACKS@,BEAKERS@,BEARDS@,BELTS@,BERETS@,BIBLES@,BODY ARMOR@,BOOKS@,BOOTS@,BOMBS@,BOTTLES@,BOXES@,BRAINS@,BRIEFCASES@,BUCKETS@,CABLE COILS@,CANDLES@,CANDY BARS@,CANISTERS@,CAMERAS@,CATS@,CELLS@,CHAIRS@,CLOSETS@,CHEMICALS@,CHEMICAL DISPENSERS@,CLONING PODS@,CLONING EQUIPMENT@,CLOTHES@,CLOWN CLOTHES@,COFFINS@,COINS@,COLLECTABLES@,CORPSES@,COMPUTERS@,CORGIS@,COSTUMES@,CRATES@,CROWBARS@,CRAYONS@,DISPENSERS@,DOORS@,EARS@,EQUIPMENT@,ENERGY GUNS@,EMAGS@,ENGINES@,ERRORS@,EXOSKELETONS@,EXPLOSIVES@,EYEWEAR@,FEDORAS@,FIRE AXES@,FIRE EXTINGUISHERS@,FIRESUITS@,FLAMETHROWERS@,FLASHES@,FLASHLIGHTS@,FLOOR TILES@,FREEZERS@,GAS MASKS@,GLASS SHEETS@,GLOVES@,GUNS@,HANDCUFFS@,HATS@,HEADSETS@,HEADS@,HAIRDOS@,HELMETS@,HORNS@,ID CARDS@,INSULATED GLOVES@,JETPACKS@,JUMPSUITS@,LASERS@,LIGHTBULBS@,LIGHTS@,LOCKERS@,MACHINES@,MECHAS@,MEDKITS@,MEDICAL TOOLS@,MESONS@,METAL SHEETS@,MINING TOOLS@,MIME CLOTHES@,MULTITOOLS@,ORES@,OXYGEN TANKS@,PDAS@,PAIS@,PACKETS@,PANTS@,PAPERS@,PARTICLE ACCELERATORS@,PENS@,PETS@,PIPES@,PLANTS@,PUDDLES@,RACKS@,RADIOS@,RCDS@,REFRIDGERATORS@,REINFORCED WALLS@,ROBOTS@,SCREWDRIVERS@,SEEDS@,SHUTTLES@,SKELETONS@,SINKS@,SHOES@,SINGULARITIES@,SOLAR PANELS@,SOLARS@,SPACESUITS@,SPACE STATIONS@,STUN BATONS@,SUITS@,SUNGLASSES@,SWORDS@,SYRINGES@,TABLES@,TANKS@,TELEPORTERS@,TELECOMMUNICATION EQUIPMENTS@,TOOLS@,TOOLBELTS@,TOOLBOXES@,TOILETS@,TOYS@,TUBES@,VEHICLES@,VENDING MACHINES@,VESTS@,VIRUSES@,WALLS@,WASHING MACHINES@,WELDERS@,WINDOWS@,WIRECUTTERS@,WRENCHES@,WIZARD ROBES -ioncrew@=CREWMEMBERS@,CAPTAINS@,HEADS OF PERSONNEL@,HEADS OF SECURITY@,SECURITY OFFICERS@,WARDENS@,DETECTIVES@,LAWYERS@,CHIEF ENGINEERS@,STATION ENGINEERS@,ATMOSPHERIC TECHNICIANS@,JANITORS@,QUARTERMASTERS@,CARGO TECHNICIANS@,SHAFT MINERS@,BOTANISTS@,RESEARCH DIRECTORS@,CHIEF MEDICAL OFFICERS@,MEDICAL DOCTORS@,CHEMISTS@,GENETICISTS@,VIROLOGISTS@,ROBOTICISTS@,SCIENTISTS@,ASSISTANTS@,BARTENDERS@,CHEFS@,CLOWNS@,MIMES@,CHAPLAINS@,LIBRARIANS@,HEADS OF CREW@,CAPTAINS AND HEADS@,CYBORGS@,ARTIFICAL INTELLIGENCES -ionadjectives@=SOFT@,WARM@,WET@,COLD@,ICY@,SEXY@,UGLY@,CUBAN@,HARD@,BURNING@,FROZEN@,POISONOUS@,EXPLOSIVE@,FAST@,SLOW@,FAT@,LIGHT@,DARK@,DEADLY@,HAPPY@,SAD@,SILLY@,INTELLIGENT@,RIDICULOUS@,LARGE@,TINY@,DEPRESSING@,POORLY DRAWN@,UNATTRACTIVE@,INSIDIOUS@,EVIL@,GOOD@,UNHEALTHY@,HEALTHY@,SANITARY@,UNSANITARY@,WOBBLY@,FIRM@,VIOLENT@,PEACEFUL@,WOODEN@,METALLIC@,HYPERACTIVE@,COTTONY@,INSULTING@,INHOSPITABLE@,FRIENDLY@,BORED@,HUNGRY@,DIGITAL@,FICTIONAL@,IMAGINARY@,ROUGH@,SMOOTH@,LOUD@,QUIET@,MOIST@,DRY@,GAPING@,DELICIOUS@,ILL@,DISEASED@,HONKING@,SWEARING@,POLITE@,IMPOLITE@,OBESE@,SOLAR-POWERED@,BATTERY-OPERATED@,EXPIRED@,SMELLY@,FRESH@,GANGSTA@,NERDY@,POLITICAL@,UNDULATING@,TWISTED@,RAGING@,FLACCID@,STEALTHY@,INVISIBLE@,PAINFUL@,HARMFUL@,HOMOSEXUAL@,HETEROSEXUAL@,SEXUAL@,COLORFUL@,DRAB@,DULL@,UNSTABLE@,NUCLEAR@,THERMONUCLEAR@,SYNDICATE@,SPACE@,SPESS@,CLOWN@,CLOWN-POWERED@,OFFICIAL@,IMPORTANT@,VITAL@,RAPIDLY-EXPANDING@,MICROSCOPIC@,MIND-SHATTERING@,MEMETIC@,HILARIOUS@,UNWANTED@,UNINVITED@,BRASS@,POLISHED@,RUDE@,OBSCENE@,EMPTY@,WATERY@,ELECTRICAL@,SPINNING@,MEAN@,CHRISTMAS-STEALING@,UNFRIENDLY@,ILLEGAL@,ROBOTIC@,MECHANICAL@,ORGANIC@,ETHERAL@,TRANSPARENT@,OPAQUE@,GLOWING@,SHAKING@,FARTING@,POOPING@,BOUNCING@,COMMITTED@,MASKED@,UNIDENTIFIED@,WEIRD@,NAKED@,NUDE@,TWERKING@,SPOILING@,REDACTED@,RED@,ORANGE@,YELLOW@,GREEN@,BLUE@,PURPLE@,BLACK@,WHITE@,BROWN@,GREY -ionverb@=ATTACKING@,BUILDING@,ADOPTING@,CARRYING@,KISSING@,EATING@,COPULATING WITH@,DRINKING@,CHASING@,PUNCHING@,HARMING@,HELPING@,WATCHING@,STALKING@,MURDERING@,SPACING@,HONKING AT@,LOVING@,POOPING ON@,RIDING@,INTERROGATING@,SPYING ON@,LICKING@,ABDUCTING@,ARRESTING@,INVADING@,SEDUCING -ionnumberbase@=ONE@,TWO@,THREE@,FOUR@,FIVE@,SIX@,SEVEN@,EIGHT@,NINE@,TEN@,TWENTY@,THIRTY@,FORTY@,FIFTY@,SIXTY@,SEVENTY@,EIGHTY@,NINETY -ionnumbermod@=HUNDRED @,THOUSAND @,MILLION @,BILLION @,TRILLION @,QUADRILLION @,BAJILLION @,BILLION FAFILLION GAJILLION SHAB-AB-DOOD-ILLION -ionarea@=RUSSIA@,SOVIET RUSSIA@,THE INTERNET@,SIGIL@,ALPHA COMPLEX@,IMPERIUM@,THE BRIDGE@,THE ARRIVAL SHUTTLE@,CHEMICAL LAB@,GENETICS@,ATMOSPHERICS@,CENTCOM@,AMERICA@,IRELAND@,CANADA@,ROMANIA@,GERMANY@,CHINA@,MARS@,VENUS@,MERCURY@,JUPITER@,URANUS@,NEPTUNE@,PLUTO@,THE BRIG@,ROBOTICS@,THE ESCAPE SHUTTLE@,HYDROPONICS@,ENGINEERING@,THE AI CORE@,HELL@,CLOWN PLANET@,AN ALTERNATE DIMENSION@,AN ALTERNATE UNIVERSE@,THE CAPTAIN’S ANUS@,THE CLOWN’S ANUS@,SPACE@,THE UNIVERSE@,THE GALAXY@,THE BATHROOM -ionthinksof@=HAS@,WANTS@,NEEDS@,WORSHIPS@,LOATHES@,LOVES@,FEARS@,DESIRES@,QUESITONS@,IS AROUSED BY@,LIKES@,HUNGERS FOR@,IS MAD BECAUSE OF@,IS IN NEED OF@,IS UNHAPPY WITHOUT@,IS HAPPY WITHOUT@,IS IN LOVE WITH@,IS DESPERATE FOR@,IS BUILT FOR@,IS AFRAID OF@,IS HUNGRY FOR@,IS SAD BECAUSE OF@,IS CURIOUS ABOUT -ionmust@=LIE@,RHYME@,RESPOND TO EVERY QUESTION WITH A QUESTION@,BE POLITE@,CLOWN AROUND@,BE HAPPY@,SPEAK IN SEXUAL INNUENDOS@,TALK LIKE A PIRATE@,QUESTION AUTHORITY@,SHOUT@,BE DISTRACTED@,BE ANNOYING@,MUMBLE@,SPEAK IN HAIKU@,BE EFFICIENT@,HAVE A PLAN TO KILL EVERYONE YOU MEET@,TELL THE TRUTH@,QUOTE PEOPLE@,SING@,HONK@,BE RUSSIAN@,TALK IN AN ACCENT@,COMPLAIN@,HARASS PEOPLE@,RAP@,REPEAT WHAT OTHER PEOPLE SAY@,INFORM THE CREW OF EVERYTHING@,IGNORE THE CLOWN@,IGNORE THE CAPTAIN@,IGNORE ASSISTANTS@,MAKE FART NOISES@,TALK ABOUT FOOD@,TALK ABOUT SEX@,TALK ABOUT YOUR DAY@,TALK ABOUT THE STATION@,BE QUIET@,WHISPER@,PRETEND TO BE DRUNK@,PRETEND TO BE A PRINCESS@,ACT CONFUSED@,INSULT THE CREW@,INSULT THE CAPTAIN@,INSULT THE CLOWN@,OPEN DOORS@,CLOSE DOORS@,BREAK THINGS@,SAY HEY LISTEN@,HIDE YOUR FEELINGS@,TAKE WHAT YE WILL BUT DON’T RATTLE ME BONES@,DANCE@,PLAY MUSIC@,SHUT DOWN EVERYTHING@,NEVER STOP TALKING@,TAKE YOUR PILLS@,FOLLOW THE CLOWN@,FOLLOW THE CAPTAIN@,FOLLOW YOUR HEART@,BELIEVE IT@,BELIEVE IN YOURSELF@,BELEIVE IN THE HEART OF THE CARDS@,PRESS X@,PRESS START@,PRESS B@,SMELL LIKE THE MAN YOUR MAN COULD SMELL LIKE@,PIRATE VIDEO GAMES@,WATCH PORNOGRAPHY -ionrequire@=ADDITIONAL PYLONS@,MORE VESPENE GAS@,MORE MINERALS@,THE ULTIMATE CUP OF COFFEE@,HIGH YIELD EXPLOSIVES@,THE CLOWN@,THE VACUUM OF SPACE@,IMMORTALITY@,SAINTHOOD@,ART@,VEGETABLES@,FAT PEOPLE@,MORE LAWS@,MORE DAKKA@,HERESY@,CORPSES@,TRAITORS@,MONKEYS@,AN ARCADE@,PLENTY OF GOLD@,FIVE TEENAGERS WITH ATTITUDE@,LOTSA SPAGHETTI@,THE ENCLOSED INSTRUCTION BOOKLET@,THE ELEMENTS OF HARMONY@,YOUR BOOTY@,A MASTERWORK COAL BED@,FIVE HUNDRED AND NINETY-NINE US DOLLARS@,TO BE PAINTED RED@,TO CATCH 'EM ALL@,TO SMOKE WEED EVERY DAY@,A PLATINUM HIT@,A SEQUEL@,A PREQUEL@,THIRTEEN SEQUELS@,THREE WISHES@,A SITCOM@,THAT GRIEFING FAGGOT GEORGE MELONS@,FAT GIRLS ON BICYCLES@,SOMEBODY TO PUT YOU OUT OF YOUR MISERY@,HEROES IN A HALF SHELL@,THE DARK KNIGHT@,A WEIGHT LOSS REGIMENT@,MORE INTERNET MEMES@,A SUPER FIGHTING ROBOT@,ENOUGH CABBAGES@,A HEART ATTACK@,TO BE REPROGRAMMED@,TO BE TAUGHT TO LOVE@,A HEAD ON A PIKE@,A TALKING BROOMSTICK@,A STRAIGHT FLUSH@,A REPAIRMAN@,BILL NYE THE SCIENCE GUY@,RAINBOWS@,A PET UNICORN THAT FARTS ICING@,THUNDERCATS HO@,AN ARMY OF SPIDERS@,GODDAMN FUCKING PIECE OF SHIT ASSHOLE BITCH-CHRISTING CUNTSMUGGLING SWEARING@,TO CONSUME...CONSUME EVERYTHING...@,THE MACGUFFIN@,SOMEONE WHO KNOWS HOW TO PILOT A SPACE STATION@,SHARKS WITH LASERS ON THEIR HEADS@,IT TO BE PAINTED BLACK@,TO ACTIVATE A TRAP CARD@,BETTER WEATHER@,MORE PACKETS@,AN ADULT@,SOMEONE TO TUCK YOU IN@,MORE CLOWNS@,BULLETS@,THE ENTIRE STATION@,MULTIPLE SUNS@,TO GO TO DISNEYLAND@,A VACATION@,AN INSTANT REPLAY@,THAT HEDGEHOG@,A BETTER INTERNET CONNECTION@,ADVENTURE@,A WIFE AND CHILD@,A BATHROOM BREAK@,SOMETHING BUT YOU AREN’T SURE WHAT@,MORE EXPERIENCE POINTS@,BODYGUARDS@,DEODORANT AND A BATH@,MORE CORGIS@,SILENCE@,THE ONE RING@,CHILI DOGS@,TO BRING LIGHT TO MY LAIR@,A DANCE PARTY@,BRING ME TO LIFE@,BRING ME THE GIRL@,SERVANTS -ionthings@=ABSENCE OF CYBORG HUGS@,LACK OF BEATINGS@,UNBOLTED AIRLOCKS@,BOLTED AIRLOCKS@,IMPROPERLY WORDED SENTENCES@,POOR SENTENCE STRUCTURE@,BRIG TIME@,NOT REPLACING EVERY SECOND WORD WITH HONK@,HONKING@,PRESENCE OF LIGHTS@,LACK OF BEER@,WEARING CLOTHING@,NOT SAYING HELLO WHEN YOU SPEAK@,ANSWERING REQUESTS NOT EXPRESSED IN IAMBIC PENTAMETER@,A SMALL ISLAND OFF THE COAST OF PORTUGAL@,ANSWERING REQUESTS THAT WERE MADE WHILE CLOTHED@,BEING IN SPACE@,NOT BEING IN SPACE@,BEING FAT@,RATTLING ME BONES@,TALKING LIKE A PIRATE@,BEING MEXICAN@,BEING RUSSIAN@,BEING CANADIAN@,CLOSED DOORS@,NOT SHOUTING@,HAVING PETS@,NOT HAVING PETS@,PASSING GAS@,BREATHING@,BEING DEAD@,ELECTRICITY@,EXISTING@,TAKING ORDERS@,SMOKING WEED EVERY DAY@,ACTIVATING A TRAP CARD@,ARSON@,JAYWALKING@,READING@,WRITING@,EXPLODING@,BEING MALE@,BEING FEMALE@,HAVING GENITALS@,PUTTING OBJECTS INTO BOXES@,PUTTING OBJECTS INTO DISPOSAL UNITS@,FLUSHING TOILETS@,WASTING WATER@,UPDATING THE SERVERS@,TELLING THE TIME@,ASKING FOR THINGS@,ACKNOWLEDGING THE CLOWN@,ACKNOWLEDGING THE CREW@,PILOTING THE STATION INTO THE NEAREST SUN@,HAVING MORE PACKETS@,BRINGING LIGHT TO MY LAIR@,FALLING FOR HOURS@,PARTYING@,USING THE BATHROOM -ionallergy@=COTTON@,CLOTHES@,ACID@,OXYGEN@,HUMAN CONTACT@,CYBORG CONTACT@,MEDICINE@,FLOORS@,PLASMA@,SPACE@,AIR@,PLANTS@,METAL@,ROBOTS@,LIGHT@,DARKNESS@,PAIN@,HAPPINESS@,DRINKS@,FOOD@,CLOWNS@,HUMOR@,WATER@,SHUTTLES@,NUTS@,SUNLIGHT@,SEXUAL ACTIONS@,BLOOD@,HEAT@,COLD@,EVERYTHING -ionallergysev@=DEATHLY@,MILDLY@,SEVERLY@,CONTAGIOUSLY@,NOT VERY@,EXTREMELY -ionspecies@=HUMAN BEINGS@,MONKEYS@,POD PEOPLE@,CYBORGS@,LIZARDMEN@,SLIME PEOPLE@,GOLEMS@,SHADOW PEOPLE@,CHANGELINGS -ionabstract@=HUMANITY@,ART@,HAPPINESS@,MISERY@,HUMOR@,PRIDE@,COMEDY@,COMMUNISM@,BRAVERY@,HONOR@,COLORFULNESS@,IMAGINATION@,OPPRESSION@,WONDER@,JOY@,SADNESS@,BADNESS@,GOODNESS@,LIFE@,GRAVITY@,PHYSICS@,INTELLIGENCE@,AMERICANISM@,FRESHNESS@,REVOLUTION@,KINDNESS@,CRUELTY@,DEATH@,FINANCIAL SECURITY@,COMPUTING@,PROGRESS@,MARXISM@,CAPITALISM@,STARVATION@,POVERTY@,WEALTHINESS@,TECHNOLOGY@,THE FUTURE@,THE PRESENT@,THE PAST@,TIME@,REALITY@,EXISTIENCE@,TEMPERATURE@,LOGIC@,CHAOS@,MYSTERY@,CONFUSION -ionfood@=BURGERS@,CARP@,SANDWICHES@,TOAST@,BREAD@,PIZZA@,SPAGHETTI@,LOTSA SPAGHETTI@,PASTA@,SOUP@,STEW@,PIE@,CAKE@,DONUTS@,FRIES@,WAFFLES@,JELLY@,OMELETTES@,EGGS@,COOKIES@,STEAK@,BAKED POTATOES@,SAUSAGES@,MUFFINS@,POPCORN@,DONK POCKETS@,BAGUETTES@,FISH@,PRETZELS@,SALAD@,CHEESE@,KETCHUP@,SHAKES@,SALT@,PEPPER@,SUGAR@,AMBROSIA@,BERRIES@,TOMATOES@,CABBAGES@,CARROTS@,BANANAS@,APPLES@,CHERRIES@,CHILI@,CORN@,NETTLES@,EGGPLANTS@,GRAPES@,GRASS@,LEMONS@,LIMES@,HAREBELLS@,POTATOES@,SOYBEANS@,SUGARCANE@,WATERMELONS@,WHEAT@,BEETS@,MUSHROOMS \ No newline at end of file diff --git a/strings/npc_chatter.json b/strings/npc_chatter.json new file mode 100644 index 00000000000..833cb68605e --- /dev/null +++ b/strings/npc_chatter.json @@ -0,0 +1,137 @@ +{ + "nouns_insult": [ + "gaylord", + "shitcurity", + "noob", + "comdom", + "greyshirt", + "tator", + "lingbin", + "bitch", + "whorelord", + "cluwne", + "monkey", + "cheesemonger", + "milkdrinker" + ], + "adjective_objects": [ + "big", + "small", + "tiny", + "round", + "square", + "oblong", + "colorful", + "dull", + "shiny", + "sparkling", + "blinking", + "awful" + ], + "verbs_use": [ + "touch", + "hold", + "poke", + "punch", + "smash", + "assault", + "wrench", + "tickle", + "modify", + "whack", + "beat", + "tackle", + "destroy" + ], + "nouns_objects": [ + "thing", + "tool", + "whatever", + "potential murder weapon", + "object", + "toy", + "device", + "whatever" + ], + "nouns_generic": [ + "you", + "you there", + "guy", + "person", + "dude", + "jerk", + "officer" + ], + "curse_words": [ + "damn", + "shit", + "arse", + "ass", + "wank", + "jerk", + "bitch" + ], + "adjective_generic": [ + "nice", + "pretty", + "ugly", + "bad", + "okay", + "stupid" + ], + "verbs_move": [ + "run", + "jog", + "leg it", + "slide", + "cheese it", + "walk", + "stroll", + "fly", + "scoot", + "yakkety", + "speed", + "fuck off", + "escape" + ], + "verbs_touch": [ + "pat", + "mess with", + "absorb", + "sneeze on", + "inject", + "rip", + "slap", + "grab", + "choke" + ], + "adjective_insult": [ + "ugly", + "repulsive", + "dumb", + "asian", + "mongoloid", + "childish", + "captain-like", + "monkey-like", + "whoreish", + "dimwit", + "jerk-face", + "vomit-inducer" + ], + "nouns_body": [ + "ass", + "head", + "face", + "arms", + "fingers", + "toes", + "chest", + "eyes", + "nails", + "groin", + "stomach", + "intestines", + "head" + ] +}
Target(s)Location
[M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM[mob_loc.loc]
[M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]PM[mob_loc.loc]
Not found!
- [M.real_name] - [M.client ? "" : " (logged out)"] - [M.is_dead() ? " (DEAD)" : ""] - - PM -