diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 1d5e0f7b05b..e9124580793 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -292,7 +292,7 @@ if("monkey") M.change_mob_type( /mob/living/carbon/monkey , null, null, delmob ) if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) if("cat") M.change_mob_type( /mob/living/simple_animal/cat , null, null, delmob ) - if("runtime") M.change_mob_type( /mob/living/simple_animal/cat/Runtime , null, null, delmob ) + if("runtime") M.change_mob_type( /mob/living/simple_animal/cat/fluff/Runtime , null, null, delmob ) if("corgi") M.change_mob_type( /mob/living/simple_animal/corgi , null, null, delmob ) if("ian") M.change_mob_type( /mob/living/simple_animal/corgi/Ian , null, null, delmob ) if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob ) diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 95593385a00..781d88feb52 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -1412,12 +1412,6 @@ ////////////////////////////// Foxler - Erstatz Vryroxes ///////////////////////////////////////////////// -/obj/item/weapon/holder/cat/fluff/bones - name = "Bones" - desc = "It's Bones! Meow." - gender = MALE - icon_state = "cat3" - //Use this subtype for spawning in the custom item. /obj/item/weapon/holder/cat/fluff/bones/custom_item @@ -1426,6 +1420,12 @@ new/mob/living/simple_animal/cat/fluff/bones (src) ..() +/obj/item/weapon/holder/cat/fluff/bones + name = "Bones" + desc = "It's Bones! Meow." + gender = MALE + icon_state = "cat3" + /mob/living/simple_animal/cat/fluff/bones name = "Bones" desc = "That's Bones the cat. He's a laid back, black cat. Meow." @@ -1434,61 +1434,12 @@ icon_living = "cat3" icon_dead = "cat3_dead" holder_type = /obj/item/weapon/holder/cat/fluff/bones - bff_name = "Erstatz Vryroxes" + var/friend_name = "Erstatz Vryroxes" -/mob/living/simple_animal/cat/fluff - var/bff_name - var/mob/living/carbon/human/bff - -/mob/living/simple_animal/cat/fluff/handle_movement_target() - if (!bff) +/mob/living/simple_animal/cat/fluff/bones/handle_movement_target() + if (!friend) for (var/mob/living/carbon/human/M in player_list) - if (M.real_name == bff_name) - bff = M + if (M.real_name == friend_name) + friend = M break - - if (bff) - var/follow_dist = 5 - if (bff.stat >= DEAD || bff.health <= config.health_threshold_softcrit) //danger - follow_dist = 1 - else if (bff.stat || bff.health <= 50) //danger or just sleeping - follow_dist = 2 - var/near_dist = max(follow_dist - 3, 1) - var/current_dist = get_dist(src, bff) - - if (movement_target != bff) - if (current_dist > follow_dist && !istype(movement_target, /mob/living/simple_animal/mouse) && (bff in oview(src))) - //stop existing movement - walk_to(src,0) - turns_since_scan = 0 - - //walk to bff - stop_automated_movement = 1 - movement_target = bff - walk_to(src, movement_target, near_dist, 4) - - //already following and close enough, stop - else if (current_dist <= near_dist) - walk_to(src,0) - movement_target = null - stop_automated_movement = 0 - - if (!(bff && movement_target == bff)) - ..() - -/mob/living/simple_animal/cat/fluff/Life() ..() - if (stat || !bff) - return - if (get_dist(src, bff) <= 1) - if (bff.stat >= DEAD || bff.health <= config.health_threshold_softcrit) - if (prob((bff.stat < DEAD)? 50 : 15)) - audible_emote(pick("meows in distress.", "meows anxiously.")) - else - if (prob(5)) - visible_emote(pick("nuzzles [bff].", - "brushes against [bff].", - "rubs against [bff].", - "purrs.")) - else if (bff.health <= 50) - if (prob(10)) audible_emote("meows anxiously.") diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index 8e012d8517f..cbedbec6bc2 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -1635,7 +1635,7 @@ proc/populate_seed_list() seed_noun = "data" display_name = "runtimes" packet_icon = "seed-replicapod" - products = list(/mob/living/simple_animal/cat/Runtime) + products = list(/mob/living/simple_animal/cat/fluff/Runtime) plant_icon = "replicapod" requires_nutrients = 0 diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 7913d23d7ed..19acdc03064 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -18,6 +18,7 @@ response_harm = "kicks" var/turns_since_scan = 0 var/mob/living/simple_animal/mouse/movement_target + var/mob/flee_target min_oxy = 16 //Require atleast 16kPA oxygen minbodytemp = 223 //Below -50 Degrees Celcius maxbodytemp = 323 //Above 50 Degrees Celcius @@ -44,27 +45,71 @@ break if(!stat && !resting && !buckled) - handle_movement_target() + turns_since_scan++ + if (turns_since_scan > 5) + walk_to(src,0) + turns_since_scan = 0 + + if (flee_target) //fleeing takes precendence + handle_flee_target() + else + handle_movement_target() -/mob/living/simple_animal/cat/proc/handle_movement_target() - turns_since_scan++ - if(turns_since_scan > 5) - walk_to(src,0) - turns_since_scan = 0 +/mob/living/simple_animal/cat/proc/handle_movement_target() + //if our target is neither inside a turf or inside a human(???), stop + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + stop_automated_movement = 0 + //if we have no target or our current one is out of sight/too far away + if( !movement_target || !(movement_target.loc in oview(src, 4)) ) + movement_target = null + stop_automated_movement = 0 + for(var/mob/living/simple_animal/mouse/snack in oview(src)) //search for a new target + if(isturf(snack.loc) && !snack.stat) + movement_target = snack + break + + if(movement_target) + stop_automated_movement = 1 + walk_to(src,movement_target,0,3) - if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) - movement_target = null - stop_automated_movement = 0 - if( !movement_target || !(movement_target.loc in oview(src, 4)) ) - movement_target = null - stop_automated_movement = 0 - for(var/mob/living/simple_animal/mouse/snack in oview(src)) - if(isturf(snack.loc) && !snack.stat) - movement_target = snack - break - if(movement_target) - stop_automated_movement = 1 - walk_to(src,movement_target,0,3) +/mob/living/simple_animal/cat/proc/handle_flee_target() + //see if we should stop fleeing + if (flee_target && !(flee_target.loc in view(src))) + flee_target = null + stop_automated_movement = 0 + + if (flee_target) + if(prob(25)) say("HSSSSS") + stop_automated_movement = 1 + walk_away(src, flee_target, 7, 2) + +/mob/living/simple_animal/cat/proc/set_flee_target(atom/A) + if(A) + flee_target = A + turns_since_scan = 5 + +/mob/living/simple_animal/cat/attackby(var/obj/item/O, var/mob/user) + . = ..() + if(O.force) + set_flee_target(user? user : src.loc) + +/mob/living/simple_animal/cat/attack_hand(mob/living/carbon/human/M as mob) + . = ..() + if(M.a_intent == "hurt") + set_flee_target(M) + +/mob/living/simple_animal/cat/ex_act() + . = ..() + set_flee_target(src.loc) + +/mob/living/simple_animal/cat/bullet_act(var/obj/item/projectile/proj) + . = ..() + set_flee_target(proj.firer? proj.firer : src.loc) + +/mob/living/simple_animal/cat/hitby(atom/movable/AM) + . = ..() + set_flee_target(AM.thrower? AM.thrower : src.loc) /mob/living/simple_animal/cat/MouseDrop(atom/over_object) @@ -82,14 +127,91 @@ return //since the holder icon looks like a living cat ..() +//Basic friend AI +/mob/living/simple_animal/cat/fluff + var/mob/living/carbon/human/friend + var/befriend_job = null + +/mob/living/simple_animal/cat/fluff/handle_movement_target() + if (friend) + var/follow_dist = 5 + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) //danger + follow_dist = 1 + else if (friend.stat || friend.health <= 50) //danger or just sleeping + follow_dist = 2 + var/near_dist = max(follow_dist - 2, 1) + var/current_dist = get_dist(src, friend) + + if (movement_target != friend) + if (current_dist > follow_dist && !istype(movement_target, /mob/living/simple_animal/mouse) && (friend in oview(src))) + //stop existing movement + walk_to(src,0) + turns_since_scan = 0 + + //walk to friend + stop_automated_movement = 1 + movement_target = friend + walk_to(src, movement_target, near_dist, 4) + + //already following and close enough, stop + else if (current_dist <= near_dist) + walk_to(src,0) + movement_target = null + stop_automated_movement = 0 + if (prob(10)) + say("Meow!") + + if (!friend || movement_target != friend) + ..() + +/mob/living/simple_animal/cat/fluff/Life() + ..() + if (stat || !friend) + return + if (get_dist(src, friend) <= 1) + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) + if (prob((friend.stat < DEAD)? 50 : 15)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) + else + if (prob(5)) + visible_emote(pick("nuzzles [friend].", + "brushes against [friend].", + "rubs against [friend].", + "purrs.")) + else if (friend.health <= 50) + if (prob(10)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote("[verb] anxiously.") + +/mob/living/simple_animal/cat/fluff/verb/friend() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + if(friend && usr == friend) + set_dir(get_dir(src, friend)) + say("Meow!") + return + + if (!(ishuman(usr) && befriend_job && usr.job == befriend_job)) + usr << "[src] ignores you." + return + + friend = usr + + set_dir(get_dir(src, friend)) + say("Meow!") + //RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/cat/Runtime +/mob/living/simple_animal/cat/fluff/Runtime name = "Runtime" desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." gender = FEMALE icon_state = "cat" icon_living = "cat" icon_dead = "cat_dead" + befriend_job = "Chief Medical Officer" /mob/living/simple_animal/cat/kitten name = "kitten" @@ -97,4 +219,8 @@ icon_state = "kitten" icon_living = "kitten" icon_dead = "kitten_dead" - gender = NEUTER \ No newline at end of file + gender = NEUTER + +/mob/living/simple_animal/cat/kitten/New() + gender = pick(MALE, FEMALE) + ..() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 74f6c85eb97..9fa5c1da9a5 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -265,7 +265,7 @@ adjustBruteLoss(-medical_pack.heal_brute) visible_message("\The [user] applies the [medical_pack] to \the [src].") else - user << "\The [src] cannot benefit from medical items in its current state." + user << "\The [src] cannot benefit from medical items in \his current state." return else if(istype(O, /obj/item/weapon/kitchenknife) || istype(O, /obj/item/weapon/butch)) diff --git a/maps/exodus-1.dmm b/maps/exodus-1.dmm index 31319bdbdfc..d8309c90723 100644 --- a/maps/exodus-1.dmm +++ b/maps/exodus-1.dmm @@ -4938,7 +4938,7 @@ "bQX" = (/turf/simulated/floor{icon_state = "white_2"},/area/medical/medbay) "bQY" = (/obj/machinery/atmospherics/unary/outlet_injector{dir = 4; frequency = 1441; icon_state = "map_injector"; id = "n2_in"; use_power = 1},/turf/simulated/floor/engine,/area/rnd/misc_lab) "bQZ" = (/turf/simulated/floor{tag = "icon-whiteblue (WEST)"; icon_state = "whiteblue"; dir = 8},/area/crew_quarters/heads/cmo) -"bRa" = (/mob/living/simple_animal/cat/Runtime,/turf/simulated/floor{icon_state = "white"},/area/crew_quarters/heads/cmo) +"bRa" = (/mob/living/simple_animal/cat/fluff/Runtime,/turf/simulated/floor{icon_state = "white"},/area/crew_quarters/heads/cmo) "bRb" = (/obj/structure/bed/chair/office/dark{dir = 1},/obj/effect/landmark/start{name = "Chief Medical Officer"},/obj/machinery/door_control{desc = "A remote control-switch for shutters."; id = "cmooffice"; name = "CMO Privacy Shutters"; pixel_x = 38; pixel_y = 21},/obj/machinery/door_control{desc = "A remote control switch for the CMO's office."; id = "cmodoor"; name = "CMO Office Door Control"; normaldoorcontrol = 1; pixel_x = 28; pixel_y = 21},/obj/machinery/door_control{desc = "A remote control-switch for shutters."; id = "virologyquar"; name = "Virology Emergency Lockdown Control"; pixel_x = -15; pixel_y = 38; req_access_txt = "5"},/obj/machinery/door_control{desc = "A remote control-switch for shutters."; id = "medbayquar"; name = "Medbay Emergency Lockdown Control"; pixel_x = -15; pixel_y = 30; req_access_txt = "5"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor{icon_state = "white"},/area/crew_quarters/heads/cmo) "bRc" = (/obj/structure/table,/obj/item/weapon/folder/white{pixel_y = 10},/obj/item/weapon/clipboard,/obj/item/weapon/paper_bin,/obj/item/weapon/pen,/obj/item/weapon/stamp/cmo,/turf/simulated/floor{tag = "icon-whiteblue (EAST)"; icon_state = "whiteblue"; dir = 4},/area/crew_quarters/heads/cmo) "bRd" = (/obj/machinery/door/firedoor/border_only{dir = 4; name = "Firelock East"},/obj/machinery/door/blast/regular{density = 0; icon_state = "pdoor0"; id = "Biohazard"; name = "Biohazard Shutter"; opacity = 0},/obj/machinery/door/airlock/research{name = "Toxins Launch Room"; req_access_txt = "8"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor,/area/rnd/mixing)