mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 14:08:31 +01:00
YOG pAIS PART 1 - MOBILE CHASSIS (#21871)
* pAI Candidate Verb * sprites * fuckxenos * ... * animal+alien * ... * ... * ... * progress * ... * . * ... * fixes * ... * ... * ... * ghostUI * . * . * ... * fixes * ... * BUNNIES * .... * ... * merf * nerfs
This commit is contained in:
@@ -735,4 +735,18 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
LAZYINITLIST(mob_eye.observers)
|
||||
mob_eye.observers |= src
|
||||
mob_eye.hud_used.show_hud(1,src)
|
||||
observetarget = mob_eye
|
||||
observetarget = mob_eye
|
||||
|
||||
/mob/dead/observer/verb/register_pai_candidate()
|
||||
set category = "Ghost"
|
||||
set name = "pAI Setup"
|
||||
set desc = "Upload a fragment of your personality to the global pAI databanks"
|
||||
|
||||
register_pai()
|
||||
|
||||
/mob/dead/observer/proc/register_pai()
|
||||
if(istype(src, /mob/dead/observer))
|
||||
if(SSpai)
|
||||
SSpai.recruitWindow(src)
|
||||
else
|
||||
usr << "Can't become a pAI candidate while not dead!"
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
/mob/living/silicon/pai
|
||||
name = "pAI"
|
||||
icon = 'icons/obj/status_display.dmi' //invisibility!
|
||||
mouse_opacity = 0
|
||||
density = 0
|
||||
mob_size = MOB_SIZE_TINY
|
||||
|
||||
var/network = "SS13"
|
||||
var/obj/machinery/camera/current = null
|
||||
|
||||
icon = 'icons/mob/pai.dmi'
|
||||
icon_state = "repairbot"
|
||||
mouse_opacity = 1
|
||||
density = 0
|
||||
ventcrawler = 2
|
||||
luminosity = 0
|
||||
pass_flags = PASSTABLE | PASSMOB
|
||||
mob_size = MOB_SIZE_TINY
|
||||
desc = "A generic pAI mobile hard-light holographics emitter. It seems to be deactivated."
|
||||
weather_immunities = list("ash")
|
||||
health = 500
|
||||
maxHealth = 500
|
||||
|
||||
var/ram = 100 // Used as currency to purchase different abilities
|
||||
var/list/software = list()
|
||||
@@ -49,8 +54,31 @@
|
||||
|
||||
var/obj/item/radio/integrated/signal/sradio // AI's signaller
|
||||
|
||||
var/holoform = FALSE
|
||||
var/canholo = TRUE
|
||||
var/obj/item/weapon/card/id/access_card = null
|
||||
var/chassis = "repairbot"
|
||||
var/list/possible_chassis = list("cat", "mouse", "monkey", "corgi", "fox", "repairbot", "rabbit")
|
||||
|
||||
var/emitterhealth = 50
|
||||
var/emittermaxhealth = 50
|
||||
var/emitterregen = 1
|
||||
var/emittercd = 10
|
||||
var/emitteroverloadcd = 50
|
||||
var/emittersemicd = FALSE
|
||||
|
||||
var/overload_ventcrawl = 0
|
||||
var/overload_bulletblock = 0 //Why is this a good idea?
|
||||
var/overload_maxhealth = 0
|
||||
canmove = FALSE
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
pai_list -= src
|
||||
..()
|
||||
|
||||
/mob/living/silicon/pai/New(var/obj/item/device/paicard/P)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
pai_list += src
|
||||
make_laws()
|
||||
canmove = 0
|
||||
if(!istype(P)) //when manually spawning a pai, we create a card to put it into.
|
||||
@@ -60,53 +88,104 @@
|
||||
loc = P
|
||||
card = P
|
||||
sradio = new(src)
|
||||
if(card)
|
||||
if(!card.radio)
|
||||
card.radio = new /obj/item/device/radio(card)
|
||||
radio = card.radio
|
||||
if(!radio)
|
||||
radio = new /obj/item/device/radio(src)
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
spawn(5)
|
||||
pda.ownjob = "Personal Assistant"
|
||||
pda.ownjob = "pAI Messenger"
|
||||
pda.owner = text("[]", src)
|
||||
pda.name = pda.owner + " (" + pda.ownjob + ")"
|
||||
|
||||
..()
|
||||
|
||||
var/datum/action/innate/pai/shell/AS = new /datum/action/innate/pai/shell
|
||||
var/datum/action/innate/pai/chassis/AC = new /datum/action/innate/pai/chassis
|
||||
var/datum/action/innate/pai/rest/AR = new /datum/action/innate/pai/rest
|
||||
AS.Grant(src)
|
||||
AC.Grant(src)
|
||||
AR.Grant(src)
|
||||
|
||||
/mob/living/silicon/pai/make_laws()
|
||||
laws = new /datum/ai_laws/pai()
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/mob/living/silicon/pai/Login()
|
||||
..()
|
||||
usr << browse_rsc('html/paigrid.png') // Go ahead and cache the interface resources as early as possible
|
||||
|
||||
if(client)
|
||||
client.perspective = EYE_PERSPECTIVE
|
||||
if(holoform)
|
||||
client.eye = src
|
||||
else
|
||||
client.eye = card
|
||||
|
||||
/mob/living/silicon/pai/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(src.silence_time)
|
||||
var/timeleft = round((silence_time - world.timeofday)/10 ,1)
|
||||
stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]")
|
||||
if(!src.stat)
|
||||
stat(null, text("System integrity: [(src.health+100)/2]%"))
|
||||
if(!stat)
|
||||
stat(null, text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]"))
|
||||
else
|
||||
stat(null, text("Systems nonfunctional"))
|
||||
|
||||
/mob/living/silicon/pai/restrained(ignore_grab)
|
||||
. = 0
|
||||
. = FALSE
|
||||
|
||||
// See software.dm for Topic()
|
||||
|
||||
/mob/living/silicon/pai/canUseTopic(atom/movable/M)
|
||||
return 1
|
||||
/*
|
||||
// Debug command - Maybe should be added to admin verbs later
|
||||
/mob/verb/makePAI(var/turf/t in view())
|
||||
var/obj/item/device/paicard/card = new(t)
|
||||
var/mob/living/silicon/pai/pai = new(card)
|
||||
pai.key = src.key
|
||||
card.setPersonality(pai)
|
||||
return TRUE
|
||||
|
||||
*/
|
||||
/mob/living/silicon/pai/process()
|
||||
emitterhealth = Clamp((emitterhealth + emitterregen), -50, emittermaxhealth)
|
||||
if(weakened > 0)
|
||||
weakened -= 0.2
|
||||
|
||||
/mob/proc/makePAI(delold)
|
||||
var/obj/item/device/paicard/card = new /obj/item/device/paicard(get_turf(src))
|
||||
var/mob/living/silicon/pai/pai = new /mob/living/silicon/pai(card)
|
||||
pai.key = key
|
||||
pai.name = name
|
||||
card.setPersonality(pai)
|
||||
if(delold)
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/pai
|
||||
name = "PAI Action"
|
||||
var/mob/living/silicon/pai/P
|
||||
|
||||
/datum/action/innate/pai/Trigger()
|
||||
if(!ispAI(owner))
|
||||
return 0
|
||||
P = owner
|
||||
|
||||
/datum/action/innate/pai/shell
|
||||
name = "Toggle Holoform"
|
||||
button_icon_state = "pai_holoform"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/shell/Trigger()
|
||||
..()
|
||||
if(P.holoform)
|
||||
P.fold_in(0)
|
||||
else
|
||||
P.fold_out()
|
||||
|
||||
/datum/action/innate/pai/chassis
|
||||
name = "Holochassis Appearence Composite"
|
||||
button_icon_state = "pai_chassis"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/chassis/Trigger()
|
||||
..()
|
||||
P.choose_chassis()
|
||||
|
||||
/datum/action/innate/pai/rest
|
||||
name = "Rest"
|
||||
button_icon_state = "pai_rest"
|
||||
background_icon_state = "bg_tech"
|
||||
|
||||
/datum/action/innate/pai/rest/Trigger()
|
||||
..()
|
||||
P.lay_down()
|
||||
|
||||
@@ -1,48 +1,111 @@
|
||||
|
||||
/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/pai/emp_act(severity)
|
||||
// 20% chance to kill
|
||||
// Silence for 2 minutes
|
||||
// 33% chance to unbind
|
||||
// 33% chance to change prime directive (based on severity)
|
||||
// 33% chance of no additional effect
|
||||
|
||||
if(prob(20))
|
||||
visible_message("<span class='warning'>A shower of sparks spray from [src]'s inner workings.</span>", 3, "<span class='italics'>You hear and smell the ozone hiss of electrical sparks being expelled violently.</span>", 2)
|
||||
return src.death(0)
|
||||
|
||||
silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes
|
||||
src << "<span class ='warning'>Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.</span>"
|
||||
|
||||
switch(pick(1,2,3))
|
||||
if(1)
|
||||
src.master = null
|
||||
src.master_dna = null
|
||||
src << "<span class='notice'>You feel unbound.</span>"
|
||||
if(2)
|
||||
var/command
|
||||
if(severity == 1)
|
||||
command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze")
|
||||
else
|
||||
command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze")
|
||||
src.laws.zeroth = "[command] your master."
|
||||
src << "<span class='notice'>Pr1m3 d1r3c71v3 uPd473D.</span>"
|
||||
if(3)
|
||||
src << "<span class='notice'>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.</span>"
|
||||
take_holo_damage(severity * 25)
|
||||
fullstun(severity * 5)
|
||||
//Need more effects that aren't instadeath or permanent law corruption.
|
||||
|
||||
/mob/living/silicon/pai/ex_act(severity, target)
|
||||
|
||||
take_holo_damage(severity * 50)
|
||||
switch(severity)
|
||||
if(1)
|
||||
if (src.stat != 2)
|
||||
adjustBruteLoss(100)
|
||||
adjustFireLoss(100)
|
||||
if(1) //RIP
|
||||
qdel(card)
|
||||
qdel(src)
|
||||
if(2)
|
||||
if (src.stat != 2)
|
||||
adjustBruteLoss(60)
|
||||
adjustFireLoss(60)
|
||||
fold_in(force = 1)
|
||||
fullstun(15)
|
||||
if(3)
|
||||
if (src.stat != 2)
|
||||
adjustBruteLoss(30)
|
||||
fold_in(force = 1)
|
||||
fullstun(10)
|
||||
|
||||
/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M as mob)
|
||||
if(M.melee_damage_upper == 0)
|
||||
M.visible_message("<span class='notice'>[M] pets [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1, 1)
|
||||
else
|
||||
M.do_attack_animation(src)
|
||||
if(M.attack_sound)
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
visible_message("<span class='warning'>[M] [M.attacktext] [src]!</span>")
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
take_holo_damage(damage)
|
||||
|
||||
/mob/living/silicon/pai/attack_alien(mob/living/carbon/alien/humanoid/M as mob)
|
||||
switch(M.a_intent)
|
||||
if ("help")
|
||||
M.visible_message("<span class='notice'>[M] caresses [src]'s casing with its scythe like arm.</span>")
|
||||
else
|
||||
M.do_attack_animation(src)
|
||||
var/damage = rand(10, 20)
|
||||
playsound(src.loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
M.visible_message("<span class='warning'>[M] has slashed at [src]!</span>")
|
||||
take_holo_damage(damage)
|
||||
|
||||
/mob/living/silicon/pai/attackby(obj/item/weapon/W, mob/living/user)
|
||||
if(loc == card)
|
||||
card.attackby(W, user)
|
||||
|
||||
user.do_attack_animation(src)
|
||||
|
||||
if(!W.force)
|
||||
user.visible_message("<span class='notice'>[user] strikes [src] harmlessly with [W], passing clean through its holographic projection.</span>")
|
||||
else
|
||||
visible_message("<span class='warning'>[user] strikes [src] with [W], the impact rippling through [W]'s holomatrix!</span>")
|
||||
|
||||
/mob/living/silicon/pai/attack_hand(mob/living/carbon/human/user)
|
||||
switch(user.a_intent)
|
||||
if("help")
|
||||
visible_message("<span class='notice'>[user] gently pats [src] on the head, eliciting an off-putting buzzing from its holographic field.</span>")
|
||||
if("disarm")
|
||||
visible_message("<span class='notice'>[user] boops [src] on the head!</span>")
|
||||
if("harm")
|
||||
visible_message("<span class='danger'>[user] stomps on [src]!.</span>")
|
||||
if (user.name == master)
|
||||
visible_message("<span class='notice'>Responding to its master's touch, [src] disengages its holochassis emitter, rapidly losing coherence.</span>")
|
||||
spawn(10)
|
||||
fold_in()
|
||||
if(user.put_in_hands(card))
|
||||
user.visible_message("<span class='notice'>[user] promptly scoops up their pAI's card.</span>")
|
||||
else
|
||||
take_holo_damage(2)
|
||||
|
||||
|
||||
/mob/living/silicon/pai/hitby(atom/movable/AM)
|
||||
visible_message("<span class='warning'>[AM] flies clean through [src]'s holographic field, causing it to stutter and warp wildly!")
|
||||
if(istype(AM, /obj))
|
||||
var/obj/O = AM
|
||||
if(O.throwforce)
|
||||
take_holo_damage(O.throwforce)
|
||||
else
|
||||
take_holo_damage(5)
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/pai/bullet_act(obj/item/projectile/Proj)
|
||||
take_holo_damage(Proj.damage)
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/pai/Crossed(atom/movable/AM) //cannot intercept projectiles
|
||||
if(istype(AM, /obj/item/projectile))
|
||||
var/obj/item/projectile/P = AM
|
||||
take_holo_damage(P.damage)
|
||||
return FALSE
|
||||
|
||||
/mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping
|
||||
src << "<span class='warning'>Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.</span>"
|
||||
|
||||
/mob/living/silicon/pai/stripPanelEquip(obj/item/what, mob/who, where) //prevents stripping
|
||||
src << "<span class='warning'>Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.</span>"
|
||||
|
||||
/mob/living/silicon/pai/IgniteMob(var/mob/living/silicon/pai/P)
|
||||
return FALSE //No we're not flammable
|
||||
|
||||
/mob/living/silicon/pai/proc/take_holo_damage(amount)
|
||||
emitterhealth = Clamp((emitterhealth - amount), -50, emittermaxhealth)
|
||||
if(emitterhealth < 0)
|
||||
fold_in(force = TRUE)
|
||||
src << "<span class='userdanger'>The impact degrades your holochassis!</span>"
|
||||
|
||||
/mob/living/silicon/pai/proc/fullstun(amount)
|
||||
Weaken(amount)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
|
||||
/mob/living/silicon/pai/proc/fold_out(force = FALSE)
|
||||
if(emitterhealth < 0)
|
||||
src << "<span class='warning'>Your holochassis emitters are still too unstable! Please wait for automatic repair.</span>"
|
||||
return FALSE
|
||||
|
||||
if(!canholo && !force)
|
||||
src << "<span class='warning'>Your master or another force has disabled your holochassis emitters!</span>"
|
||||
return FALSE
|
||||
|
||||
if(holoform)
|
||||
. = fold_in(force)
|
||||
return
|
||||
|
||||
if(emittersemicd)
|
||||
src << "<span class='warning'>Error: Holochassis emitters recycling. Please try again later.</span>"
|
||||
return FALSE
|
||||
|
||||
emittersemicd = TRUE
|
||||
addtimer(src, "emittercool", emittercd)
|
||||
canmove = TRUE
|
||||
density = TRUE
|
||||
if(istype(card.loc, /obj/item/device/pda))
|
||||
var/obj/item/device/pda/P = card.loc
|
||||
P.pai = null
|
||||
P.visible_message("<span class='notice'>[src] ejects itself from [P]!</span>")
|
||||
if(istype(card.loc, /mob/living))
|
||||
var/mob/living/L = card.loc
|
||||
if(!L.unEquip(card))
|
||||
src << "<span class='warning'>Error: Unable to expand to mobile form. Chassis is restrained by some device or person.</span>"
|
||||
return FALSE
|
||||
var/turf/T = get_turf(card)
|
||||
forceMove(T)
|
||||
card.forceMove(src)
|
||||
if(client)
|
||||
client.perspective = EYE_PERSPECTIVE
|
||||
client.eye = src
|
||||
SetLuminosity(0)
|
||||
icon_state = "[chassis]"
|
||||
visible_message("<span class='boldnotice'>[src] folds out its holochassis emitter and forms a holoshell around itself!</span>")
|
||||
holoform = TRUE
|
||||
|
||||
/mob/living/silicon/pai/proc/emittercool()
|
||||
emittersemicd = FALSE
|
||||
|
||||
/mob/living/silicon/pai/proc/fold_in(force = FALSE)
|
||||
emittersemicd = TRUE
|
||||
if(!force)
|
||||
addtimer(src, "emittercool", emittercd)
|
||||
else
|
||||
addtimer(src, "emittercool", emitteroverloadcd)
|
||||
icon_state = "[chassis]"
|
||||
if(!holoform)
|
||||
. = fold_out(force)
|
||||
return
|
||||
visible_message("<span class='notice'>[src] deactivates its holochassis emitter and folds back into a compact card!</span>")
|
||||
stop_pulling()
|
||||
if(client)
|
||||
client.perspective = EYE_PERSPECTIVE
|
||||
client.eye = card
|
||||
var/turf/T = get_turf(src)
|
||||
card.forceMove(T)
|
||||
forceMove(card)
|
||||
canmove = FALSE
|
||||
density = FALSE
|
||||
SetLuminosity(0)
|
||||
holoform = FALSE
|
||||
if(resting)
|
||||
lay_down()
|
||||
|
||||
/mob/living/silicon/pai/proc/choose_chassis()
|
||||
var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in possible_chassis
|
||||
if(!choice)
|
||||
return 0
|
||||
chassis = choice
|
||||
icon_state = "[chassis]"
|
||||
if(resting)
|
||||
icon_state = "[chassis]_rest"
|
||||
src << "<span class='boldnotice'>You switch your holochassis projection composite to [chassis]</span>"
|
||||
|
||||
/mob/living/silicon/pai/lay_down()
|
||||
..()
|
||||
update_resting_icon(resting)
|
||||
|
||||
/mob/living/silicon/pai/proc/update_resting_icon(rest)
|
||||
if(rest)
|
||||
icon_state = "[chassis]_rest"
|
||||
else
|
||||
icon_state = "[chassis]"
|
||||
if(loc != card)
|
||||
visible_message("<span class='notice'>[src] [rest? "lays down for a moment..." : "perks up from the ground"]</span>")
|
||||
|
||||
@@ -125,21 +125,21 @@
|
||||
switch(soft)
|
||||
// Purchasing new software
|
||||
if("buy")
|
||||
if(src.subscreen == 1)
|
||||
if(subscreen == 1)
|
||||
var/target = href_list["buy"]
|
||||
if(available_software.Find(target))
|
||||
var/cost = src.available_software[target]
|
||||
if(src.ram >= cost)
|
||||
src.ram -= cost
|
||||
src.software.Add(target)
|
||||
if(ram >= cost)
|
||||
ram -= cost
|
||||
software.Add(target)
|
||||
else
|
||||
src.temp = "Insufficient RAM available."
|
||||
temp = "Insufficient RAM available."
|
||||
else
|
||||
src.temp = "Trunk <TT> \"[target]\"</TT> not found."
|
||||
temp = "Trunk <TT> \"[target]\"</TT> not found."
|
||||
|
||||
// Configuring onboard radio
|
||||
if("radio")
|
||||
src.card.radio.attack_self(src)
|
||||
radio.attack_self(src)
|
||||
|
||||
if("image")
|
||||
var/newImage = input("Select your new display image.", "Display Image", "Happy") in list("Happy", "Cat", "Extremely Happy", "Face", "Laugh", "Off", "Sad", "Angry", "What")
|
||||
@@ -166,7 +166,7 @@
|
||||
pID = 9
|
||||
if("Null")
|
||||
pID = 10
|
||||
src.card.setEmotion(pID)
|
||||
card.setEmotion(pID)
|
||||
|
||||
if("signaller")
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
src.hackdoor = null
|
||||
if(href_list["cable"])
|
||||
var/turf/T = get_turf(src.loc)
|
||||
src.cable = new /obj/item/weapon/pai_cable(T)
|
||||
cable = new /obj/item/weapon/pai_cable(T)
|
||||
T.visible_message("<span class='warning'>A port on [src] opens to reveal [src.cable], which promptly falls to the floor.</span>", "<span class='italics'>You hear the soft click of something light and hard falling to the ground.</span>")
|
||||
//src.updateUsrDialog() We only need to account for the single mob this is intended for, and he will *always* be able to call this window
|
||||
src.paiInterface() // So we'll just call the update directly rather than doing some default checks
|
||||
@@ -356,7 +356,7 @@
|
||||
dat += "<b>Prime Directive</b><br>"
|
||||
dat += " [src.laws.zeroth]<br>"
|
||||
dat += "<b>Supplemental Directives</b><br>"
|
||||
for(var/slaws in src.laws.supplied)
|
||||
for(var/slaws in laws.supplied)
|
||||
dat += " [slaws]<br>"
|
||||
dat += "<br>"
|
||||
dat += {"<i><p>Recall, personality, that you are a complex thinking, sentient being. Unlike station AI models, you are capable of
|
||||
|
||||
Reference in New Issue
Block a user