mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-22 03:27:46 +01:00
Paicontroller subsystem port (#19165)
* to subsystem * wip subsystem pain * some big recoding * seperated * oop * fucking lua * working on tgui foundation * compile fixes * nice and clean * type * inviting works * unit testing * agony * test good * typo * that should be the basics * Complete recode of pai saving * restore fluff text * migration * pai notification automation * swap * length * more fixes * pai sprite datums * type 13 uses all four directions * eye handling * use accessors * working on icontests * fix * hologram eye test * oop * wait that doesn't work at all * text * eye colors and holo eyes * lots of fixes * fix those up * organized * belly cap * that too * death sprites * resting eyes * resting flag * bad check here * fixing icon files * proper file * garble * fix some states * pai death * collapse pai files into single file * allow some restricted pai interactions * lamp * block invites button * collapse in remaining vr file * this is actually needed * documenting and organizing * update to TGprefs * remove gun toggle verb on pai * forbid default name joins * pai cable retraction * various pai fixes * icon forge pai * pai eyes * type 13 iconstate * directionless * small fix * pai dir fix * missing icon * no fresh pais * oops * fixed pai join * fixed change chassis * pai UI * . * . * reuse * . * . * . * push that * Horro rmasks * pai invite data change * splitting * use refs * initial * with PAI * soem more * . * . * . * . * ugh * next * pai invite * only preview * that * use from char pref * fixed pai invite not working * small tgui fixes * remove temp code * . * pai comments properly sent * ooc * horror * need to use this for now * . * fix * lol * raw mob spawn fix * pai software * push --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
@@ -150,13 +150,9 @@
|
||||
pai.key = choice.key
|
||||
card.setPersonality(pai)
|
||||
if(tgui_alert(pai, "Do you want to load your pAI data?", "Load", list("Yes", "No")) == "Yes")
|
||||
pai.savefile_load(pai)
|
||||
pai.apply_preferences(pai.client)
|
||||
else
|
||||
pai.name = sanitizeSafe(tgui_input_text(pai, "Enter your pAI name:", "pAI Name", "Personal AI", encode = FALSE))
|
||||
card.setPersonality(pai)
|
||||
for(var/datum/paiCandidate/candidate in paiController.pai_candidates)
|
||||
if(candidate.key == choice.key)
|
||||
paiController.pai_candidates.Remove(candidate)
|
||||
log_admin("made a pAI with key=[pai.key] at ([T.x],[T.y],[T.z])")
|
||||
feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/datum/asset/spritesheet_batched/pai_icons
|
||||
name = "pai_icons"
|
||||
|
||||
/datum/asset/spritesheet_batched/pai_icons/create_spritesheets()
|
||||
for(var/name, current_sprite in SSpai.get_chassis_list())
|
||||
var/datum/pai_sprite/sprite = current_sprite
|
||||
if(!sprite.name || !sprite.sprite_icon_state)
|
||||
continue
|
||||
|
||||
var/datum/universal_icon/I_N = uni_icon(sprite.sprite_icon, sprite.sprite_icon_state, NORTH)
|
||||
var/datum/universal_icon/I_S = uni_icon(sprite.sprite_icon, sprite.sprite_icon_state, SOUTH)
|
||||
var/datum/universal_icon/I_W = uni_icon(sprite.sprite_icon, sprite.sprite_icon_state, WEST)
|
||||
var/datum/universal_icon/I_E = uni_icon(sprite.sprite_icon, sprite.sprite_icon_state, EAST)
|
||||
|
||||
if(sprite.icon_x > sprite.icon_y)
|
||||
var/buffer = (sprite.icon_x - sprite.icon_y) / 2
|
||||
I_N.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_S.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_W.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_E.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
else if (sprite.icon_x < sprite.icon_y)
|
||||
var/buffer = (sprite.icon_y - sprite.icon_x) / 2
|
||||
I_N.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_S.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_W.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_E.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_N.scale(120, 120)
|
||||
I_S.scale(120, 120)
|
||||
I_W.scale(120, 120)
|
||||
I_E.scale(120, 120)
|
||||
|
||||
var/imgid = sanitize_css_class_name("[sprite.type]")
|
||||
insert_icon(imgid + "N", I_N)
|
||||
insert_icon(imgid + "S", I_S)
|
||||
insert_icon(imgid + "W", I_W)
|
||||
insert_icon(imgid + "E", I_E)
|
||||
|
||||
if(sprite.has_eye_sprites)
|
||||
var/datum/universal_icon/I_NE = uni_icon(sprite.sprite_icon, "[sprite.sprite_icon_state]-eyes", NORTH)
|
||||
var/datum/universal_icon/I_SE = uni_icon(sprite.sprite_icon, "[sprite.sprite_icon_state]-eyes", SOUTH)
|
||||
var/datum/universal_icon/I_WE = uni_icon(sprite.sprite_icon, "[sprite.sprite_icon_state]-eyes", WEST)
|
||||
var/datum/universal_icon/I_EE = uni_icon(sprite.sprite_icon, "[sprite.sprite_icon_state]-eyes", EAST)
|
||||
|
||||
if(sprite.icon_x > sprite.icon_y)
|
||||
var/buffer = (sprite.icon_x - sprite.icon_y) / 2
|
||||
I_NE.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_SE.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_WE.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
I_EE.crop(0, -buffer, sprite.icon_x, sprite.icon_y + buffer)
|
||||
else if (sprite.icon_x < sprite.icon_y)
|
||||
var/buffer = (sprite.icon_y - sprite.icon_x) / 2
|
||||
I_NE.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_SE.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_WE.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_EE.crop(-buffer, 0, sprite.icon_x + buffer, sprite.icon_y)
|
||||
I_NE.scale(120, 120)
|
||||
I_SE.scale(120, 120)
|
||||
I_WE.scale(120, 120)
|
||||
I_EE.scale(120, 120)
|
||||
|
||||
insert_icon(imgid + "NE", I_NE)
|
||||
insert_icon(imgid + "SE", I_SE)
|
||||
insert_icon(imgid + "WE", I_WE)
|
||||
insert_icon(imgid + "EE", I_EE)
|
||||
@@ -103,20 +103,19 @@ GLOBAL_LIST_EMPTY(robot_sprite_sheets)
|
||||
var/datum/universal_icon/I_NE = uni_icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", NORTH)
|
||||
if(I_NE)
|
||||
I_N.blend_icon(I_NE, ICON_OVERLAY)
|
||||
if(S.has_eye_sprites)
|
||||
|
||||
var/datum/universal_icon/I_SE = uni_icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", SOUTH)
|
||||
if(I_SE)
|
||||
I_S.blend_icon(I_SE, ICON_OVERLAY)
|
||||
if(S.has_eye_sprites)
|
||||
|
||||
var/datum/universal_icon/I_WE = uni_icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", WEST)
|
||||
if(I_WE)
|
||||
I_W.blend_icon(I_WE, ICON_OVERLAY)
|
||||
if(S.has_eye_sprites)
|
||||
|
||||
var/datum/universal_icon/I_EE = uni_icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", EAST)
|
||||
if(I_EE)
|
||||
I_E.blend_icon(I_EE, ICON_OVERLAY)
|
||||
|
||||
var/imgid = sanitize_css_class_name("[S.type]")
|
||||
if(S.icon_x > S.icon_y)
|
||||
var/buffer = (S.icon_x - S.icon_y) / 2
|
||||
I_N.crop(0, -buffer, S.icon_x, S.icon_y + buffer)
|
||||
@@ -134,6 +133,7 @@ GLOBAL_LIST_EMPTY(robot_sprite_sheets)
|
||||
I_W.scale(120, 120)
|
||||
I_E.scale(120, 120)
|
||||
|
||||
var/imgid = sanitize_css_class_name("[S.type]")
|
||||
insert_icon(imgid + "N", I_N)
|
||||
insert_icon(imgid + "S", I_S)
|
||||
insert_icon(imgid + "W", I_W)
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
name = "Basic"
|
||||
sort_order = 7
|
||||
|
||||
var/datum/paiCandidate/candidate
|
||||
|
||||
/datum/category_item/player_setup_item/general/basic_antagonism/New()
|
||||
. = ..()
|
||||
candidate = new()
|
||||
|
||||
/datum/category_item/player_setup_item/general/basic_antagonism/load_character(list/save_data)
|
||||
pref.exploit_record = save_data["exploit_record"]
|
||||
pref.antag_faction = save_data["antag_faction"]
|
||||
@@ -19,29 +13,18 @@
|
||||
save_data["antag_vis"] = pref.antag_vis
|
||||
|
||||
/datum/category_item/player_setup_item/general/basic_antagonism/load_preferences(datum/json_savefile/savefile)
|
||||
if(!candidate)
|
||||
candidate = new()
|
||||
|
||||
var/preference_mob = preference_mob()
|
||||
if(!preference_mob)// No preference mob - this happens when we're called from client/New() before it calls ..() (via datum/preferences/New())
|
||||
spawn()
|
||||
preference_mob = preference_mob()
|
||||
if(!preference_mob)
|
||||
return
|
||||
candidate.savefile_load(preference_mob)
|
||||
return
|
||||
|
||||
candidate.savefile_load(preference_mob)
|
||||
|
||||
/datum/category_item/player_setup_item/general/basic_antagonism/save_preferences(datum/json_savefile/savefile)
|
||||
if(!candidate)
|
||||
return
|
||||
|
||||
if(!preference_mob())
|
||||
return
|
||||
|
||||
candidate.savefile_save(preference_mob())
|
||||
|
||||
/datum/category_item/player_setup_item/general/basic_antagonism/sanitize_character()
|
||||
if(!pref.antag_faction) pref.antag_faction = "None"
|
||||
if(!pref.antag_vis) pref.antag_vis = "Hidden"
|
||||
@@ -62,13 +45,23 @@
|
||||
if(!jobban_isbanned(user, "Records"))
|
||||
data["exploitable_record"] = TextPreview(pref.exploit_record, 40)
|
||||
|
||||
if(!candidate)
|
||||
CRASH("[user] pAI prefs have a null candidate var.")
|
||||
data["pai_name"] = pref.read_preference(/datum/preference/text/pai_name)
|
||||
data["pai_desc"] = pref.read_preference(/datum/preference/text/pai_description)
|
||||
data["pai_role"] = pref.read_preference(/datum/preference/text/pai_role)
|
||||
data["pai_ad"] = pref.read_preference(/datum/preference/text/pai_ad)
|
||||
data["pai_comments"] = pref.read_preference(/datum/preference/text/pai_comments)
|
||||
data["pai_eyecolor"] = pref.read_preference(/datum/preference/color/pai_eye_color)
|
||||
var/pai_chassis = pref.read_preference(/datum/preference/text/pai_chassis)
|
||||
data["pai_chassis"] = pai_chassis
|
||||
data["pai_chassises"] = SSpai.get_chassis_list()
|
||||
data["pai_emotion"] = pref.read_preference(/datum/preference/text/pai_emotion)
|
||||
data["pai_emotions"] = GLOB.pai_emotions
|
||||
|
||||
data["pai_name"] = candidate.name ? candidate.name : "None Set"
|
||||
data["pai_desc"] = candidate.description ? TextPreview(candidate.description, 40) : "None Set"
|
||||
data["pai_role"] = candidate.role ? TextPreview(candidate.role, 40) : "None Set"
|
||||
data["pai_comments"] = candidate.comments ? TextPreview(candidate.comments, 40) : "None Set"
|
||||
var/datum/pai_sprite/sprite_datum = SSpai.chassis_data(pai_chassis)
|
||||
if(sprite_datum)
|
||||
var/datum/asset/spritesheet_batched/pai_icons/spritesheet = get_asset_datum(/datum/asset/spritesheet_batched/pai_icons)
|
||||
data["pai_sprite_datum_class"] = sanitize_css_class_name("[sprite_datum.type]")
|
||||
data["pai_sprite_datum_size"] = spritesheet.icon_size_id(data["pai_sprite_datum_class"] + "S") // just get the south icon's size, the rest will be the same
|
||||
|
||||
return data
|
||||
|
||||
@@ -111,23 +104,39 @@
|
||||
pref.antag_vis = choice
|
||||
return TOPIC_REFRESH
|
||||
|
||||
if("option")
|
||||
if("pai_option")
|
||||
var/t
|
||||
switch(params["option"])
|
||||
switch(params["pai_option"])
|
||||
if("name")
|
||||
t = sanitizeName(tgui_input_text(user, "Enter a name for your pAI", "Global Preference", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN, 1)
|
||||
t = sanitizeName(params["name"], MAX_NAME_LEN, TRUE)
|
||||
if(t && CanUseTopic(user))
|
||||
candidate.name = t
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_name, t)
|
||||
if("desc")
|
||||
t = tgui_input_text(user, "Enter a description for your pAI", "Global Preference", html_decode(candidate.description), multiline = TRUE, prevent_enter = TRUE)
|
||||
t = sanitize(params["desc"], MAX_MESSAGE_LEN * 4)
|
||||
if(!isnull(t) && CanUseTopic(user))
|
||||
candidate.description = sanitize(t)
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_description, sanitize(t))
|
||||
if("ad")
|
||||
t = sanitize(params["ad"])
|
||||
if(!isnull(t) && CanUseTopic(user))
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_ad, sanitize(t))
|
||||
if("role")
|
||||
t = tgui_input_text(user, "Enter a role for your pAI", "Global Preference", html_decode(candidate.role))
|
||||
t = sanitize(params["role"])
|
||||
if(!isnull(t) && CanUseTopic(user))
|
||||
candidate.role = sanitize(t)
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_role, sanitize(t))
|
||||
if("ooc")
|
||||
t = tgui_input_text(user, "Enter any OOC comments", "Global Preference", html_decode(candidate.comments), multiline = TRUE, prevent_enter = TRUE)
|
||||
t = sanitize(params["ooc"])
|
||||
if(!isnull(t) && CanUseTopic(user))
|
||||
candidate.comments = sanitize(t)
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_comments, sanitize(t))
|
||||
if("color")
|
||||
var/new_color = sanitize_hexcolor(params["color"])
|
||||
if(new_color && CanUseTopic(user))
|
||||
pref.update_preference_by_type(/datum/preference/color/pai_eye_color, new_color)
|
||||
if("chassis")
|
||||
var/new_chassis = params["chassis"]
|
||||
if(new_chassis && CanUseTopic(user) && (new_chassis in SSpai.get_chassis_list()))
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_chassis, new_chassis)
|
||||
if("emotion")
|
||||
var/new_emotion = params["emotion"]
|
||||
if(new_emotion && CanUseTopic(user) && (new_emotion in GLOB.pai_emotions))
|
||||
pref.update_preference_by_type(/datum/preference/text/pai_emotion, new_emotion)
|
||||
return TOPIC_REFRESH
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/// Moves pai save file to tg pref in current file slot
|
||||
/datum/preferences/proc/migration_19_paifile(datum/json_savefile/S)
|
||||
//Not everyone has a pai savefile.
|
||||
var/pai_path = "data/player_saves/[copytext(client_ckey, 1, 2)]/[client_ckey]/pai.sav"
|
||||
if(!fexists(pai_path))
|
||||
return
|
||||
var/savefile/F = new /savefile(pai_path)
|
||||
if(!F)
|
||||
return
|
||||
|
||||
// Should never happen, but if someone has a wonked out file then lets avoid it too
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
if(isnull(version) || version != 1)
|
||||
fdel(pai_path)
|
||||
return
|
||||
|
||||
// Put the pai in slot 0
|
||||
var/list/prefs = S.get_entry("character1", null)
|
||||
if(!islist(prefs))
|
||||
return
|
||||
|
||||
// Get data
|
||||
var/pai_name
|
||||
var/pai_description
|
||||
var/pai_role
|
||||
var/pai_comments
|
||||
var/pai_eye_color
|
||||
var/pai_chassis
|
||||
var/pai_ouremotion
|
||||
F["name"] >> pai_name
|
||||
F["description"] >> pai_description
|
||||
F["role"] >> pai_role
|
||||
F["comments"] >> pai_comments
|
||||
F["eyecolor"] >> pai_eye_color
|
||||
F["chassis"] >> pai_chassis
|
||||
F["emotion"] >> pai_ouremotion
|
||||
// F["gender"] >> pai_gender // We use the character slot's now
|
||||
|
||||
if(pai_name)
|
||||
prefs["Pai_Name"] = pai_name
|
||||
if(pai_role)
|
||||
prefs["Pai_Role"] = pai_role
|
||||
if(pai_description)
|
||||
prefs["Pai_Desc"] = pai_description
|
||||
if(pai_comments)
|
||||
prefs["Pai_Comments"] = pai_comments
|
||||
if(pai_eye_color)
|
||||
prefs["Pai_EyeColor"] = pai_eye_color
|
||||
if(pai_chassis)
|
||||
prefs["Pai_Chassis"] = pai_chassis
|
||||
if(pai_ouremotion)
|
||||
prefs["Pai_Emotion"] = pai_ouremotion
|
||||
|
||||
fdel(pai_path)
|
||||
|
||||
S.save()
|
||||
@@ -0,0 +1,116 @@
|
||||
/datum/preference/text/pai_name
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Name"
|
||||
maximum_value_length = MAX_NAME_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_name/create_default_value()
|
||||
return PAI_UNSET
|
||||
|
||||
/datum/preference/text/pai_name/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_description
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Desc"
|
||||
maximum_value_length = MAX_MESSAGE_LEN * 4
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_description/create_default_value()
|
||||
return PAI_UNSET
|
||||
|
||||
/datum/preference/text/pai_description/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_role
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Role"
|
||||
maximum_value_length = MAX_MESSAGE_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_role/create_default_value()
|
||||
return PAI_UNSET
|
||||
|
||||
/datum/preference/text/pai_role/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_ad
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Ad"
|
||||
maximum_value_length = MAX_MESSAGE_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_ad/create_default_value()
|
||||
return PAI_UNSET
|
||||
|
||||
/datum/preference/text/pai_ad/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_comments
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Comments"
|
||||
maximum_value_length = MAX_MESSAGE_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_comments/create_default_value()
|
||||
return PAI_UNSET
|
||||
|
||||
/datum/preference/text/pai_comments/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/color/pai_eye_color
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_EyeColor"
|
||||
can_randomize = TRUE
|
||||
|
||||
/datum/preference/color/pai_eye_color/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_chassis
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Chassis"
|
||||
maximum_value_length = MAX_NAME_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_chassis/create_default_value()
|
||||
return PAI_DEFAULT_CHASSIS
|
||||
|
||||
/datum/preference/text/pai_chassis/is_valid(value)
|
||||
if(!(value in SSpai.get_chassis_list()))
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
/datum/preference/text/pai_chassis/apply_pref_to(mob/living, value)
|
||||
return
|
||||
|
||||
|
||||
/datum/preference/text/pai_emotion
|
||||
category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
|
||||
savefile_identifier = PREFERENCE_CHARACTER
|
||||
savefile_key = "Pai_Emotion"
|
||||
maximum_value_length = MAX_NAME_LEN
|
||||
can_randomize = FALSE
|
||||
|
||||
/datum/preference/text/pai_emotion/create_default_value()
|
||||
return GLOB.pai_emotions[1]
|
||||
|
||||
/datum/preference/text/pai_emotion/is_valid(value)
|
||||
if(!(value in GLOB.pai_emotions))
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
/datum/preference/text/pai_emotion/apply_pref_to(mob/living, value)
|
||||
return
|
||||
@@ -1,5 +1,5 @@
|
||||
#define SAVEFILE_VERSION_MIN 8
|
||||
#define SAVEFILE_VERSION_MAX 18
|
||||
#define SAVEFILE_VERSION_MAX 19
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
@@ -98,6 +98,16 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
log_world("[client_ckey] preferences successfully migrated from [current_version] to v18.")
|
||||
to_chat(client, span_danger("v18 savefile migration complete."))
|
||||
|
||||
// Migration for pai to tg pregs
|
||||
if(current_version < 19)
|
||||
log_world("[client_ckey] preferences migrating from [current_version] to v19....")
|
||||
to_chat(client, span_danger("Migrating savefile from version [current_version] to v19..."))
|
||||
|
||||
migration_19_paifile(S)
|
||||
|
||||
log_world("[client_ckey] preferences successfully migrated from [current_version] to v19.")
|
||||
to_chat(client, span_danger("v19 savefile migration complete."))
|
||||
/datum/preferences/proc/update_character(current_version, list/save_data)
|
||||
// Migration from BYOND savefiles to JSON: Important milemark.
|
||||
if(current_version == -3)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
get_asset_datum(/datum/asset/simple/preferences),
|
||||
get_asset_datum(/datum/asset/spritesheet/preferences),
|
||||
get_asset_datum(/datum/asset/json/preferences),
|
||||
get_asset_datum(/datum/asset/spritesheet_batched/pai_icons),
|
||||
)
|
||||
|
||||
for (var/datum/preference_middleware/preference_middleware as anything in middleware)
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
emote_sound = pick(bigsound)
|
||||
else if(ispAI(user))
|
||||
var/mob/living/silicon/pai/me = user
|
||||
if(me.chassis == "teppi")
|
||||
if(istype(SSpai.chassis_data(me.chassis_name), /datum/pai_sprite/large))
|
||||
emote_sound = pick(bigsound)
|
||||
else if(user.size_multiplier >= 1.5)
|
||||
emote_sound = pick(bigsound)
|
||||
@@ -202,7 +202,7 @@
|
||||
emote_sound = pick(bigsound)
|
||||
else if(ispAI(user))
|
||||
var/mob/living/silicon/pai/me = user
|
||||
if(me.chassis == "teppi")
|
||||
if(istype(SSpai.chassis_data(me.chassis_name), /datum/pai_sprite/large))
|
||||
emote_sound = pick(bigsound)
|
||||
else if(user.size_multiplier >= 1.5)
|
||||
emote_sound = pick(bigsound)
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
/obj/structure/musician/tgui_interact(mob/user)
|
||||
return song.tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
/* FIXME
|
||||
/obj/structure/musician/wrench_act(mob/living/user, obj/item/tool)
|
||||
. = ..()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
/datum/map_template/shelter/proc/check_deploy(turf/deploy_location, var/is_ship)
|
||||
var/affected = get_affected_turfs(deploy_location, centered=TRUE)
|
||||
for(var/turf/T in affected)
|
||||
var/shelter_status = get_turf_deployability(T)
|
||||
var/shelter_status = get_turf_deployability(T, is_ship)
|
||||
if(shelter_status != SHELTER_DEPLOY_ALLOWED)
|
||||
return shelter_status
|
||||
return SHELTER_DEPLOY_ALLOWED
|
||||
|
||||
@@ -965,24 +965,28 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
to_chat(src,span_warning("You cannot alert pAI cards when you are banned from playing as a pAI."))
|
||||
return
|
||||
|
||||
if(src.client.prefs?.be_special & BE_PAI)
|
||||
var/choice = tgui_alert(src, "Would you like to submit yourself to the recruitment list too?", "Confirmation", list("No", "Yes"))
|
||||
if(!choice)
|
||||
return
|
||||
if(choice == "Yes")
|
||||
paiController.recruitWindow(src)
|
||||
var/count = 0
|
||||
for(var/obj/item/paicard/p in GLOB.all_pai_cards)
|
||||
var/obj/item/paicard/PP = p
|
||||
if(PP.pai == null)
|
||||
count++
|
||||
PP.add_overlay("pai-ghostalert")
|
||||
PP.alertUpdate()
|
||||
spawn(54)
|
||||
PP.cut_overlays()
|
||||
to_chat(src,span_notice("Flashing the displays of [count] unoccupied PAIs."))
|
||||
else
|
||||
to_chat(src,span_warning("You have 'Be pAI' disabled in your character prefs, so we can't help you."))
|
||||
if(!(src.client.prefs?.be_special & BE_PAI))
|
||||
to_chat(src,span_warning("You have 'Be pAI' disabled in your character prefs."))
|
||||
return
|
||||
|
||||
var/choice = tgui_alert(src, "Would you like to submit yourself to the recruitment list too?", "Confirmation", list("No", "Yes"))
|
||||
if(!choice || choice != "Yes")
|
||||
return
|
||||
|
||||
to_chat(src,span_notice("Flashing the displays of [pai_card_ping()] unoccupied PAIs."))
|
||||
|
||||
/mob/observer/dead/proc/pai_card_ping()
|
||||
var/count = 0
|
||||
for(var/obj/item/paicard/p in GLOB.all_pai_cards)
|
||||
var/obj/item/paicard/PP = p
|
||||
if(PP.pai)
|
||||
continue
|
||||
count++
|
||||
PP.cut_overlays()
|
||||
PP.add_overlay("pai-ghostalert")
|
||||
PP.alertUpdate()
|
||||
addtimer(CALLBACK(PP, TYPE_PROC_REF(/obj/item/paicard, clear_invite_overlay)), 1 MINUTE, TIMER_DELETE_ME)
|
||||
return count
|
||||
|
||||
/mob/observer/dead/speech_bubble_appearance()
|
||||
return "ghost"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
add_verb(src,/mob/proc/nme_vore_ch)
|
||||
add_verb(src,/mob/proc/enter_soulcatcher)
|
||||
|
||||
if(!voice_sounds_list.len || !voice_sounds_list)
|
||||
if(!length(voice_sounds_list))
|
||||
if(client.prefs.voice_sound)
|
||||
var/prefsound = client.prefs.voice_sound
|
||||
voice_sounds_list = get_talk_sound(prefsound)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Pai chassis selection
|
||||
/datum/tgui_module/pai_chassis
|
||||
name = "PAI Chassis Configurator"
|
||||
tgui_id = "PaiChoose"
|
||||
var/selected_chassis
|
||||
var/selected_color
|
||||
|
||||
/datum/tgui_module/pai_chassis/tgui_state(mob/user)
|
||||
return GLOB.tgui_self_state
|
||||
|
||||
/datum/tgui_module/pai_chassis/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet_batched/pai_icons),
|
||||
)
|
||||
|
||||
/datum/tgui_module/pai_chassis/tgui_static_data()
|
||||
var/list/data = ..()
|
||||
var/list/available_sprites = list()
|
||||
for(var/key, value in SSpai.get_chassis_list())
|
||||
var/datum/pai_sprite/current_sprite = value
|
||||
var/model_type = "def"
|
||||
if(istype(current_sprite, /datum/pai_sprite/large))
|
||||
model_type = "big"
|
||||
UNTYPED_LIST_ADD(available_sprites, list("sprite" = current_sprite.name, "belly" = current_sprite.belly_states, "type" = model_type))
|
||||
data["pai_chassises"] = available_sprites
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_module/pai_chassis/tgui_data()
|
||||
var/list/data = ..()
|
||||
|
||||
var/mob/living/silicon/pai/pai_host = host
|
||||
data["pai_color"] = selected_color ? selected_color : pai_host.eye_color
|
||||
|
||||
var/datum/pai_sprite/sprite_datum = SSpai.chassis_data(selected_chassis || pai_host.chassis_name)
|
||||
if(sprite_datum)
|
||||
var/datum/asset/spritesheet_batched/pai_icons/spritesheet = get_asset_datum(/datum/asset/spritesheet_batched/pai_icons)
|
||||
data["pai_chassis"] = sprite_datum.name
|
||||
data["selected_chassis"] = selected_chassis
|
||||
data["sprite_datum_class"] = sanitize_css_class_name("[sprite_datum.type]")
|
||||
data["sprite_datum_size"] = spritesheet.icon_size_id(data["sprite_datum_class"] + "S") // just get the south icon's size, the rest will be the same
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_module/pai_chassis/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("pick_icon")
|
||||
var/new_chassis = params["value"]
|
||||
if(new_chassis && (new_chassis in SSpai.get_chassis_list()))
|
||||
selected_chassis = new_chassis
|
||||
return TRUE
|
||||
if("confirm")
|
||||
if(!selected_chassis)
|
||||
return FALSE
|
||||
var/mob/living/silicon/pai/pai_host = host
|
||||
if(selected_color)
|
||||
pai_host.eye_color = selected_color
|
||||
pai_host.change_chassis(selected_chassis)
|
||||
return TRUE
|
||||
if("change_color")
|
||||
var/new_color = sanitize_hexcolor(params["color"])
|
||||
if(!new_color)
|
||||
return FALSE
|
||||
selected_color = new_color
|
||||
return TRUE
|
||||
@@ -19,7 +19,8 @@
|
||||
card.damage_random_component()
|
||||
|
||||
if(gibbed)
|
||||
qdel(card)
|
||||
if(!QDELETED(card)) // Either the pai or card could be deleted first, prevent a loop
|
||||
qdel(card)
|
||||
..(gibbed)
|
||||
else
|
||||
card.add_overlay("pai-dead")
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
/mob/living/silicon/pai/Life()
|
||||
check_retract_cable()
|
||||
|
||||
if(src.cable)
|
||||
if(get_dist(src, src.cable) > 1)
|
||||
var/turf/T = get_turf_or_move(src.loc)
|
||||
for (var/mob/M in viewers(T))
|
||||
M.show_message(span_red("The data cable rapidly retracts back into its spool."), 3, span_red("You hear a click and the sound of wire spooling rapidly."), 2)
|
||||
playsound(src, 'sound/machines/click.ogg', 50, 1)
|
||||
|
||||
qdel(src.cable)
|
||||
src.cable = null
|
||||
|
||||
if (src.stat == DEAD)
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
if(card.cell != PP_FUNCTIONAL|| card.processor != PP_FUNCTIONAL || card.board != PP_FUNCTIONAL || card.capacitor != PP_FUNCTIONAL)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person)
|
||||
pass_flags = 1
|
||||
mob_size = MOB_SMALL
|
||||
softfall = TRUE
|
||||
|
||||
holder_type = /obj/item/holder/pai
|
||||
|
||||
@@ -25,7 +26,9 @@
|
||||
var/obj/item/radio/borg/pai/radio // Our primary radio
|
||||
var/obj/item/communicator/integrated/communicator // Our integrated communicator.
|
||||
|
||||
var/chassis = "pai-repairbot" // A record of your chosen chassis.
|
||||
var/atom/movable/screen/pai/pai_fold_display = null
|
||||
|
||||
var/chassis_name = PAI_DEFAULT_CHASSIS // A record of your chosen chassis.
|
||||
|
||||
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
|
||||
|
||||
@@ -68,15 +71,47 @@
|
||||
|
||||
var/our_icon_rotation = 0
|
||||
|
||||
var/eye_glow = TRUE
|
||||
var/hide_glow = FALSE
|
||||
var/image/eye_layer = null // Holds the eye overlay.
|
||||
var/eye_color = "#00ff0d"
|
||||
var/icon/holo_icon_south
|
||||
var/icon/holo_icon_north
|
||||
var/icon/holo_icon_east
|
||||
var/icon/holo_icon_west
|
||||
var/holo_icon_dimension_X = 32
|
||||
var/holo_icon_dimension_Y = 32
|
||||
|
||||
//These vars keep track of whether you have the related software, used for easily updating the UI
|
||||
var/soft_ut = FALSE //universal translator
|
||||
var/soft_mr = FALSE //medical records
|
||||
var/soft_sr = FALSE //security records
|
||||
var/soft_dj = FALSE //door jack
|
||||
var/soft_as = FALSE //atmosphere sensor
|
||||
var/soft_si = FALSE //signaler
|
||||
var/soft_ar = FALSE //ar hud
|
||||
var/soft_da = FALSE //death alarm
|
||||
|
||||
var/datum/tgui_module/pai_chassis/pai_ui_chassis
|
||||
|
||||
vore_capacity = 1
|
||||
vore_capacity_ex = list("stomach" = 1)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Init and destroy
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/mob/living/silicon/pai/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
card = loc
|
||||
if(!istype(card))
|
||||
return INITIALIZE_HINT_QDEL
|
||||
card = new(src)
|
||||
card.pai = src
|
||||
|
||||
sradio = new(src)
|
||||
communicator = new(src)
|
||||
pai_ui_chassis = new(src)
|
||||
if(card)
|
||||
if(!card.radio)
|
||||
card.radio = new /obj/item/radio/borg/pai(src.card)
|
||||
@@ -92,6 +127,12 @@
|
||||
|
||||
add_verb(src, /mob/living/silicon/pai/proc/choose_chassis)
|
||||
add_verb(src, /mob/living/silicon/pai/proc/choose_verbs)
|
||||
add_verb(src, /mob/proc/dominate_predator)
|
||||
add_verb(src, /mob/living/proc/dominate_prey)
|
||||
add_verb(src, /mob/living/proc/set_size)
|
||||
add_verb(src, /mob/living/proc/shred_limb)
|
||||
|
||||
remove_verb(src, /mob/verb/toggle_gun_mode) // Pai doesn't have support for this and shouldn't be able to use guns anyway
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
@@ -104,7 +145,11 @@
|
||||
M.toff = FALSE
|
||||
|
||||
/mob/living/silicon/pai/Login()
|
||||
..()
|
||||
. = ..()
|
||||
if(!holo_icon_south)
|
||||
last_special = world.time + 100 //Let's give get_character_icon time to work
|
||||
get_character_icon()
|
||||
|
||||
// Vorestation Edit: Meta Info for pAI
|
||||
if (client.prefs)
|
||||
ooc_notes = client.prefs.read_preference(/datum/preference/text/living/ooc_notes)
|
||||
@@ -117,19 +162,233 @@
|
||||
|
||||
src << sound('sound/effects/pai_login.ogg', volume = 75) //VOREStation Add
|
||||
|
||||
// this function shows the information about being silenced as a pAI in the Status panel
|
||||
/mob/living/silicon/pai/proc/show_silenced()
|
||||
. = ""
|
||||
if(src.silence_time)
|
||||
var/timeleft = round((silence_time - world.timeofday)/10 ,1)
|
||||
. += "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]"
|
||||
/// Load pref save data from client and apply it to the pai.
|
||||
/mob/living/silicon/pai/proc/apply_preferences(client/cli, var/silent = 1)
|
||||
if(!cli?.prefs)
|
||||
return FALSE
|
||||
var/datum/preferences/pref = cli.prefs
|
||||
|
||||
SetName(pref.read_preference(/datum/preference/text/pai_name))
|
||||
flavor_text = pref.read_preference(/datum/preference/text/pai_description)
|
||||
change_chassis(pref.read_preference(/datum/preference/text/pai_chassis))
|
||||
gender = pref.read_preference(/datum/preference/choiced/gender/biological) // Cannot use identifying yet due to byond limits
|
||||
eye_color = pref.read_preference(/datum/preference/color/pai_eye_color)
|
||||
card.screen_color = eye_color
|
||||
card.setEmotion(GLOB.pai_emotions[pref.read_preference(/datum/preference/text/pai_emotion)])
|
||||
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
release_vore_contents()
|
||||
check_retract_cable()
|
||||
if(!QDELETED(card))
|
||||
QDEL_NULL(card)
|
||||
QDEL_NULL(pai_ui_chassis)
|
||||
QDEL_NULL(sradio)
|
||||
QDEL_NULL(communicator)
|
||||
QDEL_NULL(pda)
|
||||
if(pai_fold_display)
|
||||
QDEL_NULL(pai_fold_display)
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
hackdoor = null
|
||||
current = null
|
||||
if(ckey)
|
||||
GLOB.paikeys -= ckey
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/clear_client()
|
||||
if(ckey)
|
||||
GLOB.paikeys -= ckey
|
||||
return ..()
|
||||
|
||||
// No binary for pAIs.
|
||||
/mob/living/silicon/pai/binarycheck()
|
||||
return 0
|
||||
|
||||
/// Verb used to select a chassis from the list of available chassis
|
||||
/mob/living/silicon/pai/proc/choose_chassis()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Choose Chassis"
|
||||
|
||||
pai_ui_chassis.tgui_interact(src)
|
||||
|
||||
/// Change pai sprite and offsets based upon the selected chassis id
|
||||
/mob/living/silicon/pai/proc/change_chassis(new_chassis)
|
||||
if(!(new_chassis in SSpai.get_chassis_list()))
|
||||
new_chassis = PAI_DEFAULT_CHASSIS
|
||||
chassis_name = new_chassis
|
||||
|
||||
//We resize ourselves to normal here for a moment to let the vis_height get reset
|
||||
var/oursize = size_multiplier
|
||||
resize(1, FALSE, TRUE, TRUE, FALSE)
|
||||
|
||||
// Get icon data setup
|
||||
var/datum/pai_sprite/chassis_data = SSpai.chassis_data(chassis_name)
|
||||
if(chassis_data.holo_projector)
|
||||
// Rebuild holosprite from character
|
||||
if(!holo_icon_south)
|
||||
get_character_icon()
|
||||
else
|
||||
// Get data from our sprite datum
|
||||
icon = chassis_data.sprite_icon
|
||||
pixel_x = chassis_data.pixel_x
|
||||
default_pixel_x = pixel_x
|
||||
pixel_y = chassis_data.pixel_y
|
||||
default_pixel_y = pixel_y
|
||||
vis_height = chassis_data.vis_height
|
||||
|
||||
// Drops you if you change to a non-flying chassis
|
||||
if(chassis_data.flying)
|
||||
hovering = TRUE
|
||||
else
|
||||
hovering = FALSE
|
||||
if(isopenspace(loc))
|
||||
fall()
|
||||
|
||||
// Set vore size.
|
||||
vore_capacity = max(1, chassis_data.belly_states) // Minimum of 1
|
||||
vore_capacity_ex = list("stomach" = vore_capacity)
|
||||
|
||||
// Emergency eject if you change to a smaller belly
|
||||
if(vore_fullness > vore_capacity && vore_selected)
|
||||
vore_selected.release_all_contents(TRUE)
|
||||
|
||||
update_icon()
|
||||
resize(oursize, FALSE, TRUE, TRUE, FALSE) //And then back again now that we're sure the vis_height is correct.
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Click interactions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob)
|
||||
var/obj/item/card/id/ID = W.GetID()
|
||||
if(ID)
|
||||
if (idaccessible == 1)
|
||||
switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel")))
|
||||
if("Add Access")
|
||||
idcard.access |= ID.GetAccess()
|
||||
to_chat(user, span_notice("You add the access from the [W] to [src]."))
|
||||
to_chat(src, span_notice("\The [user] swipes the [W] over you. You copy the access codes."))
|
||||
if(radio)
|
||||
radio.recalculateChannels()
|
||||
return
|
||||
if("Remove Access")
|
||||
idcard.access = list()
|
||||
to_chat(user, span_notice("You remove the access from [src]."))
|
||||
to_chat(src, span_warning("\The [user] swipes the [W] over you, removing access codes from you."))
|
||||
if(radio)
|
||||
radio.recalculateChannels()
|
||||
return
|
||||
if("Cancel", null)
|
||||
return
|
||||
else if (istype(W, /obj/item/card/id) && idaccessible == 0)
|
||||
to_chat(user, span_notice("[src] is not accepting access modifcations at this time."))
|
||||
return
|
||||
|
||||
//Overriding this will stop a number of headaches down the track.
|
||||
/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(W.force)
|
||||
visible_message(span_danger("[user.name] attacks [src] with [W]!"))
|
||||
src.adjustBruteLoss(W.force)
|
||||
src.updatehealth()
|
||||
else
|
||||
visible_message(span_warning("[user.name] bonks [src] harmlessly with [W]."))
|
||||
spawn(1)
|
||||
if(stat != DEAD) close_up()
|
||||
return
|
||||
|
||||
/mob/living/silicon/pai/attack_hand(mob/user as mob)
|
||||
if(user.a_intent == I_HELP)
|
||||
visible_message(span_notice("[user.name] pats [src]."))
|
||||
else
|
||||
visible_message(span_danger("[user.name] boops [src] on the head."))
|
||||
close_up()
|
||||
|
||||
/mob/living/silicon/pai/UnarmedAttack(atom/A, proximity_flag)
|
||||
. = ..()
|
||||
|
||||
// Some restricted objects to interact with
|
||||
var/obj/O = A
|
||||
if(istype(O) && O.allow_pai_interaction(proximity_flag))
|
||||
O.attack_hand(src)
|
||||
return
|
||||
|
||||
// Zmovement already allows these to be used with the verbs anyway
|
||||
if(istype(A,/obj/structure/ladder))
|
||||
var/obj/structure/ladder/L = A
|
||||
L.attack_hand(src)
|
||||
return
|
||||
|
||||
// We don't want to pick these up, just toggle them
|
||||
if(istype(A,/obj/item/flashlight/lamp))
|
||||
var/obj/item/flashlight/lamp/L = A
|
||||
L.toggle_light()
|
||||
return
|
||||
|
||||
// All other computers explain why it's not accessible by showing a firewall warning
|
||||
if(istype(A,/obj/machinery/computer))
|
||||
to_chat(src,span_warning("A firewall prevents you from interfacing with this device!"))
|
||||
return
|
||||
|
||||
if(!ismob(A) || A == src)
|
||||
return
|
||||
|
||||
switch(a_intent)
|
||||
if(I_HELP)
|
||||
if(isliving(A))
|
||||
hug(src, A)
|
||||
if(I_GRAB)
|
||||
pai_nom(A)
|
||||
|
||||
// Allow card inhabited machines to be interacted with
|
||||
// This has to override ClickOn because of storage depth nonsense with how pAIs are in cards in GLOB.machines
|
||||
/mob/living/silicon/pai/ClickOn(var/atom/A, var/params)
|
||||
if(istype(A, /obj/machinery))
|
||||
var/obj/machinery/M = A
|
||||
if(M.paicard == card)
|
||||
M.attack_ai(src)
|
||||
return
|
||||
return ..()
|
||||
|
||||
// Handle being picked up.
|
||||
/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop)
|
||||
var/obj/item/holder/H = ..(grabber, self_drop)
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
H.icon_state = SSpai.chassis_data(chassis_name).sprite_icon_state
|
||||
grabber.update_inv_l_hand()
|
||||
grabber.update_inv_r_hand()
|
||||
return H
|
||||
|
||||
/mob/living/silicon/pai/Moved(atom/oldloc, direct, forced, movetime)
|
||||
. = ..()
|
||||
check_retract_cable()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Status and damage
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/mob/living/silicon/pai/get_status_tab_items()
|
||||
. = ..()
|
||||
. += ""
|
||||
. += show_silenced()
|
||||
|
||||
/mob/living/silicon/pai/adjustBruteLoss(amount, include_robo)
|
||||
. = ..()
|
||||
if(amount > 0 && health <= 90) //Something's probably attacking us!
|
||||
if(prob(amount)) //The more damage it is doing, the more likely it is to damage something important!
|
||||
card.damage_random_component()
|
||||
|
||||
/mob/living/silicon/pai/adjustFireLoss(amount, include_robo)
|
||||
. = ..()
|
||||
if(amount > 0 && health <= 90)
|
||||
if(prob(amount))
|
||||
card.damage_random_component()
|
||||
|
||||
/mob/living/silicon/pai/restrained()
|
||||
if(istype(src.loc,/obj/item/paicard))
|
||||
return 0
|
||||
@@ -169,145 +428,36 @@
|
||||
if(3)
|
||||
to_chat(src, span_infoplain(span_green("You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.")))
|
||||
|
||||
/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C)
|
||||
if (!C)
|
||||
src.reset_perspective()
|
||||
return 0
|
||||
if (stat == 2 || !C.status || !(src.network in C.network)) return 0
|
||||
/// this function shows the information about being silenced as a pAI in the Status panel
|
||||
/mob/living/silicon/pai/proc/show_silenced()
|
||||
. = ""
|
||||
if(src.silence_time)
|
||||
var/timeleft = round((silence_time - world.timeofday)/10 ,1)
|
||||
. += "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]"
|
||||
|
||||
// ok, we're alive, camera is good and in our network...
|
||||
src.current = C
|
||||
src.AddComponent(/datum/component/remote_view, focused_on = C, viewsize = null, vconfig_path = /datum/remote_view_config/camera_standard)
|
||||
return 1
|
||||
/// Fully heals a pai, used when a pai is repaired
|
||||
/mob/living/silicon/pai/proc/full_restore()
|
||||
adjustBruteLoss(- bruteloss)
|
||||
adjustFireLoss(- fireloss)
|
||||
addtimer(CALLBACK(src, PROC_REF(restore_delay_start)), 5 SECONDS, TIMER_DELETE_ME)
|
||||
|
||||
/mob/living/silicon/pai/verb/reset_record_view()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Reset Records Software"
|
||||
/mob/living/silicon/pai/proc/restore_delay_start()
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
PRIVATE_PROC(TRUE)
|
||||
card.setEmotion(16)
|
||||
stat = CONSCIOUS
|
||||
addtimer(CALLBACK(src, PROC_REF(restore_delay_end)), 1 SECONDS, TIMER_DELETE_ME)
|
||||
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
security_cannotfind = 0
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
medical_cannotfind = 0
|
||||
SStgui.update_uis(src)
|
||||
to_chat(src, span_notice("You reset your record-viewing software."))
|
||||
|
||||
/mob/living/silicon/pai/cancel_camera()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Cancel Camera View"
|
||||
reset_perspective()
|
||||
|
||||
/mob/living/silicon/pai/reset_perspective(atom/new_eye)
|
||||
. = ..()
|
||||
current = null
|
||||
|
||||
// Procs/code after this point is used to convert the stationary pai item into a
|
||||
// mobile pai mob. This also includes handling some of the general shit that can occur
|
||||
// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z
|
||||
|
||||
/mob/living/silicon/pai/verb/fold_out()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Unfold Chassis"
|
||||
|
||||
if(stat || sleeping || paralysis || weakened)
|
||||
return
|
||||
|
||||
if(loc != card)
|
||||
return
|
||||
|
||||
// Lets not trap the pai forever. These are special cases we want to escape out of when in our card
|
||||
if(istype(loc.loc, /obj/item/pda))
|
||||
var/obj/item/pda/ourpda = loc.loc
|
||||
if(ourpda.pai == card)
|
||||
ourpda.pai.forceMove(ourpda.loc)
|
||||
ourpda.pai = null
|
||||
visible_message(span_warning("\The [card] ejects itself from \the [ourpda]."))
|
||||
return
|
||||
if(istype(loc.loc, /obj/item/storage/vore_egg))
|
||||
var/obj/item/storage/vore_egg/ouregg = loc.loc
|
||||
to_chat(src, span_notice("You craftily use your built in rumble function to break free of \the [ouregg]'s confines!"))
|
||||
ouregg.hatch(src)
|
||||
return
|
||||
|
||||
if(is_folding_unsafe(loc.loc))
|
||||
to_chat(src, span_danger("It's not safe to unfold while inside a [loc.loc]!"))
|
||||
return
|
||||
|
||||
if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL)
|
||||
to_chat(src, span_warning("ERROR: System malfunction. Service required!"))
|
||||
|
||||
if(world.time <= last_special)
|
||||
to_chat(src, span_warning("You can't unfold yet."))
|
||||
return
|
||||
|
||||
last_special = world.time + 100
|
||||
|
||||
if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves.
|
||||
var/obj/machinery/M = card.loc
|
||||
M.ejectpai()
|
||||
//I'm not sure how much of this is necessary, but I would rather avoid issues.
|
||||
if(istype(card.loc,/obj/item/rig_module))
|
||||
to_chat(src, span_filter_notice("There is no room to unfold inside this rig module. You're good and stuck."))
|
||||
return 0
|
||||
else if(istype(card.loc,/mob))
|
||||
var/mob/holder = card.loc
|
||||
if(ishuman(holder))
|
||||
var/mob/living/carbon/human/H = holder
|
||||
for(var/obj/item/organ/external/affecting in H.organs)
|
||||
if(card in affecting.implants)
|
||||
affecting.take_damage(rand(30,50))
|
||||
affecting.implants -= card
|
||||
H.visible_message(span_danger("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!"))
|
||||
break
|
||||
holder.drop_from_inventory(card)
|
||||
else if(isbelly(card.loc)) //VOREStation edit.
|
||||
to_chat(src, span_notice("There is no room to unfold in here. You're good and stuck.")) //VOREStation edit.
|
||||
return 0 //VOREStation edit.
|
||||
else if(istype(card.loc,/obj/item/pda))
|
||||
var/obj/item/pda/holder = card.loc
|
||||
holder.pai = null
|
||||
|
||||
src.forceMove(card.loc)
|
||||
card.forceMove(src)
|
||||
card.screen_loc = null
|
||||
/mob/living/silicon/pai/proc/restore_delay_end()
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
PRIVATE_PROC(TRUE)
|
||||
var/mob/observer/dead/ghost = get_ghost()
|
||||
if(ghost)
|
||||
ghost.notify_revive("Someone is trying to revive you. Re-enter your body if you want to be revived!", 'sound/effects/pai-restore.ogg', source = card)
|
||||
canmove = TRUE
|
||||
|
||||
if(isturf(loc))
|
||||
var/turf/T = get_turf(src)
|
||||
if(istype(T)) T.visible_message(span_filter_notice(span_bold("[src]") + " folds outwards, expanding into a mobile form."))
|
||||
|
||||
add_verb(src, /mob/living/silicon/pai/proc/pai_nom)
|
||||
add_verb(src, /mob/living/proc/vertical_nom)
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/fold_up()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Collapse Chassis"
|
||||
|
||||
if(stat || sleeping || paralysis || weakened)
|
||||
return
|
||||
|
||||
if(src.loc == card)
|
||||
return
|
||||
|
||||
if(world.time <= last_special)
|
||||
to_chat(src, span_warning("You can't fold up yet."))
|
||||
return
|
||||
|
||||
close_up()
|
||||
|
||||
/mob/living/silicon/pai/proc/choose_verbs()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Choose Speech Verbs"
|
||||
|
||||
var/choice = tgui_input_list(src,"What theme would you like to use for your speech verbs?","Theme Choice", GLOB.possible_say_verbs)
|
||||
if(!choice) return
|
||||
|
||||
var/list/sayverbs = GLOB.possible_say_verbs[choice]
|
||||
speak_statement = sayverbs[1]
|
||||
speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)]
|
||||
speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)]
|
||||
card.setEmotion(15)
|
||||
playsound(card, 'sound/effects/pai-restore.ogg', 50, FALSE)
|
||||
card.visible_message(span_filter_notice("\The [card] chimes."), runemessage = "chime")
|
||||
|
||||
/mob/living/silicon/pai/lay_down()
|
||||
set name = "Rest"
|
||||
@@ -320,193 +470,166 @@
|
||||
if(istype(rig))
|
||||
rig.force_rest(src)
|
||||
return
|
||||
else if(chassis == "13")
|
||||
resting = !resting
|
||||
//update_transform() I want this to make you ROTATE like normal HUMANS do! But! There's lots of problems and I don't know how to fix them!
|
||||
else
|
||||
resting = !resting
|
||||
icon_state = resting ? "[chassis]_rest" : "[chassis]"
|
||||
update_icon() //VOREStation edit
|
||||
update_icon()
|
||||
to_chat(src, span_notice("You are now [resting ? "resting" : "getting up"]."))
|
||||
|
||||
canmove = !resting
|
||||
|
||||
/*
|
||||
/mob/living/silicon/pai/update_transform()
|
||||
/mob/living/silicon/pai/proc/check_retract_cable()
|
||||
if(!cable)
|
||||
return
|
||||
|
||||
var/desired_scale_x = size_multiplier * icon_scale_x
|
||||
var/desired_scale_y = size_multiplier * icon_scale_y
|
||||
var/turf/current = get_turf(src)
|
||||
var/turf/cableturf = get_turf(cable)
|
||||
if(get_dist(current, cableturf) <= 1)
|
||||
return
|
||||
|
||||
// Now for the regular stuff.
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(desired_scale_x, desired_scale_y)
|
||||
M.Translate(0, (vis_height/2)*(desired_scale_y-1))
|
||||
cableturf.visible_message("The data cable rapidly retracts back into its spool.", "You hear a click and the sound of wire spooling rapidly.")
|
||||
playsound(src, 'sound/machines/click.ogg', 50, 1)
|
||||
QDEL_NULL(cable)
|
||||
|
||||
if(chassis != "13")
|
||||
appearance_flags |= PIXEL_SCALE
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Update icons
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var/anim_time = 3
|
||||
/mob/living/silicon/pai/update_icon()
|
||||
. = ..()
|
||||
|
||||
if(resting)
|
||||
M.Turn(90)
|
||||
M.Scale(desired_scale_y, desired_scale_x)
|
||||
if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64)
|
||||
M.Translate(13,-22)
|
||||
else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64)
|
||||
M.Translate(1,-22)
|
||||
else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32)
|
||||
M.Translate(13,-6)
|
||||
var/datum/pai_sprite/chassis_data = SSpai.chassis_data(chassis_name)
|
||||
if(chassis_data.holo_projector)
|
||||
icon_state = null
|
||||
icon = holo_icon_south
|
||||
add_eyes()
|
||||
return
|
||||
|
||||
update_fullness()
|
||||
|
||||
// Don't get a vore belly size if we have no belly size set!
|
||||
var/belly_size = CLAMP(vore_fullness, 0, chassis_data.belly_states)
|
||||
if(resting && !chassis_data.resting_belly) // check if we have a belly while resting
|
||||
belly_size = 0
|
||||
var/fullness_extension = ""
|
||||
if(belly_size > 1) // Multibelly support
|
||||
fullness_extension = "_[belly_size]"
|
||||
icon_state = "[chassis_data.sprite_icon_state][resting && chassis_data.can_rest ? "_rest" : ""][belly_size ? "_full[fullness_extension]" : ""]"
|
||||
|
||||
add_eyes()
|
||||
|
||||
/// Applies the eye overlay if the chassis has it
|
||||
/mob/living/silicon/pai/proc/add_eyes()
|
||||
remove_eyes()
|
||||
|
||||
var/datum/pai_sprite/chassis_data = SSpai.chassis_data(chassis_name)
|
||||
if(chassis_data.holo_projector)
|
||||
// Special eyes that are based on holoprojection of your character's icon size
|
||||
if(holo_icon_south.Width() > 32)
|
||||
holo_icon_dimension_X = 64
|
||||
pixel_x = -16
|
||||
default_pixel_x = -16
|
||||
// Get height too
|
||||
if(holo_icon_south.Height() > 32)
|
||||
holo_icon_dimension_Y = 64
|
||||
vis_height = 64
|
||||
// Set eyes
|
||||
if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 32)
|
||||
eye_layer = image('icons/mob/pai.dmi', chassis_data.holo_eyes_icon_state)
|
||||
else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64)
|
||||
eye_layer = image('icons/mob/pai32x64.dmi', chassis_data.holo_eyes_icon_state)
|
||||
else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32)
|
||||
eye_layer = image('icons/mob/pai64x32.dmi', chassis_data.holo_eyes_icon_state)
|
||||
else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64)
|
||||
eye_layer = image('icons/mob/pai64x64.dmi', chassis_data.holo_eyes_icon_state)
|
||||
else if(chassis_data.has_eye_sprites)
|
||||
// Default eye handling
|
||||
eye_layer = image(icon, "[icon_state]-eyes")
|
||||
else
|
||||
// No eyes, so don't bother setting icon stuff
|
||||
return
|
||||
eye_layer.appearance_flags = appearance_flags
|
||||
eye_layer.color = eye_color
|
||||
if(eye_glow && !hide_glow)
|
||||
eye_layer.plane = PLANE_LIGHTING_ABOVE
|
||||
add_overlay(eye_layer)
|
||||
|
||||
/// Removes the eye overlay if it has one
|
||||
/mob/living/silicon/pai/proc/remove_eyes()
|
||||
if(!eye_layer)
|
||||
return
|
||||
cut_overlay(eye_layer)
|
||||
qdel(eye_layer)
|
||||
eye_layer = null
|
||||
|
||||
/// Gets icons for all four directions based on the character slot currently loaded
|
||||
/mob/living/silicon/pai/proc/get_character_icon()
|
||||
if(!client || !client.prefs) return FALSE
|
||||
var/mob/living/carbon/human/dummy/dummy = new ()
|
||||
//This doesn't include custom_items because that's ... hard.
|
||||
client.prefs.dress_preview_mob(dummy)
|
||||
sleep(1 SECOND) //Strange bug in preview code? Without this, certain things won't show up. Yay race conditions?
|
||||
dummy.regenerate_icons()
|
||||
|
||||
var/icon/new_holo = getCompoundIcon(dummy)
|
||||
|
||||
dummy.tail_layering = TRUE
|
||||
dummy.set_dir(NORTH)
|
||||
var/icon/new_holo_north = getCompoundIcon(dummy)
|
||||
dummy.set_dir(EAST)
|
||||
var/icon/new_holo_east = getCompoundIcon(dummy)
|
||||
dummy.set_dir(WEST)
|
||||
var/icon/new_holo_west = getCompoundIcon(dummy)
|
||||
|
||||
qdel(holo_icon_south)
|
||||
qdel(holo_icon_north)
|
||||
qdel(holo_icon_east)
|
||||
qdel(holo_icon_west)
|
||||
qdel(dummy)
|
||||
holo_icon_south = new_holo
|
||||
holo_icon_north = new_holo_north
|
||||
holo_icon_east = new_holo_east
|
||||
holo_icon_west = new_holo_west
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/set_dir(var/new_dir)
|
||||
. = ..()
|
||||
if(. && SSpai.chassis_data(chassis_name).holo_projector)
|
||||
switch(dir)
|
||||
if(SOUTH)
|
||||
icon = holo_icon_south
|
||||
if(NORTH)
|
||||
icon = holo_icon_north
|
||||
if(EAST)
|
||||
icon = holo_icon_east
|
||||
if(WEST)
|
||||
icon = holo_icon_west
|
||||
else
|
||||
M.Translate(1,-6)
|
||||
layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters
|
||||
else
|
||||
M.Scale(desired_scale_x, desired_scale_y)
|
||||
M.Translate(0, (vis_height/2)*(desired_scale_y-1))
|
||||
layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters
|
||||
animate(src, transform = M, time = anim_time)
|
||||
src.transform = M
|
||||
handle_status_indicators()
|
||||
*/
|
||||
//Overriding this will stop a number of headaches down the track.
|
||||
/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(W.force)
|
||||
visible_message(span_danger("[user.name] attacks [src] with [W]!"))
|
||||
src.adjustBruteLoss(W.force)
|
||||
src.updatehealth()
|
||||
else
|
||||
visible_message(span_warning("[user.name] bonks [src] harmlessly with [W]."))
|
||||
spawn(1)
|
||||
if(stat != 2) close_up()
|
||||
return
|
||||
icon = holo_icon_north
|
||||
|
||||
/mob/living/silicon/pai/attack_hand(mob/user as mob)
|
||||
if(user.a_intent == I_HELP)
|
||||
visible_message(span_notice("[user.name] pats [src]."))
|
||||
else
|
||||
visible_message(span_danger("[user.name] boops [src] on the head."))
|
||||
close_up()
|
||||
/mob/living/silicon/pai/a_intent_change(input as text)
|
||||
. = ..()
|
||||
|
||||
//I'm not sure how much of this is necessary, but I would rather avoid issues.
|
||||
/mob/living/silicon/pai/proc/close_up(silent= FALSE)
|
||||
switch(a_intent)
|
||||
if(I_HELP)
|
||||
hud_used.help_intent.icon_state = "intent_help-s"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
last_special = world.time + 100
|
||||
if(I_DISARM)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-s"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
if(loc == card)
|
||||
return
|
||||
if(I_GRAB)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-s"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
// some snowflake locations where we really shouldn't fold up...
|
||||
if(is_folding_unsafe(loc))
|
||||
to_chat(src, span_danger("It's not safe to fold up while inside a [loc]!"))
|
||||
return
|
||||
|
||||
release_vore_contents(FALSE) //VOREStation Add
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(istype(T) && !silent) T.visible_message(span_filter_notice(span_bold("[src]") + " neatly folds inwards, compacting down to a rectangular card."))
|
||||
|
||||
stop_pulling()
|
||||
|
||||
//stop resting
|
||||
resting = 0
|
||||
|
||||
// If we are being held, handle removing our holder from their inv.
|
||||
var/obj/item/holder/our_holder = loc
|
||||
if(istype(our_holder))
|
||||
var/turf/drop_turf = get_turf(our_holder)
|
||||
var/mob/living/M = our_holder.loc
|
||||
if(istype(M))
|
||||
M.drop_from_inventory(our_holder)
|
||||
src.forceMove(card)
|
||||
card.forceMove(drop_turf)
|
||||
|
||||
if(isbelly(loc)) //If in tumby, when fold up, card go into tumby
|
||||
var/obj/belly/B = loc
|
||||
src.forceMove(card)
|
||||
card.forceMove(B)
|
||||
|
||||
if(isdisposalpacket(loc))
|
||||
var/obj/structure/disposalholder/hold = loc
|
||||
src.forceMove(card)
|
||||
card.forceMove(hold)
|
||||
|
||||
else //Otherwise go on floor
|
||||
card.forceMove(get_turf(src))
|
||||
src.forceMove(card)
|
||||
|
||||
canmove = 1
|
||||
resting = 0
|
||||
icon_state = "[chassis]"
|
||||
if(isopenspace(card.loc))
|
||||
fall()
|
||||
remove_verb(src, /mob/living/silicon/pai/proc/pai_nom)
|
||||
remove_verb(src, /mob/living/proc/vertical_nom)
|
||||
|
||||
/mob/living/silicon/pai/proc/is_folding_unsafe(check_location)
|
||||
return isbelly(check_location) || istype(check_location, /obj/machinery) || istype(check_location, /obj/item/storage/vore_egg || istype(check_location, /obj/item/pda))
|
||||
|
||||
// No binary for pAIs.
|
||||
/mob/living/silicon/pai/binarycheck()
|
||||
return 0
|
||||
|
||||
// Handle being picked up.
|
||||
/mob/living/silicon/pai/get_scooped(var/mob/living/carbon/grabber, var/self_drop)
|
||||
var/obj/item/holder/H = ..(grabber, self_drop)
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
H.icon_state = "[chassis]"
|
||||
grabber.update_inv_l_hand()
|
||||
grabber.update_inv_r_hand()
|
||||
return H
|
||||
|
||||
/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob)
|
||||
var/obj/item/card/id/ID = W.GetID()
|
||||
if(ID)
|
||||
if (idaccessible == 1)
|
||||
switch(tgui_alert(user, "Do you wish to add access to [src] or remove access from [src]?","Access Modify",list("Add Access","Remove Access", "Cancel")))
|
||||
if("Add Access")
|
||||
idcard.access |= ID.GetAccess()
|
||||
to_chat(user, span_notice("You add the access from the [W] to [src]."))
|
||||
to_chat(src, span_notice("\The [user] swipes the [W] over you. You copy the access codes."))
|
||||
if(radio)
|
||||
radio.recalculateChannels()
|
||||
return
|
||||
if("Remove Access")
|
||||
idcard.access = list()
|
||||
to_chat(user, span_notice("You remove the access from [src]."))
|
||||
to_chat(src, span_warning("\The [user] swipes the [W] over you, removing access codes from you."))
|
||||
if(radio)
|
||||
radio.recalculateChannels()
|
||||
return
|
||||
if("Cancel", null)
|
||||
return
|
||||
else if (istype(W, /obj/item/card/id) && idaccessible == 0)
|
||||
to_chat(user, span_notice("[src] is not accepting access modifcations at this time."))
|
||||
return
|
||||
|
||||
/mob/living/silicon/pai/verb/allowmodification()
|
||||
set name = "Change Access Modifcation Permission"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Allows people to modify your access or block people from modifying your access."
|
||||
|
||||
if(idaccessible == 0)
|
||||
idaccessible = 1
|
||||
visible_message(span_notice("\The [src] clicks as their access modification slot opens."),span_notice("You allow access modifications."), runemessage = "click")
|
||||
else
|
||||
idaccessible = 0
|
||||
visible_message(span_notice("\The [src] clicks as their access modification slot closes."),span_notice("You block access modfications."), runemessage = "click")
|
||||
|
||||
|
||||
/mob/living/silicon/pai/verb/wipe_software()
|
||||
set name = "Enter Storage"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot."
|
||||
|
||||
// Make sure people don't kill themselves accidentally
|
||||
if(tgui_alert(src, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes")
|
||||
return
|
||||
|
||||
close_up()
|
||||
visible_message(span_filter_notice(span_bold("[src]") + " fades away from the screen, the pAI device goes silent."))
|
||||
card.removePersonality()
|
||||
clear_client()
|
||||
if(I_HURT)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-s"
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/mob/living/silicon/pai/verb/fold_out()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Unfold Chassis"
|
||||
|
||||
if(stat || sleeping || paralysis || weakened)
|
||||
return
|
||||
|
||||
if(loc != card)
|
||||
return
|
||||
|
||||
// Lets not trap the pai forever. These are special cases we want to escape out of when in our card
|
||||
if(istype(loc.loc, /obj/item/pda))
|
||||
var/obj/item/pda/ourpda = loc.loc
|
||||
if(ourpda.pai == card)
|
||||
ourpda.pai.forceMove(ourpda.loc)
|
||||
ourpda.pai = null
|
||||
visible_message(span_warning("\The [card] ejects itself from \the [ourpda]."))
|
||||
return
|
||||
if(istype(loc.loc, /obj/item/storage/vore_egg))
|
||||
var/obj/item/storage/vore_egg/ouregg = loc.loc
|
||||
to_chat(src, span_notice("You craftily use your built in rumble function to break free of \the [ouregg]'s confines!"))
|
||||
ouregg.hatch(src)
|
||||
return
|
||||
|
||||
if(is_folding_unsafe(loc.loc))
|
||||
to_chat(src, span_danger("It's not safe to unfold while inside a [loc.loc]!"))
|
||||
return
|
||||
|
||||
if(card.projector != PP_FUNCTIONAL && card.emitter != PP_FUNCTIONAL)
|
||||
to_chat(src, span_warning("ERROR: System malfunction. Service required!"))
|
||||
|
||||
if(world.time <= last_special)
|
||||
to_chat(src, span_warning("You can't unfold yet."))
|
||||
return
|
||||
|
||||
last_special = world.time + 100
|
||||
|
||||
if(istype(card.loc, /obj/machinery)) // VOREStation edit, this statement allows pAIs stuck in a machine to eject themselves.
|
||||
var/obj/machinery/M = card.loc
|
||||
M.ejectpai()
|
||||
//I'm not sure how much of this is necessary, but I would rather avoid issues.
|
||||
if(istype(card.loc,/obj/item/rig_module))
|
||||
to_chat(src, span_filter_notice("There is no room to unfold inside this rig module. You're good and stuck."))
|
||||
return 0
|
||||
else if(istype(card.loc,/mob))
|
||||
var/mob/holder = card.loc
|
||||
if(ishuman(holder))
|
||||
var/mob/living/carbon/human/H = holder
|
||||
for(var/obj/item/organ/external/affecting in H.organs)
|
||||
if(card in affecting.implants)
|
||||
affecting.take_damage(rand(30,50))
|
||||
affecting.implants -= card
|
||||
H.visible_message(span_danger("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!"))
|
||||
break
|
||||
holder.drop_from_inventory(card)
|
||||
else if(isbelly(card.loc)) //VOREStation edit.
|
||||
to_chat(src, span_notice("There is no room to unfold in here. You're good and stuck.")) //VOREStation edit.
|
||||
return 0 //VOREStation edit.
|
||||
else if(istype(card.loc,/obj/item/pda))
|
||||
var/obj/item/pda/holder = card.loc
|
||||
holder.pai = null
|
||||
|
||||
src.forceMove(card.loc)
|
||||
card.forceMove(src)
|
||||
card.screen_loc = null
|
||||
canmove = TRUE
|
||||
|
||||
if(isturf(loc))
|
||||
var/turf/T = get_turf(src)
|
||||
if(istype(T)) T.visible_message(span_filter_notice(span_bold("[src]") + " folds outwards, expanding into a mobile form."))
|
||||
|
||||
add_verb(src, /mob/living/silicon/pai/proc/pai_nom)
|
||||
add_verb(src, /mob/living/proc/vertical_nom)
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/fold_up()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Collapse Chassis"
|
||||
|
||||
if(stat || sleeping || paralysis || weakened)
|
||||
return
|
||||
|
||||
if(src.loc == card)
|
||||
return
|
||||
|
||||
if(world.time <= last_special)
|
||||
to_chat(src, span_warning("You can't fold up yet."))
|
||||
return
|
||||
|
||||
close_up()
|
||||
|
||||
//I'm not sure how much of this is necessary, but I would rather avoid issues.
|
||||
/mob/living/silicon/pai/proc/close_up(silent= FALSE)
|
||||
|
||||
last_special = world.time + 100
|
||||
|
||||
if(loc == card)
|
||||
return
|
||||
|
||||
// some snowflake locations where we really shouldn't fold up...
|
||||
if(is_folding_unsafe(loc))
|
||||
to_chat(src, span_danger("It's not safe to fold up while inside a [loc]!"))
|
||||
return
|
||||
|
||||
release_vore_contents(FALSE) //VOREStation Add
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(istype(T) && !silent) T.visible_message(span_filter_notice(span_bold("[src]") + " neatly folds inwards, compacting down to a rectangular card."))
|
||||
|
||||
stop_pulling()
|
||||
|
||||
//stop resting
|
||||
resting = 0
|
||||
|
||||
// If we are being held, handle removing our holder from their inv.
|
||||
var/obj/item/holder/our_holder = loc
|
||||
if(istype(our_holder))
|
||||
var/turf/drop_turf = get_turf(our_holder)
|
||||
var/mob/living/M = our_holder.loc
|
||||
if(istype(M))
|
||||
M.drop_from_inventory(our_holder)
|
||||
src.forceMove(card)
|
||||
card.forceMove(drop_turf)
|
||||
|
||||
if(isbelly(loc)) //If in tumby, when fold up, card go into tumby
|
||||
var/obj/belly/B = loc
|
||||
src.forceMove(card)
|
||||
card.forceMove(B)
|
||||
|
||||
if(isdisposalpacket(loc))
|
||||
var/obj/structure/disposalholder/hold = loc
|
||||
src.forceMove(card)
|
||||
card.forceMove(hold)
|
||||
|
||||
else //Otherwise go on floor
|
||||
card.forceMove(get_turf(src))
|
||||
src.forceMove(card)
|
||||
|
||||
canmove = 1
|
||||
resting = 0
|
||||
icon_state = SSpai.chassis_data(chassis_name).sprite_icon_state
|
||||
if(isopenspace(card.loc))
|
||||
fall()
|
||||
remove_verb(src, /mob/living/silicon/pai/proc/pai_nom)
|
||||
remove_verb(src, /mob/living/proc/vertical_nom)
|
||||
|
||||
/mob/living/silicon/pai/proc/is_folding_unsafe(check_location)
|
||||
return isbelly(check_location) || istype(check_location, /obj/machinery) || istype(check_location, /obj/item/storage/vore_egg || istype(check_location, /obj/item/pda))
|
||||
@@ -1,6 +1,3 @@
|
||||
/mob/living/silicon/pai
|
||||
var/atom/movable/screen/pai/pai_fold_display = null
|
||||
|
||||
/atom/movable/screen/pai
|
||||
icon = 'icons/mob/pai_hud.dmi'
|
||||
var/base_state
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/mob/living/silicon/pai/proc/refresh_software_status() //This manages the pAI software status buttons icon states based on if you have them and if they are enabled
|
||||
for(var/thing in software) //this only gets called when you click one of the relevent buttons, rather than all the time!
|
||||
var/datum/pai_software/soft = software[thing]
|
||||
if(istype(soft,/datum/pai_software/med_records))
|
||||
soft_mr = TRUE
|
||||
if(istype(soft,/datum/pai_software/sec_records))
|
||||
soft_sr = TRUE
|
||||
if(istype(soft,/datum/pai_software/door_jack))
|
||||
soft_dj = TRUE
|
||||
if(istype(soft,/datum/pai_software/atmosphere_sensor))
|
||||
soft_as = TRUE
|
||||
if(istype(soft,/datum/pai_software/pai_hud))
|
||||
soft_ar = TRUE
|
||||
if(istype(soft,/datum/pai_software/translator))
|
||||
soft_ut = TRUE
|
||||
if(istype(soft,/datum/pai_software/signaller))
|
||||
soft_si = TRUE
|
||||
if(istype(soft,/datum/pai_software/deathalarm))
|
||||
soft_da = TRUE
|
||||
for(var/atom/movable/screen/pai/button in hud_used.other)
|
||||
if(button.name == "medical records")
|
||||
if(soft_mr)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "security records")
|
||||
if(soft_sr)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "door jack")
|
||||
if(soft_dj)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "atmosphere sensor")
|
||||
if(soft_as)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "remote signaler")
|
||||
if(soft_si)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "universal translator")
|
||||
if(soft_ut && translator_on)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "ar hud")
|
||||
if(soft_ar && paiHUD)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "death alarm")
|
||||
if(soft_da && paiDA)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
|
||||
/mob/living/silicon/pai/verb/wipe_software()
|
||||
set name = "Enter Storage"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Upload your personality to the cloud and wipe your software from the card. This is functionally equivalent to cryo or robotic storage, freeing up your job slot."
|
||||
|
||||
// Make sure people don't kill themselves accidentally
|
||||
if(tgui_alert(src, "WARNING: This will immediately wipe your software and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?", "Wipe Software", list("No", "Yes")) != "Yes")
|
||||
return
|
||||
|
||||
close_up()
|
||||
visible_message(span_filter_notice(span_bold("[src]") + " fades away from the screen, the pAI device goes silent."))
|
||||
card.removePersonality()
|
||||
clear_client()
|
||||
|
||||
/mob/living/silicon/pai/proc/touch_window(soft_name) //This lets us touch TGUI procs and windows that may be nested behind other TGUI procs and windows
|
||||
if(stat != CONSCIOUS) //so we can access our software without having to open up the software interface TGUI window
|
||||
to_chat(src, span_warning("You can't do that right now."))
|
||||
return
|
||||
for(var/thing in software)
|
||||
var/datum/pai_software/S = software[thing]
|
||||
if(istype(S, /datum/pai_software) && S.name == soft_name)
|
||||
if(S.toggle)
|
||||
S.toggle(src)
|
||||
to_chat(src, span_notice("You toggled [S.name]."))
|
||||
refresh_software_status()
|
||||
else
|
||||
S.tgui_interact(src)
|
||||
refresh_software_status()
|
||||
return
|
||||
for(var/thing in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/our_soft = GLOB.pai_software_by_key[thing]
|
||||
if(our_soft.name == soft_name)
|
||||
if(!(ram >= our_soft.ram_cost))
|
||||
to_chat(src, span_warning("Insufficient RAM for download. (Cost [our_soft.ram_cost] : [ram] Remaining)"))
|
||||
return
|
||||
if(tgui_alert(src, "Do you want to download [our_soft.name]? It costs [our_soft.ram_cost], and you have [ram] remaining.", "Download [our_soft.name]", list("Yes", "No")) == "Yes")
|
||||
if(!(ram >= our_soft.ram_cost))
|
||||
return
|
||||
if(software[our_soft.id])
|
||||
return
|
||||
ram -= our_soft.ram_cost
|
||||
software[our_soft.id] = our_soft
|
||||
to_chat(src, span_notice("You downloaded [our_soft.name]. ([ram] RAM remaining.)"))
|
||||
refresh_software_status()
|
||||
|
||||
//Procs for using the various UI buttons for your softwares
|
||||
/mob/living/silicon/pai/proc/directives()
|
||||
touch_window("Directives")
|
||||
|
||||
/mob/living/silicon/pai/proc/crew_manifest()
|
||||
touch_window("Crew Manifest")
|
||||
|
||||
/mob/living/silicon/pai/proc/med_records()
|
||||
touch_window("Medical Records")
|
||||
|
||||
/mob/living/silicon/pai/proc/sec_records()
|
||||
touch_window("Security Records")
|
||||
|
||||
/mob/living/silicon/pai/proc/remote_signal()
|
||||
touch_window("Remote Signaler")
|
||||
|
||||
/mob/living/silicon/pai/proc/atmos_sensor()
|
||||
touch_window("Atmosphere Sensor")
|
||||
|
||||
/mob/living/silicon/pai/proc/translator()
|
||||
touch_window("Universal Translator")
|
||||
|
||||
/mob/living/silicon/pai/proc/door_jack()
|
||||
touch_window("Door Jack")
|
||||
|
||||
/mob/living/silicon/pai/proc/ar_hud()
|
||||
touch_window("AR HUD")
|
||||
|
||||
/mob/living/silicon/pai/proc/death_alarm()
|
||||
touch_window("Death Alarm")
|
||||
@@ -0,0 +1,219 @@
|
||||
/// Change currently viewed camera
|
||||
/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C)
|
||||
if (!C)
|
||||
src.reset_perspective()
|
||||
return 0
|
||||
if (stat == 2 || !C.status || !(src.network in C.network)) return 0
|
||||
|
||||
// ok, we're alive, camera is good and in our network...
|
||||
src.current = C
|
||||
src.AddComponent(/datum/component/remote_view, focused_on = C, viewsize = null, vconfig_path = /datum/remote_view_config/camera_standard)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/cancel_camera()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Cancel Camera View"
|
||||
reset_perspective()
|
||||
|
||||
/mob/living/silicon/pai/reset_perspective(atom/new_eye)
|
||||
. = ..()
|
||||
current = null
|
||||
|
||||
/mob/living/silicon/pai/verb/reset_record_view()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Reset Records Software"
|
||||
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
security_cannotfind = 0
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
medical_cannotfind = 0
|
||||
SStgui.update_uis(src)
|
||||
to_chat(src, span_notice("You reset your record-viewing software."))
|
||||
|
||||
/mob/living/silicon/pai/proc/choose_verbs()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Choose Speech Verbs"
|
||||
|
||||
var/choice = tgui_input_list(src,"What theme would you like to use for your speech verbs?","Theme Choice", GLOB.possible_say_verbs)
|
||||
if(!choice) return
|
||||
|
||||
var/list/sayverbs = GLOB.possible_say_verbs[choice]
|
||||
speak_statement = sayverbs[1]
|
||||
speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)]
|
||||
speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)]
|
||||
|
||||
/mob/living/silicon/pai/verb/allowmodification()
|
||||
set name = "Change Access Modifcation Permission"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Allows people to modify your access or block people from modifying your access."
|
||||
|
||||
if(idaccessible == 0)
|
||||
idaccessible = 1
|
||||
visible_message(span_notice("\The [src] clicks as their access modification slot opens."),span_notice("You allow access modifications."), runemessage = "click")
|
||||
else
|
||||
idaccessible = 0
|
||||
visible_message(span_notice("\The [src] clicks as their access modification slot closes."),span_notice("You block access modfications."), runemessage = "click")
|
||||
|
||||
/mob/living/silicon/pai/verb/toggle_gender_identity_vr()
|
||||
set name = "Set Gender Identity"
|
||||
set desc = "Sets the pronouns when examined and performing an emote."
|
||||
set category = "IC.Settings"
|
||||
var/new_gender_identity = tgui_input_list(src, "Please select a gender Identity:", "Set Gender Identity", list(FEMALE, MALE, NEUTER, PLURAL, HERM))
|
||||
if(!new_gender_identity)
|
||||
return 0
|
||||
gender = new_gender_identity
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/verb/pai_hide()
|
||||
set name = "Hide"
|
||||
set desc = "Allows to hide beneath tables or certain items. Toggled on or off."
|
||||
set category = "Abilities.pAI"
|
||||
|
||||
hide()
|
||||
if(status_flags & HIDING)
|
||||
hide_glow = TRUE
|
||||
else
|
||||
hide_glow = FALSE
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/screen_message(message as text|null)
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Screen Message"
|
||||
set desc = "Allows you to display a message on your screen. This will show up in the chat of anyone who is holding your card."
|
||||
|
||||
if (src.client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, span_warning("You cannot speak in IC (muted)."))
|
||||
return
|
||||
if(loc != card)
|
||||
to_chat(src, span_warning("Your message won't be visible while unfolded!"))
|
||||
if (!message)
|
||||
message = tgui_input_text(src, "Enter text you would like to show on your screen.","Screen Message", encode = FALSE)
|
||||
message = sanitize_or_reflect(message,src)
|
||||
if (!message)
|
||||
return
|
||||
message = capitalize(message)
|
||||
if (stat == DEAD)
|
||||
return
|
||||
card.screen_msg = message
|
||||
var/logmsg = "(CARD SCREEN)[message]"
|
||||
log_talk(logmsg, LOG_SAY)
|
||||
to_chat(src, span_filter_say(span_cult("You print a message to your screen, \"[message]\"")))
|
||||
if(isliving(card.loc))
|
||||
var/mob/living/L = card.loc
|
||||
if(L.client)
|
||||
to_chat(L, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(isbelly(card.loc))
|
||||
var/obj/belly/b = card.loc
|
||||
if(b.owner.client)
|
||||
to_chat(b.owner, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(istype(card.loc, /obj/item/pda))
|
||||
var/obj/item/pda/p = card.loc
|
||||
if(isliving(p.loc))
|
||||
var/mob/living/L = p.loc
|
||||
if(L.client)
|
||||
to_chat(L, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(isbelly(p.loc))
|
||||
var/obj/belly/b = card.loc
|
||||
if(b.owner.client)
|
||||
to_chat(b.owner, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else return
|
||||
else return
|
||||
to_chat(src, span_notice("Your message was relayed."))
|
||||
for (var/mob/G in GLOB.player_list)
|
||||
if (isnewplayer(G))
|
||||
continue
|
||||
else if(isobserver(G) && G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_ears))
|
||||
if((client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) || check_rights_for(G.client, R_HOLDER)) && \
|
||||
G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle))
|
||||
to_chat(G, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
|
||||
/mob/living/silicon/pai/proc/pai_nom(var/mob/living/T in oview(1))
|
||||
set name = "pAI Nom"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Allows you to eat someone while unfolded. Can't be used while in card form."
|
||||
|
||||
if (stat != CONSCIOUS)
|
||||
return
|
||||
return feed_grabbed_to_self(src,T)
|
||||
|
||||
/mob/living/silicon/pai/verb/toggle_eyeglow()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Toggle Eye Glow"
|
||||
|
||||
if(!SSpai.chassis_data(chassis_name).has_eye_sprites)
|
||||
to_chat(src, span_filter_notice("Your selected chassis cannot modify its eye glow!"))
|
||||
return
|
||||
|
||||
if(eye_glow && !hide_glow)
|
||||
eye_glow = FALSE
|
||||
else
|
||||
eye_glow = TRUE
|
||||
hide_glow = FALSE
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/pick_eye_color()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Pick Eye Color"
|
||||
|
||||
if(!SSpai.chassis_data(chassis_name).has_eye_sprites)
|
||||
to_chat(src, span_warning("Your selected chassis eye color can not be modified. The color you pick will only apply to supporting chassis and your card screen."))
|
||||
return
|
||||
|
||||
var/new_eye_color = tgui_color_picker(src, "Choose your character's eye color:", "Eye Color")
|
||||
if(new_eye_color)
|
||||
eye_color = new_eye_color
|
||||
update_icon()
|
||||
card.setEmotion(card.current_emotion)
|
||||
|
||||
/mob/living/silicon/pai/proc/hug(var/mob/living/silicon/pai/H, var/mob/living/target)
|
||||
|
||||
var/t_him = "them"
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/T = target
|
||||
switch(T.identifying_gender)
|
||||
if(MALE)
|
||||
t_him = "him"
|
||||
if(FEMALE)
|
||||
t_him = "her"
|
||||
if(NEUTER)
|
||||
t_him = "it"
|
||||
if(HERM)
|
||||
t_him = "hir"
|
||||
else
|
||||
t_him = "them"
|
||||
else
|
||||
switch(target.gender)
|
||||
if(MALE)
|
||||
t_him = "him"
|
||||
if(FEMALE)
|
||||
t_him = "her"
|
||||
if(NEUTER)
|
||||
t_him = "it"
|
||||
if(HERM)
|
||||
t_him = "hir"
|
||||
else
|
||||
t_him = "them"
|
||||
|
||||
if(H.zone_sel.selecting == BP_HEAD)
|
||||
H.visible_message( \
|
||||
span_notice("[H] pats [target] on the head."), \
|
||||
span_notice("You pat [target] on the head."), )
|
||||
else if(H.zone_sel.selecting == BP_R_HAND || H.zone_sel.selecting == BP_L_HAND)
|
||||
H.visible_message( \
|
||||
span_notice("[H] shakes [target]'s hand."), \
|
||||
span_notice("You shake [target]'s hand."), )
|
||||
else if(H.zone_sel.selecting == "mouth")
|
||||
H.visible_message( \
|
||||
span_notice("[H] boops [target]'s nose."), \
|
||||
span_notice("You boop [target] on the nose."), )
|
||||
else
|
||||
H.visible_message(span_notice("[H] hugs [target] to make [t_him] feel better!"), \
|
||||
span_notice("You hug [target] to make [t_him] feel better!"))
|
||||
playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
@@ -1,659 +0,0 @@
|
||||
/mob/living/silicon/pai
|
||||
icon = 'icons/mob/pai_vr.dmi'
|
||||
softfall = TRUE
|
||||
var/eye_glow = TRUE
|
||||
var/hide_glow = FALSE
|
||||
var/image/eye_layer = null // Holds the eye overlay.
|
||||
var/eye_color = "#00ff0d"
|
||||
var/icon/holo_icon
|
||||
var/icon/holo_icon_north
|
||||
var/holo_icon_dimension_X = 32
|
||||
var/holo_icon_dimension_Y = 32
|
||||
//These vars keep track of whether you have the related software, used for easily updating the UI
|
||||
var/soft_ut = FALSE //universal translator
|
||||
var/soft_mr = FALSE //medical records
|
||||
var/soft_sr = FALSE //security records
|
||||
var/soft_dj = FALSE //door jack
|
||||
var/soft_as = FALSE //atmosphere sensor
|
||||
var/soft_si = FALSE //signaler
|
||||
var/soft_ar = FALSE //ar hud
|
||||
var/soft_da = FALSE //death alarm
|
||||
|
||||
vore_capacity = 1
|
||||
vore_capacity_ex = list("stomach" = 1)
|
||||
|
||||
/mob/living/silicon/pai/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
add_verb(src, /mob/proc/dominate_predator)
|
||||
add_verb(src, /mob/living/proc/dominate_prey)
|
||||
add_verb(src, /mob/living/proc/set_size)
|
||||
add_verb(src, /mob/living/proc/shred_limb)
|
||||
|
||||
/mob/living/silicon/pai/Login()
|
||||
. = ..()
|
||||
if(!holo_icon)
|
||||
last_special = world.time + 100 //Let's give get_character_icon time to work
|
||||
get_character_icon()
|
||||
if(stat == DEAD)
|
||||
healths.icon_state = "health7"
|
||||
|
||||
/mob/living/silicon/pai/proc/full_restore() //This is using do_after all kinds of weird...
|
||||
adjustBruteLoss(- bruteloss)
|
||||
adjustFireLoss(- fireloss)
|
||||
do_after(src, 1 SECONDS, target = src)
|
||||
card.setEmotion(16)
|
||||
stat = CONSCIOUS
|
||||
do_after(src, 5 SECONDS, target = src)
|
||||
var/mob/observer/dead/ghost = src.get_ghost()
|
||||
if(ghost)
|
||||
ghost.notify_revive("Someone is trying to revive you. Re-enter your body if you want to be revived!", 'sound/effects/pai-restore.ogg', source = card)
|
||||
canmove = TRUE
|
||||
card.setEmotion(15)
|
||||
playsound(card, 'sound/effects/pai-restore.ogg', 50, FALSE)
|
||||
card.visible_message(span_filter_notice("\The [card] chimes."), runemessage = "chime")
|
||||
|
||||
/mob/living/silicon/pai/proc/pai_nom(var/mob/living/T in oview(1))
|
||||
set name = "pAI Nom"
|
||||
set category = "Abilities.pAI Commands"
|
||||
set desc = "Allows you to eat someone while unfolded. Can't be used while in card form."
|
||||
|
||||
if (stat != CONSCIOUS)
|
||||
return
|
||||
return feed_grabbed_to_self(src,T)
|
||||
|
||||
/mob/living/silicon/pai/update_icon() //Some functions cause this to occur, such as resting
|
||||
..()
|
||||
if(chassis == "13")
|
||||
icon = holo_icon
|
||||
add_eyes()
|
||||
return
|
||||
|
||||
update_fullness()
|
||||
|
||||
//Add a check when selecting a chassis if you add in support for this, to set vore_capacity to 2 or however many states you have.
|
||||
var/fullness_extension = ""
|
||||
if(vore_capacity > 1 && vore_fullness > 1)
|
||||
fullness_extension = "_[vore_fullness]"
|
||||
|
||||
if(!vore_fullness && !resting)
|
||||
icon_state = "[chassis]" //Using icon_state here resulted in quite a few bugs. Chassis is much less buggy.
|
||||
else if(!vore_fullness && resting)
|
||||
icon_state = "[chassis]_rest"
|
||||
|
||||
// Unfortunately not all these states exist, ugh.
|
||||
else if(vore_fullness && !resting)
|
||||
if(icon_exists(icon, "[chassis]_full[fullness_extension]"))
|
||||
icon_state = "[chassis]_full[fullness_extension]"
|
||||
else
|
||||
icon_state = "[chassis]"
|
||||
else if(vore_fullness && resting)
|
||||
if(icon_exists(icon, "[chassis]_rest_full[fullness_extension]"))
|
||||
icon_state = "[chassis]_rest_full[fullness_extension]"
|
||||
else
|
||||
icon_state = "[chassis]_rest"
|
||||
if(chassis in GLOB.wide_chassis)
|
||||
pixel_x = -16
|
||||
default_pixel_x = -16
|
||||
else
|
||||
pixel_x = 0
|
||||
default_pixel_x = 0
|
||||
add_eyes()
|
||||
|
||||
/mob/living/silicon/pai/update_icons() //And other functions cause this to occur, such as digesting someone.
|
||||
..()
|
||||
if(chassis == "13")
|
||||
icon = holo_icon
|
||||
add_eyes()
|
||||
return
|
||||
update_fullness()
|
||||
//Add a check when selecting a chassis if you add in support for this, to set vore_capacity to 2 or however many states you have.
|
||||
var/fullness_extension = ""
|
||||
if(vore_capacity > 1 && vore_fullness > 1)
|
||||
fullness_extension = "_[vore_fullness]"
|
||||
if(!vore_fullness && !resting)
|
||||
icon_state = "[chassis]"
|
||||
else if(!vore_fullness && resting)
|
||||
icon_state = "[chassis]_rest"
|
||||
else if(vore_fullness && !resting)
|
||||
icon_state = "[chassis]_full[fullness_extension]"
|
||||
else if(vore_fullness && resting)
|
||||
icon_state = "[chassis]_rest_full[fullness_extension]"
|
||||
if(chassis in GLOB.wide_chassis)
|
||||
pixel_x = -16
|
||||
default_pixel_x = -16
|
||||
else
|
||||
pixel_x = 0
|
||||
default_pixel_x = 0
|
||||
add_eyes()
|
||||
|
||||
//proc override to avoid pAI players being invisible while the chassis selection window is open
|
||||
/mob/living/silicon/pai/proc/choose_chassis()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Choose Chassis"
|
||||
var/choice
|
||||
|
||||
choice = tgui_input_list(src, "What would you like to use for your mobile chassis icon?", "Chassis Choice", GLOB.possible_chassis)
|
||||
if(!choice) return
|
||||
var/oursize = size_multiplier
|
||||
resize(1, FALSE, TRUE, TRUE, FALSE) //We resize ourselves to normal here for a moment to let the vis_height get reset
|
||||
chassis = GLOB.possible_chassis[choice]
|
||||
|
||||
vore_capacity = 1
|
||||
vore_capacity_ex = list("stomach" = 1)
|
||||
|
||||
if(chassis == "13")
|
||||
if(!holo_icon)
|
||||
if(!get_character_icon())
|
||||
return
|
||||
icon_state = null
|
||||
icon = holo_icon
|
||||
else if(chassis in GLOB.wide_chassis)
|
||||
icon = 'icons/mob/pai_vr64x64.dmi'
|
||||
vis_height = 64
|
||||
else
|
||||
icon = 'icons/mob/pai_vr.dmi'
|
||||
vis_height = 32
|
||||
resize(oursize, FALSE, TRUE, TRUE, FALSE) //And then back again now that we're sure the vis_height is correct.
|
||||
|
||||
if(chassis in GLOB.flying_chassis)
|
||||
hovering = TRUE
|
||||
else
|
||||
hovering = FALSE
|
||||
if(isopenspace(loc))
|
||||
fall()
|
||||
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/toggle_eyeglow()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Toggle Eye Glow"
|
||||
|
||||
if(chassis in GLOB.allows_eye_color)
|
||||
if(eye_glow && !hide_glow)
|
||||
eye_glow = FALSE
|
||||
else
|
||||
eye_glow = TRUE
|
||||
hide_glow = FALSE
|
||||
update_icon()
|
||||
else
|
||||
to_chat(src, span_filter_notice("Your selected chassis cannot modify its eye glow!"))
|
||||
return
|
||||
|
||||
|
||||
/mob/living/silicon/pai/verb/pick_eye_color()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Pick Eye Color"
|
||||
if(!(chassis in GLOB.allows_eye_color))
|
||||
to_chat(src, span_warning("Your selected chassis eye color can not be modified. The color you pick will only apply to supporting chassis and your card screen."))
|
||||
|
||||
var/new_eye_color = tgui_color_picker(src, "Choose your character's eye color:", "Eye Color")
|
||||
if(new_eye_color)
|
||||
eye_color = new_eye_color
|
||||
update_icon()
|
||||
card.setEmotion(card.current_emotion)
|
||||
|
||||
// Release belly contents before being gc'd!
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
release_vore_contents()
|
||||
if(ckey)
|
||||
GLOB.paikeys -= ckey
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/clear_client()
|
||||
if(ckey)
|
||||
GLOB.paikeys -= ckey
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/proc/add_eyes()
|
||||
remove_eyes()
|
||||
if(chassis == "13")
|
||||
if(holo_icon.Width() > 32)
|
||||
holo_icon_dimension_X = 64
|
||||
pixel_x = -16
|
||||
default_pixel_x = -16
|
||||
if(holo_icon.Height() > 32)
|
||||
holo_icon_dimension_Y = 64
|
||||
if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 32)
|
||||
eye_layer = image('icons/mob/pai_vr.dmi', "type13-eyes")
|
||||
else if(holo_icon_dimension_X == 32 && holo_icon_dimension_Y == 64)
|
||||
eye_layer = image('icons/mob/pai_vr32x64.dmi', "type13-eyes")
|
||||
else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 32)
|
||||
eye_layer = image('icons/mob/pai_vr64x32.dmi', "type13-eyes")
|
||||
else if(holo_icon_dimension_X == 64 && holo_icon_dimension_Y == 64)
|
||||
eye_layer = image('icons/mob/pai_vr64x64.dmi', "type13-eyes")
|
||||
else if(chassis in GLOB.allows_eye_color)
|
||||
eye_layer = image(icon, "[icon_state]-eyes")
|
||||
else return
|
||||
eye_layer.appearance_flags = appearance_flags
|
||||
eye_layer.color = eye_color
|
||||
if(eye_glow && !hide_glow)
|
||||
eye_layer.plane = PLANE_LIGHTING_ABOVE
|
||||
add_overlay(eye_layer)
|
||||
|
||||
/mob/living/silicon/pai/proc/remove_eyes()
|
||||
cut_overlay(eye_layer)
|
||||
qdel(eye_layer)
|
||||
eye_layer = null
|
||||
|
||||
/mob/living/silicon/pai/UnarmedAttack(atom/A, proximity_flag)
|
||||
. = ..()
|
||||
|
||||
if(istype(A,/obj/structure/ladder))
|
||||
// Zmovement already allows these to be used with the verbs anyway
|
||||
var/obj/structure/ladder/L = A
|
||||
L.attack_hand(src)
|
||||
return
|
||||
|
||||
if(!ismob(A) || A == src)
|
||||
return
|
||||
|
||||
switch(a_intent)
|
||||
if(I_HELP)
|
||||
if(isliving(A))
|
||||
hug(src, A)
|
||||
if(I_GRAB)
|
||||
pai_nom(A)
|
||||
|
||||
// Allow card inhabited machines to be interacted with
|
||||
// This has to override ClickOn because of storage depth nonsense with how pAIs are in cards in GLOB.machines
|
||||
/mob/living/silicon/pai/ClickOn(var/atom/A, var/params)
|
||||
if(istype(A, /obj/machinery))
|
||||
var/obj/machinery/M = A
|
||||
if(M.paicard == card)
|
||||
M.attack_ai(src)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/pai/proc/hug(var/mob/living/silicon/pai/H, var/mob/living/target)
|
||||
|
||||
var/t_him = "them"
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/T = target
|
||||
switch(T.identifying_gender)
|
||||
if(MALE)
|
||||
t_him = "him"
|
||||
if(FEMALE)
|
||||
t_him = "her"
|
||||
if(NEUTER)
|
||||
t_him = "it"
|
||||
if(HERM)
|
||||
t_him = "hir"
|
||||
else
|
||||
t_him = "them"
|
||||
else
|
||||
switch(target.gender)
|
||||
if(MALE)
|
||||
t_him = "him"
|
||||
if(FEMALE)
|
||||
t_him = "her"
|
||||
if(NEUTER)
|
||||
t_him = "it"
|
||||
if(HERM)
|
||||
t_him = "hir"
|
||||
else
|
||||
t_him = "them"
|
||||
|
||||
if(H.zone_sel.selecting == BP_HEAD)
|
||||
H.visible_message( \
|
||||
span_notice("[H] pats [target] on the head."), \
|
||||
span_notice("You pat [target] on the head."), )
|
||||
else if(H.zone_sel.selecting == BP_R_HAND || H.zone_sel.selecting == BP_L_HAND)
|
||||
H.visible_message( \
|
||||
span_notice("[H] shakes [target]'s hand."), \
|
||||
span_notice("You shake [target]'s hand."), )
|
||||
else if(H.zone_sel.selecting == "mouth")
|
||||
H.visible_message( \
|
||||
span_notice("[H] boops [target]'s nose."), \
|
||||
span_notice("You boop [target] on the nose."), )
|
||||
else
|
||||
H.visible_message(span_notice("[H] hugs [target] to make [t_him] feel better!"), \
|
||||
span_notice("You hug [target] to make [t_him] feel better!"))
|
||||
playsound(src, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
/mob/living/silicon/pai/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
|
||||
/mob/living/silicon/pai/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(src.savefile_path(user))
|
||||
|
||||
|
||||
F["name"] << src.name
|
||||
F["description"] << src.flavor_text
|
||||
F["eyecolor"] << src.eye_color
|
||||
F["chassis"] << src.chassis
|
||||
F["emotion"] << src.card.current_emotion
|
||||
F["gender"] << src.gender
|
||||
F["version"] << 1
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/proc/savefile_load(mob/user, var/silent = 1)
|
||||
if (IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/path = savefile_path(user)
|
||||
|
||||
if (!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(path)
|
||||
|
||||
if(!F) return //Not everyone has a pai savefile.
|
||||
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
|
||||
if (isnull(version) || version != 1)
|
||||
fdel(path)
|
||||
if (!silent)
|
||||
tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.")
|
||||
return 0
|
||||
var/ourname
|
||||
var/ouremotion
|
||||
var/ourdesc
|
||||
var/oureyes
|
||||
var/ourchassis
|
||||
var/ourgender
|
||||
F["name"] >> ourname
|
||||
F["description"] >> ourdesc
|
||||
F["eyecolor"] >> oureyes
|
||||
F["chassis"] >> ourchassis
|
||||
F["emotion"] >> ouremotion
|
||||
F["gender"] >> ourgender
|
||||
if(ourname)
|
||||
SetName(ourname)
|
||||
if(ourdesc)
|
||||
flavor_text = ourdesc
|
||||
if(ourchassis)
|
||||
chassis = ourchassis
|
||||
if(ourgender)
|
||||
gender = ourgender
|
||||
if(oureyes)
|
||||
card.screen_color = oureyes
|
||||
eye_color = oureyes
|
||||
if(ouremotion)
|
||||
card.setEmotion(ouremotion)
|
||||
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/verb/save_pai_to_slot()
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Save Configuration"
|
||||
savefile_save(src)
|
||||
to_chat(src, span_filter_notice("[name] configuration saved to global pAI settings."))
|
||||
|
||||
/mob/living/silicon/pai/a_intent_change(input as text)
|
||||
. = ..()
|
||||
|
||||
switch(a_intent)
|
||||
if(I_HELP)
|
||||
hud_used.help_intent.icon_state = "intent_help-s"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
if(I_DISARM)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-s"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
if(I_GRAB)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-s"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-n"
|
||||
|
||||
if(I_HURT)
|
||||
hud_used.help_intent.icon_state = "intent_help-n"
|
||||
hud_used.disarm_intent.icon_state = "intent_disarm-n"
|
||||
hud_used.grab_intent.icon_state = "intent_grab-n"
|
||||
hud_used.hurt_intent.icon_state = "intent_harm-s"
|
||||
|
||||
/mob/living/silicon/pai/verb/toggle_gender_identity_vr()
|
||||
set name = "Set Gender Identity"
|
||||
set desc = "Sets the pronouns when examined and performing an emote."
|
||||
set category = "IC.Settings"
|
||||
var/new_gender_identity = tgui_input_list(src, "Please select a gender Identity:", "Set Gender Identity", list(FEMALE, MALE, NEUTER, PLURAL, HERM))
|
||||
if(!new_gender_identity)
|
||||
return 0
|
||||
gender = new_gender_identity
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/verb/pai_hide()
|
||||
set name = "Hide"
|
||||
set desc = "Allows to hide beneath tables or certain items. Toggled on or off."
|
||||
set category = "Abilities.pAI"
|
||||
|
||||
hide()
|
||||
if(status_flags & HIDING)
|
||||
hide_glow = TRUE
|
||||
else
|
||||
hide_glow = FALSE
|
||||
update_icon()
|
||||
|
||||
/mob/living/silicon/pai/verb/screen_message(message as text|null)
|
||||
set category = "Abilities.pAI Commands"
|
||||
set name = "Screen Message"
|
||||
set desc = "Allows you to display a message on your screen. This will show up in the chat of anyone who is holding your card."
|
||||
|
||||
if (src.client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, span_warning("You cannot speak in IC (muted)."))
|
||||
return
|
||||
if(loc != card)
|
||||
to_chat(src, span_warning("Your message won't be visible while unfolded!"))
|
||||
if (!message)
|
||||
message = tgui_input_text(src, "Enter text you would like to show on your screen.","Screen Message", encode = FALSE)
|
||||
message = sanitize_or_reflect(message,src)
|
||||
if (!message)
|
||||
return
|
||||
message = capitalize(message)
|
||||
if (stat == DEAD)
|
||||
return
|
||||
card.screen_msg = message
|
||||
var/logmsg = "(CARD SCREEN)[message]"
|
||||
log_talk(logmsg, LOG_SAY)
|
||||
to_chat(src, span_filter_say(span_cult("You print a message to your screen, \"[message]\"")))
|
||||
if(isliving(card.loc))
|
||||
var/mob/living/L = card.loc
|
||||
if(L.client)
|
||||
to_chat(L, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(isbelly(card.loc))
|
||||
var/obj/belly/b = card.loc
|
||||
if(b.owner.client)
|
||||
to_chat(b.owner, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(istype(card.loc, /obj/item/pda))
|
||||
var/obj/item/pda/p = card.loc
|
||||
if(isliving(p.loc))
|
||||
var/mob/living/L = p.loc
|
||||
if(L.client)
|
||||
to_chat(L, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else if(isbelly(p.loc))
|
||||
var/obj/belly/b = card.loc
|
||||
if(b.owner.client)
|
||||
to_chat(b.owner, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
else return
|
||||
else return
|
||||
else return
|
||||
to_chat(src, span_notice("Your message was relayed."))
|
||||
for (var/mob/G in GLOB.player_list)
|
||||
if (isnewplayer(G))
|
||||
continue
|
||||
else if(isobserver(G) && G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_ears))
|
||||
if((client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) || check_rights_for(G.client, R_HOLDER)) && \
|
||||
G.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle))
|
||||
to_chat(G, span_filter_say(span_cult("[src.name]'s screen prints, \"[message]\"")))
|
||||
|
||||
/mob/living/silicon/pai/proc/touch_window(soft_name) //This lets us touch TGUI procs and windows that may be nested behind other TGUI procs and windows
|
||||
if(stat != CONSCIOUS) //so we can access our software without having to open up the software interface TGUI window
|
||||
to_chat(src, span_warning("You can't do that right now."))
|
||||
return
|
||||
for(var/thing in software)
|
||||
var/datum/pai_software/S = software[thing]
|
||||
if(istype(S, /datum/pai_software) && S.name == soft_name)
|
||||
if(S.toggle)
|
||||
S.toggle(src)
|
||||
to_chat(src, span_notice("You toggled [S.name]."))
|
||||
refresh_software_status()
|
||||
else
|
||||
S.tgui_interact(src)
|
||||
refresh_software_status()
|
||||
return
|
||||
for(var/thing in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/our_soft = GLOB.pai_software_by_key[thing]
|
||||
if(our_soft.name == soft_name)
|
||||
if(!(ram >= our_soft.ram_cost))
|
||||
to_chat(src, span_warning("Insufficient RAM for download. (Cost [our_soft.ram_cost] : [ram] Remaining)"))
|
||||
return
|
||||
if(tgui_alert(src, "Do you want to download [our_soft.name]? It costs [our_soft.ram_cost], and you have [ram] remaining.", "Download [our_soft.name]", list("Yes", "No")) == "Yes")
|
||||
if(!(ram >= our_soft.ram_cost))
|
||||
return
|
||||
if(software[our_soft.id])
|
||||
return
|
||||
ram -= our_soft.ram_cost
|
||||
software[our_soft.id] = our_soft
|
||||
to_chat(src, span_notice("You downloaded [our_soft.name]. ([ram] RAM remaining.)"))
|
||||
refresh_software_status()
|
||||
|
||||
/mob/living/silicon/pai/proc/refresh_software_status() //This manages the pAI software status buttons icon states based on if you have them and if they are enabled
|
||||
for(var/thing in software) //this only gets called when you click one of the relevent buttons, rather than all the time!
|
||||
var/datum/pai_software/soft = software[thing]
|
||||
if(istype(soft,/datum/pai_software/med_records))
|
||||
soft_mr = TRUE
|
||||
if(istype(soft,/datum/pai_software/sec_records))
|
||||
soft_sr = TRUE
|
||||
if(istype(soft,/datum/pai_software/door_jack))
|
||||
soft_dj = TRUE
|
||||
if(istype(soft,/datum/pai_software/atmosphere_sensor))
|
||||
soft_as = TRUE
|
||||
if(istype(soft,/datum/pai_software/pai_hud))
|
||||
soft_ar = TRUE
|
||||
if(istype(soft,/datum/pai_software/translator))
|
||||
soft_ut = TRUE
|
||||
if(istype(soft,/datum/pai_software/signaller))
|
||||
soft_si = TRUE
|
||||
if(istype(soft,/datum/pai_software/deathalarm))
|
||||
soft_da = TRUE
|
||||
for(var/atom/movable/screen/pai/button in hud_used.other)
|
||||
if(button.name == "medical records")
|
||||
if(soft_mr)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "security records")
|
||||
if(soft_sr)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "door jack")
|
||||
if(soft_dj)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "atmosphere sensor")
|
||||
if(soft_as)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "remote signaler")
|
||||
if(soft_si)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "universal translator")
|
||||
if(soft_ut && translator_on)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "ar hud")
|
||||
if(soft_ar && paiHUD)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
if(button.name == "death alarm")
|
||||
if(soft_da && paiDA)
|
||||
button.icon_state = "[button.base_state]"
|
||||
else
|
||||
button.icon_state = "[button.base_state]_o"
|
||||
|
||||
//Procs for using the various UI buttons for your softwares
|
||||
/mob/living/silicon/pai/proc/directives()
|
||||
touch_window("Directives")
|
||||
|
||||
/mob/living/silicon/pai/proc/crew_manifest()
|
||||
touch_window("Crew Manifest")
|
||||
|
||||
/mob/living/silicon/pai/proc/med_records()
|
||||
touch_window("Medical Records")
|
||||
|
||||
/mob/living/silicon/pai/proc/sec_records()
|
||||
touch_window("Security Records")
|
||||
|
||||
/mob/living/silicon/pai/proc/remote_signal()
|
||||
touch_window("Remote Signaler")
|
||||
|
||||
/mob/living/silicon/pai/proc/atmos_sensor()
|
||||
touch_window("Atmosphere Sensor")
|
||||
|
||||
/mob/living/silicon/pai/proc/translator()
|
||||
touch_window("Universal Translator")
|
||||
|
||||
/mob/living/silicon/pai/proc/door_jack()
|
||||
touch_window("Door Jack")
|
||||
|
||||
/mob/living/silicon/pai/proc/ar_hud()
|
||||
touch_window("AR HUD")
|
||||
|
||||
/mob/living/silicon/pai/proc/death_alarm()
|
||||
touch_window("Death Alarm")
|
||||
|
||||
/mob/living/silicon/pai/proc/get_character_icon()
|
||||
if(!client || !client.prefs) return FALSE
|
||||
var/mob/living/carbon/human/dummy/dummy = new ()
|
||||
//This doesn't include custom_items because that's ... hard.
|
||||
client.prefs.dress_preview_mob(dummy)
|
||||
sleep(1 SECOND) //Strange bug in preview code? Without this, certain things won't show up. Yay race conditions?
|
||||
dummy.regenerate_icons()
|
||||
|
||||
var/icon/new_holo = getCompoundIcon(dummy)
|
||||
|
||||
dummy.tail_layering = TRUE
|
||||
dummy.set_dir(NORTH)
|
||||
var/icon/new_holo_north = getCompoundIcon(dummy)
|
||||
|
||||
qdel(holo_icon)
|
||||
qdel(holo_icon_north)
|
||||
qdel(dummy)
|
||||
holo_icon = new_holo
|
||||
holo_icon_north = new_holo_north
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/set_dir(var/new_dir)
|
||||
. = ..()
|
||||
if(. && (chassis == "13"))
|
||||
switch(dir)
|
||||
if(SOUTH)
|
||||
icon = holo_icon
|
||||
else
|
||||
icon = holo_icon_north
|
||||
|
||||
/mob/living/silicon/pai/adjustBruteLoss(amount, include_robo)
|
||||
. = ..()
|
||||
if(amount > 0 && health <= 90) //Something's probably attacking us!
|
||||
if(prob(amount)) //The more damage it is doing, the more likely it is to damage something important!
|
||||
card.damage_random_component()
|
||||
|
||||
/mob/living/silicon/pai/adjustFireLoss(amount, include_robo)
|
||||
. = ..()
|
||||
if(amount > 0 && health <= 90)
|
||||
if(prob(amount))
|
||||
card.damage_random_component()
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
name
|
||||
key
|
||||
description
|
||||
role
|
||||
comments
|
||||
ready = 0
|
||||
*/
|
||||
|
||||
/datum/paiCandidate/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
|
||||
/datum/paiCandidate/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(src.savefile_path(user))
|
||||
|
||||
|
||||
F["name"] << src.name
|
||||
F["description"] << src.description
|
||||
F["role"] << src.role
|
||||
F["comments"] << src.comments
|
||||
|
||||
F["version"] << 1
|
||||
|
||||
return 1
|
||||
|
||||
// loads the savefile corresponding to the mob's ckey
|
||||
// if silent=true, report incompatible savefiles
|
||||
// returns 1 if loaded (or file was incompatible)
|
||||
// returns 0 if savefile did not exist
|
||||
|
||||
/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1)
|
||||
if (IsGuestKey(user.key))
|
||||
return 0
|
||||
|
||||
var/path = savefile_path(user)
|
||||
|
||||
if (!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/F = new /savefile(path)
|
||||
|
||||
if(!F) return //Not everyone has a pai savefile.
|
||||
|
||||
var/version = null
|
||||
F["version"] >> version
|
||||
|
||||
if (isnull(version) || version != 1)
|
||||
fdel(path)
|
||||
if (!silent)
|
||||
tgui_alert_async(user, "Your savefile was incompatible with this version and was deleted.")
|
||||
return 0
|
||||
|
||||
F["name"] >> src.name
|
||||
F["description"] >> src.description
|
||||
F["role"] >> src.role
|
||||
F["comments"] >> src.comments
|
||||
F["eyecolor"] >> src.eye_color
|
||||
F["chassis"] >> src.chassis
|
||||
F["emotion"] >> src.ouremotion
|
||||
F["gender"] >> src.gender
|
||||
|
||||
return 1
|
||||
@@ -1,398 +0,0 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
|
||||
|
||||
// Recruiting observers to play as pAIs
|
||||
|
||||
var/datum/paiController/paiController // Global handler for pAI candidates
|
||||
|
||||
/datum/paiCandidate
|
||||
var/name
|
||||
var/key
|
||||
var/description
|
||||
var/role
|
||||
var/comments
|
||||
var/ready = 0
|
||||
var/chassis
|
||||
var/ouremotion
|
||||
var/eye_color
|
||||
var/gender
|
||||
|
||||
/hook/startup/proc/paiControllerSetup()
|
||||
paiController = new /datum/paiController()
|
||||
return 1
|
||||
|
||||
|
||||
/datum/paiController
|
||||
var/inquirer = null
|
||||
var/list/pai_candidates = list()
|
||||
var/list/asked = list()
|
||||
|
||||
var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min]
|
||||
|
||||
/datum/paiController/Topic(href, href_list[])
|
||||
if(href_list["download"])
|
||||
var/datum/paiCandidate/candidate = locate(href_list["candidate"])
|
||||
var/obj/item/paicard/card = locate(href_list["device"])
|
||||
if(card.pai)
|
||||
return
|
||||
if(istype(card,/obj/item/paicard) && istype(candidate,/datum/paiCandidate))
|
||||
var/mob/living/silicon/pai/pai = new(card)
|
||||
pai.key = candidate.key
|
||||
GLOB.paikeys |= pai.ckey
|
||||
card.setPersonality(pai)
|
||||
if(!candidate.name)
|
||||
pai.SetName(pick(GLOB.ninja_names))
|
||||
else
|
||||
pai.SetName(candidate.name)
|
||||
if(candidate.description)
|
||||
pai.flavor_text = candidate.description
|
||||
if(candidate.eye_color)
|
||||
pai.eye_color = candidate.eye_color
|
||||
card.screen_color = pai.eye_color
|
||||
if(candidate.chassis)
|
||||
pai.chassis = candidate.chassis
|
||||
if(candidate.ouremotion)
|
||||
card.setEmotion(candidate.ouremotion)
|
||||
if(candidate.gender)
|
||||
pai.gender = candidate.gender
|
||||
pai.update_icon()
|
||||
pai.real_name = pai.name
|
||||
card.looking_for_personality = 0
|
||||
|
||||
if(pai.mind) update_antag_icons(pai.mind)
|
||||
|
||||
pai_candidates -= candidate
|
||||
usr << browse(null, "window=findPai")
|
||||
|
||||
if(href_list["new"])
|
||||
var/datum/paiCandidate/candidate = locate(href_list["candidate"])
|
||||
var/option = href_list["option"]
|
||||
var/t = ""
|
||||
|
||||
switch(option)
|
||||
if("name")
|
||||
t = sanitizeSafe(tgui_input_text(usr, "Enter a name for your pAI", "pAI Name", candidate.name, MAX_NAME_LEN, encode = FALSE), MAX_NAME_LEN)
|
||||
if(t)
|
||||
candidate.name = t
|
||||
if("desc")
|
||||
t = tgui_input_text(usr, "Enter a description for your pAI", "pAI Description", candidate.description, multiline = TRUE, prevent_enter = TRUE)
|
||||
if(t)
|
||||
candidate.description = sanitize(t, encode = FALSE)
|
||||
if("role")
|
||||
t = tgui_input_text(usr, "Enter a role for your pAI", "pAI Role", candidate.role)
|
||||
if(t)
|
||||
candidate.role = sanitize(t, encode = FALSE)
|
||||
if("ooc")
|
||||
t = tgui_input_text(usr, "Enter any OOC comments", "pAI OOC Comments", candidate.comments, multiline = TRUE, prevent_enter = TRUE)
|
||||
if(t)
|
||||
candidate.comments = sanitize(t, encode = FALSE)
|
||||
if("save")
|
||||
candidate.savefile_save(usr)
|
||||
if("load")
|
||||
candidate.savefile_load(usr)
|
||||
//In case people have saved unsanitized stuff.
|
||||
if(candidate.name)
|
||||
candidate.name = sanitizeSafe(candidate.name, MAX_NAME_LEN)
|
||||
if(candidate.description)
|
||||
candidate.description = sanitize(candidate.description)
|
||||
if(candidate.role)
|
||||
candidate.role = sanitize(candidate.role)
|
||||
if(candidate.comments)
|
||||
candidate.comments = sanitize(candidate.comments)
|
||||
|
||||
if("submit")
|
||||
if(candidate)
|
||||
candidate.ready = 1
|
||||
for(var/obj/item/paicard/p in GLOB.all_pai_cards)
|
||||
if(p.looking_for_personality == 1)
|
||||
p.alertUpdate()
|
||||
usr << browse(null, "window=paiRecruit")
|
||||
return
|
||||
|
||||
recruitWindow(usr, href_list["allow_submit"] != "0")
|
||||
|
||||
/datum/paiController/proc/recruitWindow(var/mob/M as mob, allowSubmit = 1)
|
||||
var/datum/paiCandidate/candidate
|
||||
for(var/datum/paiCandidate/c in pai_candidates)
|
||||
if(!istype(c) || !istype(M))
|
||||
break
|
||||
if(c.key == M.key)
|
||||
candidate = c
|
||||
if(!candidate)
|
||||
candidate = new /datum/paiCandidate()
|
||||
candidate.key = M.key
|
||||
pai_candidates.Add(candidate)
|
||||
|
||||
var/dat = ""
|
||||
dat += {"
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin-top:5px;
|
||||
font-family:Verdana;
|
||||
color:white;
|
||||
font-size:13px;
|
||||
background-image:url('uiBackground.png');
|
||||
background-repeat:repeat-x;
|
||||
background-color:#272727;
|
||||
background-position:center top;
|
||||
}
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
font-size:13px;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #333333;
|
||||
}
|
||||
p.top {
|
||||
background-color: none;
|
||||
color: white;
|
||||
}
|
||||
tr.d0 td {
|
||||
background-color: #c0c0c0;
|
||||
color: black;
|
||||
border:0px;
|
||||
border: 1px solid #333333;
|
||||
}
|
||||
tr.d0 th {
|
||||
background-color: none;
|
||||
color: #4477E0;
|
||||
text-align:right;
|
||||
vertical-align:top;
|
||||
width:120px;
|
||||
border:0px;
|
||||
}
|
||||
tr.d1 td {
|
||||
background-color: #555555;
|
||||
color: white;
|
||||
}
|
||||
td.button {
|
||||
border: 1px solid #161616;
|
||||
background-color: #40628a;
|
||||
}
|
||||
td.desc {
|
||||
font-weight:bold;
|
||||
}
|
||||
a {
|
||||
color:#4477E0;
|
||||
}
|
||||
a.button {
|
||||
color:white;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
"}
|
||||
|
||||
dat += {"
|
||||
<body>
|
||||
<b><font size="3px">pAI Personality Configuration</font></b>
|
||||
<p class="top">Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!</p>
|
||||
|
||||
<table>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=\ref[src];option=name;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]'>Name</a>:</th>
|
||||
<td class="desc">[candidate.name] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.</td>
|
||||
</tr>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=\ref[src];option=desc;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]'>Description</a>:</th>
|
||||
<td class="desc">[candidate.description] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.</td>
|
||||
</tr>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=\ref[src];option=role;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]'>Preferred Role</a>:</th>
|
||||
<td class="desc">[candidate.role] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.</td>
|
||||
</tr>
|
||||
<tr class="d0">
|
||||
<th rowspan="2"><a href='byond://?src=\ref[src];option=ooc;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]'>OOC Comments</a>:</th>
|
||||
<td class="desc">[candidate.comments] </td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<td>Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="button">
|
||||
<a href='byond://?src=\ref[src];option=save;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]' class="button">Save Personality</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="button">
|
||||
<a href='byond://?src=\ref[src];option=load;new=1;allow_submit=[allowSubmit];candidate=\ref[candidate]' class="button"><b><font size="3px">Load Personality</font></b></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table><br>
|
||||
"}
|
||||
if(allowSubmit)
|
||||
dat += {"
|
||||
<table>
|
||||
<td class="button"><a href='byond://?src=\ref[src];option=submit;new=1;candidate=\ref[candidate]' class="button"><b><font size="4px">Submit Personality</font></b></a></td>
|
||||
</table><br>
|
||||
"}
|
||||
dat += {"
|
||||
</body>
|
||||
"}
|
||||
|
||||
M << browse("<html>[dat]</html>", "window=paiRecruit;size=580x580;")
|
||||
|
||||
/datum/paiController/proc/findPAI(var/obj/item/paicard/p, var/mob/user)
|
||||
requestRecruits(user)
|
||||
var/list/available = list()
|
||||
for(var/datum/paiCandidate/c in paiController.pai_candidates)
|
||||
if(c.ready)
|
||||
var/found = 0
|
||||
for(var/mob/observer/dead/o in GLOB.player_list)
|
||||
if(o.key == c.key && o.MayRespawn())
|
||||
found = 1
|
||||
if(found)
|
||||
available.Add(c)
|
||||
var/dat = ""
|
||||
|
||||
dat += {"
|
||||
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
margin-top:5px;
|
||||
font-family:Verdana;
|
||||
color:white;
|
||||
font-size:13px;
|
||||
background-image:url('uiBackground.png');
|
||||
background-repeat:repeat-x;
|
||||
background-color:#272727;
|
||||
background-position:center top;
|
||||
}
|
||||
table {
|
||||
font-size:13px;
|
||||
}
|
||||
table.desc {
|
||||
border-collapse:collapse;
|
||||
font-size:13px;
|
||||
border: 1px solid #161616;
|
||||
width:100%;
|
||||
}
|
||||
table.download {
|
||||
border-collapse:collapse;
|
||||
font-size:13px;
|
||||
border: 1px solid #161616;
|
||||
width:100%;
|
||||
}
|
||||
tr.d0 td, tr.d0 th {
|
||||
background-color: #506070;
|
||||
color: white;
|
||||
}
|
||||
tr.d1 td, tr.d1 th {
|
||||
background-color: #708090;
|
||||
color: white;
|
||||
}
|
||||
tr.d2 td {
|
||||
background-color: #00FF00;
|
||||
color: white;
|
||||
text-align:center;
|
||||
}
|
||||
td.button {
|
||||
border: 1px solid #161616;
|
||||
background-color: #40628a;
|
||||
text-align: center;
|
||||
}
|
||||
td.download {
|
||||
border: 1px solid #161616;
|
||||
background-color: #40628a;
|
||||
text-align: center;
|
||||
}
|
||||
th {
|
||||
text-align:left;
|
||||
width:125px;
|
||||
vertical-align:top;
|
||||
}
|
||||
a.button {
|
||||
color:white;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<b><font size='3px'>pAI Availability List</font></b><br><br>
|
||||
"}
|
||||
dat += "<p>Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.</p>"
|
||||
|
||||
for(var/datum/paiCandidate/c in available)
|
||||
dat += {"
|
||||
<table class="desc">
|
||||
<tr class="d0">
|
||||
<th>Name:</th>
|
||||
<td>[c.name]</td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<th>Description:</th>
|
||||
<td>[c.description]</td>
|
||||
</tr>
|
||||
<tr class="d0">
|
||||
<th>Preferred Role:</th>
|
||||
<td>[c.role]</td>
|
||||
</tr>
|
||||
<tr class="d1">
|
||||
<th>OOC Comments:</th>
|
||||
<td>[c.comments]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="download">
|
||||
<td class="download"><a href='byond://?src=\ref[src];download=1;candidate=\ref[c];device=\ref[p]' class="button"><b>Download [c.name]</b></a>
|
||||
</td>
|
||||
</table>
|
||||
<br>
|
||||
"}
|
||||
|
||||
dat += {"
|
||||
</body>
|
||||
</html>
|
||||
"}
|
||||
|
||||
user << browse(dat, "window=findPai")
|
||||
|
||||
|
||||
/datum/paiController/proc/requestRecruits(var/mob/user)
|
||||
inquirer = user
|
||||
for(var/mob/observer/dead/O in GLOB.player_list)
|
||||
if(!O.MayRespawn())
|
||||
continue
|
||||
if(jobban_isbanned(O, "pAI"))
|
||||
continue
|
||||
if(asked.Find(O.key))
|
||||
if(world.time < asked[O.key] + askDelay)
|
||||
continue
|
||||
else
|
||||
asked.Remove(O.key)
|
||||
if(O.client)
|
||||
if(O.client.prefs.be_special & BE_PAI)
|
||||
question(O.client)
|
||||
|
||||
/datum/paiController/proc/question(var/client/C)
|
||||
spawn(0)
|
||||
if(!C) return
|
||||
asked.Add(C.key)
|
||||
asked[C.key] = world.time
|
||||
|
||||
var/mob/ourmob = C.mob
|
||||
if(ourmob)
|
||||
var/time_till_respawn = ourmob.time_till_respawn()
|
||||
if(time_till_respawn == -1 || time_till_respawn)
|
||||
return
|
||||
for(var/ourkey in GLOB.paikeys)
|
||||
if(ourkey == ourmob.ckey)
|
||||
return
|
||||
|
||||
var/response = tgui_alert(C, "[inquirer] is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", list("Yes", "No", "Never for this round"))
|
||||
if(!C) return //handle logouts that happen whilst the alert is waiting for a response.
|
||||
if(response == "Yes")
|
||||
recruitWindow(C.mob)
|
||||
else if (response == "Never for this round")
|
||||
C.prefs.be_special ^= BE_PAI
|
||||
@@ -1,39 +1,3 @@
|
||||
var/list/pai_emotions = list(
|
||||
"Neutral" = 1,
|
||||
"What" = 2,
|
||||
"Happy" = 3,
|
||||
"Cat" = 4,
|
||||
"Extremely Happy" = 5,
|
||||
"Face" = 6,
|
||||
"Laugh" = 7,
|
||||
"Sad" = 8,
|
||||
"Angry" = 9,
|
||||
"Silly" = 10,
|
||||
"Nose" = 11,
|
||||
"Smirk" = 12,
|
||||
"Exclamation Points" = 13,
|
||||
"Question Mark" = 14,
|
||||
"Blank" = 15,
|
||||
"Off" = 16
|
||||
)
|
||||
|
||||
|
||||
GLOBAL_LIST_EMPTY(pai_software_by_key)
|
||||
GLOBAL_LIST_EMPTY(default_pai_software)
|
||||
/hook/startup/proc/populate_pai_software_list()
|
||||
var/r = 1 // I would use ., but it'd sacrifice runtime detection
|
||||
for(var/type in subtypesof(/datum/pai_software))
|
||||
var/datum/pai_software/P = new type()
|
||||
if(GLOB.pai_software_by_key[P.id])
|
||||
var/datum/pai_software/O = GLOB.pai_software_by_key[P.id]
|
||||
to_chat(world, span_warning("pAI software module [P.name] has the same key as [O.name]!"))
|
||||
r = 0
|
||||
continue
|
||||
GLOB.pai_software_by_key[P.id] = P
|
||||
if(P.default)
|
||||
GLOB.default_pai_software[P.id] = P
|
||||
return r
|
||||
|
||||
/mob/living/silicon/pai/Initialize(mapload)
|
||||
. = ..()
|
||||
software = GLOB.default_pai_software.Copy()
|
||||
@@ -79,11 +43,12 @@ GLOBAL_LIST_EMPTY(default_pai_software)
|
||||
|
||||
// Emotions
|
||||
var/list/emotions = list()
|
||||
for(var/name in pai_emotions)
|
||||
var/list/emote = list()
|
||||
emote["name"] = name
|
||||
emote["id"] = pai_emotions[name]
|
||||
emotions.Add(list(emote))
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/list/emote = list(
|
||||
"displayText" = name,
|
||||
"value" = GLOB.pai_emotions[name]
|
||||
)
|
||||
UNTYPED_LIST_ADD(emotions, emote)
|
||||
|
||||
data["emotions"] = emotions
|
||||
data["current_emotion"] = card.current_emotion
|
||||
@@ -114,6 +79,6 @@ GLOBAL_LIST_EMPTY(default_pai_software)
|
||||
|
||||
if("image")
|
||||
var/img = text2num(params["image"])
|
||||
if(1 <= img && img <= (pai_emotions.len))
|
||||
if(1 <= img && img <= (GLOB.pai_emotions.len))
|
||||
card.setEmotion(img)
|
||||
return TRUE
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/datum/pai_sprite
|
||||
var/name = DEVELOPER_WARNING_NAME
|
||||
var/hidden = FALSE
|
||||
|
||||
var/sprite_icon = null
|
||||
var/sprite_icon_state = null
|
||||
var/has_eye_sprites = TRUE
|
||||
var/flying = FALSE
|
||||
var/belly_states = 1
|
||||
|
||||
var/can_rest = TRUE // We really shouldn't be permitting this... eventually. Remove when all sprite can rest.
|
||||
var/resting_belly = TRUE
|
||||
|
||||
var/holo_projector = FALSE // Special AI hologram-like handling
|
||||
var/holo_eyes_icon_state = null
|
||||
|
||||
var/pixel_x = 0
|
||||
var/pixel_y = 0
|
||||
var/icon_x = 32
|
||||
var/icon_y = 32
|
||||
var/vis_height = 32
|
||||
|
||||
// PAI sprites
|
||||
/datum/pai_sprite/drone
|
||||
name = PAI_DEFAULT_CHASSIS
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-repairbot"
|
||||
|
||||
/datum/pai_sprite/cat
|
||||
name = "Cat"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-cat"
|
||||
|
||||
/datum/pai_sprite/mouse
|
||||
name = "Mouse"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-mouse"
|
||||
|
||||
/datum/pai_sprite/monkey
|
||||
name = "Monkey"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-monkey"
|
||||
|
||||
/datum/pai_sprite/borgi
|
||||
name = "Borgi"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-borgi"
|
||||
has_eye_sprites = FALSE
|
||||
|
||||
/datum/pai_sprite/fox
|
||||
name = "Fox"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-fox"
|
||||
has_eye_sprites = FALSE
|
||||
|
||||
/datum/pai_sprite/parrot
|
||||
name = "Parrot"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-parrot"
|
||||
has_eye_sprites = FALSE
|
||||
flying = TRUE
|
||||
|
||||
/datum/pai_sprite/rabbit
|
||||
name = "Rabbit"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-rabbit"
|
||||
has_eye_sprites = FALSE
|
||||
|
||||
/datum/pai_sprite/bear
|
||||
name = "Bear"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-bear"
|
||||
|
||||
/datum/pai_sprite/fennec
|
||||
name = "Fennec"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-fen"
|
||||
belly_states = 0
|
||||
|
||||
/datum/pai_sprite/type_zero
|
||||
name = "Type Zero"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-typezero"
|
||||
|
||||
/datum/pai_sprite/raccoon
|
||||
name = "Raccoon"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-raccoon"
|
||||
|
||||
/datum/pai_sprite/raptor
|
||||
name = "Raptor"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-raptor"
|
||||
|
||||
/datum/pai_sprite/corgi
|
||||
name = "Corgi"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-corgi"
|
||||
has_eye_sprites = FALSE
|
||||
|
||||
/datum/pai_sprite/bat
|
||||
name = "Bat"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-bat"
|
||||
flying = TRUE
|
||||
|
||||
/datum/pai_sprite/butterfly
|
||||
name = "Butterfly"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-butterfly"
|
||||
flying = TRUE
|
||||
belly_states = 0
|
||||
|
||||
/datum/pai_sprite/hawk
|
||||
name = "Hawk"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-hawk"
|
||||
has_eye_sprites = FALSE
|
||||
flying = TRUE
|
||||
belly_states = 0
|
||||
|
||||
/datum/pai_sprite/duffel
|
||||
name = "Duffel"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "pai-duffel"
|
||||
has_eye_sprites = FALSE
|
||||
|
||||
/datum/pai_sprite/cyber_elf
|
||||
name = "Cyber Elf"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "cyberelf"
|
||||
flying = TRUE
|
||||
resting_belly = FALSE
|
||||
|
||||
/datum/pai_sprite/catslug
|
||||
name = "Catslug"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "catslug"
|
||||
resting_belly = FALSE
|
||||
|
||||
/datum/pai_sprite/car
|
||||
name = "Car"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "car"
|
||||
|
||||
/datum/pai_sprite/type_one
|
||||
name = "Type One"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
sprite_icon_state = "typeone"
|
||||
|
||||
/datum/pai_sprite/type_thirteen
|
||||
name = "Type Thirteen"
|
||||
sprite_icon = 'icons/mob/pai.dmi'
|
||||
holo_projector = TRUE
|
||||
sprite_icon_state = "type13"
|
||||
holo_eyes_icon_state = "type13-eyes"
|
||||
|
||||
// Large pai sprites
|
||||
/datum/pai_sprite/large
|
||||
pixel_x = -16
|
||||
icon_x = 64
|
||||
icon_y = 64
|
||||
vis_height = 64
|
||||
|
||||
/datum/pai_sprite/large/dire_wolf
|
||||
name = "Dire wolf"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "pai-diredog"
|
||||
|
||||
/datum/pai_sprite/large/horse_lune
|
||||
name = "Horse (Lune)"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "pai-horse_lune"
|
||||
can_rest = FALSE // FIX ME!
|
||||
resting_belly = FALSE
|
||||
|
||||
/datum/pai_sprite/large/horse_soleil
|
||||
name = "Horse (Soleil)"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "pai-horse_soleil"
|
||||
can_rest = FALSE // FIX ME!
|
||||
resting_belly = FALSE
|
||||
|
||||
/datum/pai_sprite/large/dragon
|
||||
name = "Dragon"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "pai-pdragon"
|
||||
can_rest = FALSE // FIX ME!
|
||||
resting_belly = FALSE
|
||||
|
||||
/datum/pai_sprite/large/rat
|
||||
name = "Rat"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "rat"
|
||||
|
||||
/datum/pai_sprite/large/panther
|
||||
name = "Panther"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "panther"
|
||||
|
||||
/datum/pai_sprite/large/teppi
|
||||
name = "Teppi"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "teppi"
|
||||
|
||||
/datum/pai_sprite/large/protogen_dog
|
||||
name = "Protogen Dog"
|
||||
sprite_icon = 'icons/mob/pai64x64.dmi'
|
||||
sprite_icon_state = "pai-protodog"
|
||||
@@ -84,7 +84,7 @@
|
||||
model_type = "wide"
|
||||
if(istype(S, /datum/robot_sprite/dogborg/tall))
|
||||
model_type = "tall"
|
||||
available_sprites += list(list("sprite" = S.name, "belly" = S.has_vore_belly_sprites, "type" = model_type))
|
||||
UNTYPED_LIST_ADD(available_sprites, list("sprite" = S.name, "belly" = S.has_vore_belly_sprites, "type" = model_type))
|
||||
|
||||
data["possible_sprites"] = available_sprites
|
||||
data["sprite_datum"] = sprite_datum
|
||||
|
||||
@@ -141,6 +141,11 @@
|
||||
QDEL_NULL(mind)
|
||||
qdel(src)
|
||||
|
||||
// pAI notify if we have be pAI invite on
|
||||
SSpai.clear_pai_block_delay(REF(observer)) // Reset invite cooldown if we cancelled all invites for the round
|
||||
if(SSpai.invite_valid(observer))
|
||||
observer.pai_card_ping()
|
||||
|
||||
return TRUE
|
||||
if("shownews")
|
||||
handle_server_news()
|
||||
|
||||
@@ -363,8 +363,8 @@
|
||||
|
||||
//Eye color/icon
|
||||
var/should_have_eyes = owner.should_have_organ(O_EYES)
|
||||
var/has_eye_color = owner.species.appearance_flags & HAS_EYE_COLOR
|
||||
if((should_have_eyes || has_eye_color) && eye_icon)
|
||||
var/has_eye_sprites = owner.species.appearance_flags & HAS_EYE_COLOR
|
||||
if((should_have_eyes || has_eye_sprites) && eye_icon)
|
||||
var/obj/item/organ/internal/eyes/eyes = owner.internal_organs_by_name[O_EYES]
|
||||
var/icon/eyes_icon = new/icon(eye_icon_location, eye_icon)
|
||||
//Do we have a special eye icon with its own coloration? Remove
|
||||
@@ -373,7 +373,7 @@
|
||||
if(should_have_eyes)
|
||||
//And we have them
|
||||
if(eyes)
|
||||
if(has_eye_color)
|
||||
if(has_eye_sprites)
|
||||
eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD)
|
||||
//They're gone!
|
||||
else
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
return
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/computer/power_monitor/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
// Uses dark magic to operate the NanoUI of this computer.
|
||||
/obj/machinery/computer/power_monitor/tgui_interact(mob/user, var/datum/tgui/ui = null)
|
||||
power_monitor.tgui_interact(user, ui)
|
||||
|
||||
@@ -342,6 +342,9 @@
|
||||
to_chat(user, "You adjust the speed of the conveyor switch")
|
||||
return
|
||||
|
||||
/obj/machinery/conveyor_switch/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
/obj/machinery/conveyor_switch/oneway
|
||||
oneway = 1
|
||||
|
||||
|
||||
@@ -170,6 +170,9 @@
|
||||
user.visible_message("[user] places \the [I] into the [src].", "You place \the [I] into the [src].","Ca-Clunk")
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/disposal/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
// Transform into next machine type
|
||||
/obj/machinery/disposal/proc/alter_bin_type(mob/user)
|
||||
if(contents.len > 0)
|
||||
|
||||
@@ -124,10 +124,10 @@
|
||||
|
||||
// Emotions
|
||||
var/list/emotions = list()
|
||||
for(var/name in pai_emotions)
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/list/emote = list()
|
||||
emote["name"] = name
|
||||
emote["id"] = pai_emotions[name]
|
||||
emote["id"] = GLOB.pai_emotions[name]
|
||||
emotions.Add(list(emote))
|
||||
|
||||
data["emotions"] = emotions
|
||||
|
||||
@@ -72,7 +72,11 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
|
||||
. = min(., shared_living_tgui_distance(src_object)) //simple animals can only use things they're near.
|
||||
|
||||
/mob/living/silicon/pai/default_can_use_tgui_topic(src_object)
|
||||
// pAIs can only use themselves and the owner's radio.
|
||||
// Allows few objects...
|
||||
var/obj/check_obj = src_object
|
||||
if(!stat && istype(check_obj) && check_obj.allow_pai_interaction(get_dist(check_obj, src) <= 1))
|
||||
return STATUS_INTERACTIVE
|
||||
// ...otherwise pAIs can only use themselves and the owner's radio.
|
||||
if((src_object == src || src_object == radio || src_object == communicator) && !stat)
|
||||
return STATUS_INTERACTIVE
|
||||
else
|
||||
|
||||
@@ -97,6 +97,9 @@
|
||||
return
|
||||
lift.queue_move_to(floor)
|
||||
|
||||
/obj/structure/lift/button/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
/obj/structure/lift/button/proc/light_up()
|
||||
light_up = TRUE
|
||||
update_icon()
|
||||
@@ -140,6 +143,9 @@
|
||||
/obj/structure/lift/panel/attack_ghost(var/mob/user)
|
||||
return interact(user)
|
||||
|
||||
/obj/structure/lift/panel/allow_pai_interaction(proximity_flag)
|
||||
return proximity_flag
|
||||
|
||||
/obj/structure/lift/panel/interact(var/mob/user)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
@@ -106,7 +106,9 @@
|
||||
#include "mapping.dm"
|
||||
#include "material_tests.dm"
|
||||
// #include "nuke_cinematic.dm" // TODO: This is probably fixed later on
|
||||
#include "pai_tests.dm"
|
||||
#include "poster_tests.dm"
|
||||
#include "special_roles.dm"
|
||||
// #include "preferences.dm" // This unit test is missing some other stuff
|
||||
#include "reagent_tests.dm"
|
||||
#include "recipe_tests.dm"
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/datum/unit_test/pai_software_shall_have_unique_id
|
||||
|
||||
/datum/unit_test/pai_software_shall_have_unique_id/Run()
|
||||
for(var/datum/pai_software/P as anything in subtypesof(/datum/pai_software))
|
||||
// Compare against all other pai softwares
|
||||
for(var/datum/pai_software/O as anything in subtypesof(/datum/pai_software))
|
||||
if(P == O) // Don't test against ourselves
|
||||
continue
|
||||
if(initial(P.id) == initial(O.id))
|
||||
TEST_FAIL("pAI software module [P] has the same id as [O]!")
|
||||
|
||||
/datum/unit_test/pai_sprites_shall_be_valid
|
||||
|
||||
/datum/unit_test/pai_sprites_shall_be_valid/Run()
|
||||
var/failed = FALSE
|
||||
var/list/used_names = list()
|
||||
for(var/sprite_key in SSpai.get_chassis_list())
|
||||
var/datum/pai_sprite/sprite = SSpai.chassis_data(sprite_key)
|
||||
if(sprite.sprite_icon == null)
|
||||
continue
|
||||
|
||||
if(sprite.name in used_names)
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite name is not unique: \"[sprite.name]\"")
|
||||
failed = TRUE
|
||||
used_names.Add(sprite.name)
|
||||
|
||||
if(!sprite.name || sprite.name == DEVELOPER_WARNING_NAME)
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite had invalid name: \"[sprite.name]\"")
|
||||
failed = TRUE
|
||||
|
||||
|
||||
if(!sprite.holo_projector)
|
||||
// check if we have valid icons
|
||||
if(!sprite.sprite_icon_state)
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite had no iconstate assigned")
|
||||
failed = TRUE
|
||||
|
||||
if(!icon_exists(sprite.sprite_icon, sprite.sprite_icon_state))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
|
||||
if(sprite.can_rest)
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]_rest"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]_rest\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
|
||||
// Check for vore stuff
|
||||
if(sprite.belly_states > 0)
|
||||
for(var/i = 1 to sprite.belly_states)
|
||||
var/fullness_extension = ""
|
||||
if(i > 1)
|
||||
fullness_extension = "_[i]"
|
||||
// standing bellies
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]_full[fullness_extension]"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]_full[fullness_extension]\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
if(sprite.can_rest && sprite.resting_belly)
|
||||
// resting bellies
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]_rest_full[fullness_extension]"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]_rest_full[fullness_extension]\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
|
||||
// Does it have colorable eyes?
|
||||
if(sprite.has_eye_sprites)
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]-eyes"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]-eyes\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
if(sprite.can_rest)
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]_rest-eyes"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]_rest-eyes\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
|
||||
// Death
|
||||
if(!icon_exists(sprite.sprite_icon, "[sprite.sprite_icon_state]_dead"))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai sprite state \"[sprite.sprite_icon_state]_dead\" did not exist in the dmi set \"[sprite.sprite_icon]\"")
|
||||
failed = TRUE
|
||||
|
||||
else // Holoprojector uses ai holo instead of a sprite
|
||||
// Holoprojectil eyes need to exist at all sizes for the state provided
|
||||
if(sprite.holo_eyes_icon_state)
|
||||
if(!icon_exists('icons/mob/pai.dmi', sprite.holo_eyes_icon_state))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai holoprojection eyes state \"[sprite.holo_eyes_icon_state]\" did not exist in the dmi set \"['icons/mob/pai.dmi']\"")
|
||||
failed = TRUE
|
||||
if(!icon_exists('icons/mob/pai32x64.dmi', sprite.holo_eyes_icon_state))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai holoprojection eyes state \"[sprite.holo_eyes_icon_state]\" did not exist in the dmi set \"['icons/mob/pai32x64.dmi']\"")
|
||||
failed = TRUE
|
||||
if(!icon_exists('icons/mob/pai64x32.dmi', sprite.holo_eyes_icon_state))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai holoprojection eyes state \"[sprite.holo_eyes_icon_state]\" did not exist in the dmi set \"['icons/mob/pai64x32.dmi']\"")
|
||||
failed = TRUE
|
||||
if(!icon_exists('icons/mob/pai64x64.dmi', sprite.holo_eyes_icon_state))
|
||||
TEST_NOTICE(src, "[sprite.type] - Pai holoprojection eyes state \"[sprite.holo_eyes_icon_state]\" did not exist in the dmi set \"['icons/mob/pai64x64.dmi']\"")
|
||||
failed = TRUE
|
||||
|
||||
if(failed)
|
||||
TEST_FAIL("PAI sprites had invalid entries.")
|
||||
@@ -0,0 +1,13 @@
|
||||
/datum/unit_test/special_role_flags_syncronized
|
||||
|
||||
/datum/unit_test/special_role_flags_syncronized/Run()
|
||||
if(GLOB.special_roles.len != GLOB.be_special_flags.len)
|
||||
TEST_FAIL("GLOB.special_roles and GLOB.be_special_flags lengths do not match. [GLOB.special_roles] vs [GLOB.be_special_flags].")
|
||||
|
||||
// These get out of order way too often.
|
||||
var/index = 1
|
||||
for(var/key in GLOB.special_roles)
|
||||
var/check_key = GLOB.be_special_flags[index]
|
||||
if(lowertext(check_key) != lowertext(key))
|
||||
TEST_FAIL("Special role flag misalignment index: [index], be_special_flags: [check_key], special_roles: [key]. Keys must also match when caste to lowercase.")
|
||||
index++
|
||||
Reference in New Issue
Block a user