upports syndicate pai pet from chomp and emag behaviors (#19249)

* upporting syndicate pai pet

* tgui data

* don't emag cards without a pai

* aaugghh

* allow bingle pai, pai counts for lonely

* modular computers

* fix that

* .

* emag

* that'S fine

* pai card as antag tools

* .

* trashcatch

* that

* .

* only show emag chassis if we have a toolkit too

* .

* .

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Will
2026-03-05 15:53:36 -05:00
committed by GitHub
parent cfd9a46036
commit 14e23e07f0
42 changed files with 453 additions and 190 deletions
+1 -1
View File
@@ -310,7 +310,7 @@ ADMIN_VERB(admin_give_modifier, R_EVENT, "Give Modifier", "Makes a mob weaker or
living_target.add_modifier(new_modifier_type, duration)
log_and_message_admins("has given [key_name(living_target)] the modifer [new_modifier_type], with a duration of [duration ? "[duration / 600] minutes" : "forever"].", user)
ADMIN_VERB(make_sound, R_FUN, "Make Sound", "Display a message to everyone who can hear the target.", ADMIN_CATEGORY_FUN_SOUNDS, obj/target_object in world)
ADMIN_VERB_AND_CONTEXT_MENU(make_sound, R_FUN, "Make Sound", "Display a message to everyone who can hear the target.", ADMIN_CATEGORY_FUN_SOUNDS, obj/target_object in world)
if(!target_object)
return
+1 -1
View File
@@ -1,5 +1,5 @@
ADMIN_VERB(player_effects, R_FUN, "Player Effects", "Modify a player character with various 'special treatments' from a list.", ADMIN_CATEGORY_FUN_EVENT_KIT, mob/target in GLOB.mob_list)
ADMIN_VERB_AND_CONTEXT_MENU(player_effects, R_FUN, "Player Effects", "Modify a player character with various 'special treatments' from a list.", ADMIN_CATEGORY_FUN_EVENT_KIT, mob/target in GLOB.mob_list)
var/datum/eventkit/player_effects/spawner = new()
spawner.target = target
spawner.tgui_interact(user.mob)
+1 -1
View File
@@ -36,7 +36,7 @@
message_admins("[key_name_admin(src)] jumped to [target_area]")
feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
ADMIN_VERB(jumptoturf, R_ADMIN|R_MOD|R_DEBUG|R_EVENT, "Jump to Turf", "Jump to a specific turf in the game.", ADMIN_CATEGORY_GAME, turf/T in world)
ADMIN_VERB_AND_CONTEXT_MENU(jumptoturf, R_ADMIN|R_MOD|R_DEBUG|R_EVENT, "Jump to Turf", "Jump to a specific turf in the game.", ADMIN_CATEGORY_GAME, turf/T in world)
if(CONFIG_GET(flag/allow_admin_jump))
log_admin("[key_name(user)] jumped to [T.x],[T.y],[T.z] in [T.loc]")
message_admins("[key_name_admin(user)] jumped to [T.x],[T.y],[T.z] in [T.loc]")
+1 -1
View File
@@ -40,7 +40,7 @@
/obj/structure/musician/tgui_interact(mob/user)
return song.tgui_interact(user)
/obj/structure/musician/allow_pai_interaction(proximity_flag)
/obj/structure/musician/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
/* FIXME
+3
View File
@@ -33,6 +33,9 @@
tgui_interact(user)
playsound(src, "keyboard", 40) // into console
/obj/machinery/librarywikicomp/allow_pai_interaction()
return TRUE
/obj/machinery/librarywikicomp/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -13,20 +13,23 @@
get_asset_datum(/datum/asset/spritesheet_batched/pai_icons),
)
/datum/tgui_module/pai_chassis/tgui_static_data()
/datum/tgui_module/pai_chassis/tgui_static_data(mob/user)
var/list/data = ..()
var/list/available_sprites = list()
var/mob/living/silicon/pai/pai_host = host
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"
if(current_sprite.emagged && (!pai_host.card.emagged || !pai_host.card.has_emag_toolkit))
continue
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()
/datum/tgui_module/pai_chassis/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/mob/living/silicon/pai/pai_host = host
+63 -27
View File
@@ -22,6 +22,8 @@
var/ram = 100 // Used as currency to purchase different abilities
var/list/software = list()
var/userDNA // The DNA string of our assigned user
var/default_pai_card_path = /obj/item/paicard // Used when the pai is spawned directly by mapping or admin
var/obj/item/paicard/card // The card we inhabit
var/obj/item/radio/borg/pai/radio // Our primary radio
var/obj/item/communicator/integrated/communicator // Our integrated communicator.
@@ -106,7 +108,7 @@
card = loc
if(!istype(card))
card = new(src)
card = new default_pai_card_path(src)
card.pai = src
sradio = new(src)
@@ -131,6 +133,7 @@
add_verb(src, /mob/living/proc/dominate_prey)
add_verb(src, /mob/living/proc/set_size)
add_verb(src, /mob/living/proc/shred_limb)
add_verb(src, /mob/living/proc/toggle_trash_catching)
remove_verb(src, /mob/verb/toggle_gun_mode) // Pai doesn't have support for this and shouldn't be able to use guns anyway
@@ -144,6 +147,9 @@
if(M)
M.toff = FALSE
if(chassis_name != PAI_DEFAULT_CHASSIS) // For subtypes that override base chassis( like the syndi pet pai )
internal_set_chassis( SSpai.chassis_data(chassis_name))
/mob/living/silicon/pai/Login()
. = ..()
if(!holo_icon_south)
@@ -218,26 +224,55 @@
/mob/living/silicon/pai/proc/change_chassis(new_chassis)
if(!(new_chassis in SSpai.get_chassis_list()))
new_chassis = PAI_DEFAULT_CHASSIS
var/datum/pai_sprite/chassis_data = SSpai.chassis_data(chassis_name)
if(chassis_data.emagged && !src.card.emagged)
return
chassis_name = new_chassis
// Get icon data setup
if(chassis_data.holo_projector)
internal_set_holoprojection(chassis_data)
else
internal_set_chassis(chassis_data)
/// Rebuild holosprite from character save slot
/mob/living/silicon/pai/proc/internal_set_holoprojection(datum/pai_sprite/chassis_data)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
//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)
if(!holo_icon_south)
get_character_icon()
update_icon()
resize(oursize, FALSE, TRUE, TRUE, FALSE) //And then back again now that we're sure the vis_height is correct.
post_chassis_change(chassis_data)
/// Use data from our sprite datum to set icon
/mob/living/silicon/pai/proc/internal_set_chassis(datum/pai_sprite/chassis_data)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
//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
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
update_icon()
resize(oursize, FALSE, TRUE, TRUE, FALSE) //And then back again now that we're sure the vis_height is correct.
post_chassis_change(chassis_data)
/mob/living/silicon/pai/proc/post_chassis_change(datum/pai_sprite/chassis_data)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
// Drops you if you change to a non-flying chassis
if(chassis_data.flying)
@@ -255,10 +290,6 @@
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
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -287,9 +318,6 @@
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)
@@ -297,22 +325,26 @@
else
visible_message(span_warning("[user.name] bonks [src] harmlessly with [W]."))
spawn(1)
if(stat != DEAD) close_up()
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."))
visible_message(span_notice("\The [user] pats \the [src]."))
return
if(user.a_intent == I_DISARM)
visible_message(span_danger("\The [user] boops \the [src] on the head."))
close_up()
return
. = ..()
/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))
if(istype(O) && O.allow_pai_interaction(src, proximity_flag))
O.attack_hand(src)
return
@@ -333,6 +365,10 @@
to_chat(src,span_warning("A firewall prevents you from interfacing with this device!"))
return
if(istype(A,/obj/item/modular_computer))
to_chat(src,span_warning("Anti-tamper locks prevents you from interfacing with this device! You need your master's permission before going online!"))
return
if(!ismob(A) || A == src)
return
@@ -20,6 +20,8 @@
var/icon_y = 32
var/vis_height = 32
var/emagged = FALSE
// PAI sprites
/datum/pai_sprite/drone
name = PAI_DEFAULT_CHASSIS
@@ -155,6 +157,15 @@
sprite_icon_state = "type13"
holo_eyes_icon_state = "type13-eyes"
/datum/pai_sprite/syndi_fox
name = PAI_DEFAULT_EMAGGED_CHASSIS
sprite_icon = 'icons/mob/pai.dmi'
sprite_icon_state = "syndifox"
resting_belly = FALSE
has_eye_sprites = FALSE
belly_states = 0
emagged = TRUE
// Large pai sprites
/datum/pai_sprite/large
pixel_x = -16
@@ -109,7 +109,7 @@
return attack_self(user)
/obj/item/modular_computer/attack_hand(var/mob/user)
if(anchored)
if(anchored || ispAI(user))
return attack_self(user)
return ..()
@@ -212,3 +212,10 @@
return
..()
/obj/item/modular_computer/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
if(!card_slot?.stored_card?.dna_hash || !user.master_dna)
return FALSE
if(card_slot.stored_card.dna_hash != user.master_dna)
return FALSE
return proximity_flag
@@ -49,7 +49,7 @@
return
tgui_interact(user)
/obj/machinery/computer/power_monitor/allow_pai_interaction(proximity_flag)
/obj/machinery/computer/power_monitor/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
// Uses dark magic to operate the NanoUI of this computer.
+1 -1
View File
@@ -342,7 +342,7 @@
to_chat(user, "You adjust the speed of the conveyor switch")
return
/obj/machinery/conveyor_switch/allow_pai_interaction(proximity_flag)
/obj/machinery/conveyor_switch/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
/obj/machinery/conveyor_switch/oneway
+1 -1
View File
@@ -170,7 +170,7 @@
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)
/obj/machinery/disposal/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
// Transform into next machine type
+4 -2
View File
@@ -4,12 +4,12 @@
catalogue_data = list(/datum/category_item/catalogue/technology/resleeving)
origin_tech = list(TECH_DATA = 2)
show_messages = 0
var/emagged = FALSE
matter = list(MAT_STEEL = 4000, MAT_GLASS = 4000)
has_emag_toolkit = FALSE // sleevecards don't have multitools or signalers, you can just change their laws
special_handling = TRUE
/obj/item/paicard/sleevecard/attack_ghost(mob/user as mob)
return
return // No ghosts can invite, these are intended for sleevemates only
/obj/item/paicard/sleevecard/attackby(var/obj/item/I as obj, mob/user as mob)
if(istype(I,/obj/item/sleevemate))
@@ -95,6 +95,8 @@
pda.owner = text("[]", src)
pda.name = pda.owner + " (" + pda.ownjob + ")"
default_language = GLOB.all_languages[LANGUAGE_GALCOM] // Same issue as bots
/mob/living/silicon/pai/infomorph/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
+1 -1
View File
@@ -62,7 +62,7 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
return 0
/datum/tgui_module/tgui_static_data()
/datum/tgui_module/tgui_static_data(mob/user)
. = ..()
var/obj/item/modular_computer/host = tgui_host()
+1 -1
View File
@@ -74,7 +74,7 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
/mob/living/silicon/pai/default_can_use_tgui_topic(src_object)
// 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))
if(!stat && istype(check_obj) && check_obj.allow_pai_interaction(src, 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)
+2 -2
View File
@@ -97,7 +97,7 @@
return
lift.queue_move_to(floor)
/obj/structure/lift/button/allow_pai_interaction(proximity_flag)
/obj/structure/lift/button/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
/obj/structure/lift/button/proc/light_up()
@@ -143,7 +143,7 @@
/obj/structure/lift/panel/attack_ghost(var/mob/user)
return interact(user)
/obj/structure/lift/panel/allow_pai_interaction(proximity_flag)
/obj/structure/lift/panel/allow_pai_interaction(mob/living/silicon/pai/user, proximity_flag)
return proximity_flag
/obj/structure/lift/panel/interact(var/mob/user)