diff --git a/aurorastation.dme b/aurorastation.dme
index 5dff3a36396..0029cebbb87 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -2253,6 +2253,7 @@
#include "code\modules\mob\living\simple_animal\friendly\carp.dm"
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
+#include "code\modules\mob\living\simple_animal\friendly\cosmozoan.dm"
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm"
#include "code\modules\mob\living\simple_animal\friendly\fox.dm"
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 1d4ab724353..12d10b95620 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -96,3 +96,27 @@
var/mob/living/simple_animal/hostile/carp/fish = carp_weakref.resolve()
if (fish && prob(50) && is_type_in_typecache(fish.loc, despawn_turfs))
qdel(fish)
+
+/datum/event/carp_migration/cozmo/start()
+ spawn_fish(7, 3, 5)
+
+/datum/event/carp_migration/cozmo/announce()
+ command_announcement.Announce("A migration of non-hostile entities has been detected outside the facility.", "Lifesign Alert", 'sound/AI/cozmo_migration.ogg')
+
+/datum/event/carp_migration/cozmo/spawn_fish(var/num_groups, var/group_size_min = 3, var/group_size_max = 5, var/spawn_drones = FALSE)
+ set waitfor = FALSE
+ var/list/spawn_locations = list()
+
+ for(var/obj/effect/landmark/C in landmarks_list)
+ if(C.name == "carpspawn")
+ spawn_locations.Add(C.loc)
+ spawn_locations = shuffle(spawn_locations)
+ num_groups = min(num_groups, spawn_locations.len)
+
+ var/i = 1
+ while (i <= num_groups)
+ var/group_size = rand(group_size_min, group_size_max)
+ for(var/j = 1, j <= group_size, j++)
+ var/mob/living/simple_animal/cosmozoan/cozmo = new(spawn_locations[i])
+ spawned_carp += WEAKREF(cozmo)
+ i++
\ No newline at end of file
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index c1c49ecbe01..963391c81b2 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -134,6 +134,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Camera Damage", /datum/event/camera_damage, 20, list(ASSIGNMENT_ENGINEER = 10)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Economic News", /datum/event/economic_event, 300),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Lost Carp", /datum/event/carp_migration, 20, list(ASSIGNMENT_SECURITY = 10), 1),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Cozmozoan Migration", /datum/event/carp_migration/cozmo, 60),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 10),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 1, 5, 15),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300),
diff --git a/code/modules/mob/living/simple_animal/friendly/cosmozoan.dm b/code/modules/mob/living/simple_animal/friendly/cosmozoan.dm
new file mode 100644
index 00000000000..e4a9c803348
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/cosmozoan.dm
@@ -0,0 +1,51 @@
+/mob/living/simple_animal/cosmozoan
+ name = "cosmozoan"
+ desc = "These jellyfish-like entities drift through asteroid fields, emitting a soft glow."
+ desc_info = "Schools of Cosmozoans often congregate in asteroid fields, though they have rarely been witnessed in greater number and size in the Frontier. Their origin remains a mystery but it is believed they predate early man. This belief landed them the name Cosmozoa."
+ icon_state = "cosmozoan"
+ icon_living = "cosmozoan"
+ icon_dead = "cosmozoan_dead"
+ maxHealth = 15
+ health = 15
+ meat_type = /obj/item/reagent_containers/food/snacks/fish/cosmozoan
+ meat_amount = 2
+ organ_names = list("hood", "tentacles")
+ response_help = "pets"
+ response_disarm = "gently pushes aside"
+ response_harm = "whacks"
+ harm_intent_damage = 5
+ blood_type = COLOR_CRYSTAL
+ blood_overlay_icon = null
+ canbrush = TRUE
+ brush = /obj/item/reagent_containers/glass/rag
+ wanders_diagonally = TRUE
+
+ min_oxy = 0
+ max_oxy = 0
+ min_tox = 0
+ max_tox = 0
+ min_co2 = 0
+ max_co2 = 0
+ min_n2 = 0
+ max_n2 = 0
+ minbodytemp = 0
+
+ flying = TRUE
+ see_in_dark = 8
+ see_invisible = SEE_INVISIBLE_NOLIGHTING
+
+/mob/living/simple_animal/cosmozoan/Initialize()
+ . = ..()
+ set_light(1.4, 3, COLOR_CRYSTAL)
+
+// run away when attacked
+/mob/living/simple_animal/cosmozoan/handle_attack_by(var/mob/M)
+ var/list/valid_dirs = alldirs.Copy()
+ valid_dirs -= get_dir(src, M)
+ var/turf/target_turf = get_ranged_target_turf(src, pick(valid_dirs), 5)
+ if(target_turf)
+ for(var/i = 1 to 5)
+ if(loc == target_turf)
+ break
+ step_to(src, target_turf)
+ sleep(6)
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
index eb27b172e2c..8dad3933152 100644
--- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
+++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
@@ -48,7 +48,9 @@
..()
//chance to go crazy and start wacking stuff
if(!enemies.len && prob(1))
- Retaliate()
+ var/mob/living/L = locate() in oview(world.view, src)
+ if(L)
+ handle_attack_by(L)
if(enemies.len && prob(10))
enemies = list()
@@ -61,10 +63,10 @@
var/step = get_step_to(src, food, 0)
Move(step)
-/mob/living/simple_animal/hostile/retaliate/goat/Retaliate()
+/mob/living/simple_animal/hostile/retaliate/goat/handle_attack_by(mob/M)
..()
if(stat == CONSCIOUS)
- visible_message("[src] gets an evil-looking gleam in their eye.")
+ visible_message(SPAN_WARNING("[src] gets an evil-looking gleam in their eye."))
/mob/living/simple_animal/hostile/retaliate/goat/Move()
..()
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
index 88d600baaa0..93e1eaaf183 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm
@@ -21,30 +21,10 @@
see &= enemies // Remove all entries that aren't in enemies
return see
-/mob/living/simple_animal/hostile/retaliate/proc/Retaliate(var/mob/M)
+/mob/living/simple_animal/hostile/retaliate/handle_attack_by(mob/M)
enemies |= M
targets |= M
for(var/mob/living/simple_animal/hostile/retaliate/H in view(world.view, get_turf(src)))
if(H.faction == faction)
- H.enemies |= M
-
-/mob/living/simple_animal/hostile/retaliate/attack_hand(mob/living/carbon/human/M)
- . = ..()
- if(M.a_intent in list(I_DISARM, I_GRAB, I_HURT))
- Retaliate(M)
-
-/mob/living/simple_animal/hostile/retaliate/attacked_with_item(obj/item/O, mob/user, var/proximity)
- . = ..()
- if(.)
- Retaliate(user)
-
-/mob/living/simple_animal/hostile/retaliate/hitby(atom/movable/AM, speed)
- . = ..()
- if(ismob(AM.thrower))
- Retaliate(AM.thrower)
-
-/mob/living/simple_animal/hostile/retaliate/bullet_act(obj/item/projectile/P, def_zone)
- . = ..()
- if(ismob(P.firer))
- Retaliate(P.firer)
\ No newline at end of file
+ H.enemies |= M
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index b3046ad5b2e..45aa4a20e5c 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -46,6 +46,7 @@
var/stop_thinking = FALSE // prevents them from doing any AI stuff whatsoever
var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals.
var/wander = 1 // Does the mob wander around when idle?
+ var/wanders_diagonally = FALSE // does the mob move diagonally when wandering?
var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it.
var/atom/movement_target = null//Thing we're moving towards
var/turns_since_scan = 0
@@ -285,7 +286,7 @@
if(isturf(loc) && !resting && !buckled_to && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc.
if(turns_since_move >= turns_per_move && !(stop_automated_movement_when_pulled && pulledby)) //Some animals don't move when pulled
var/moving_to = 0 // otherwise it always picks 4, fuck if I know. Did I mention fuck BYOND
- moving_to = pick(cardinal)
+ moving_to = wanders_diagonally ? pick(alldirs) : pick(cardinal)
set_dir(moving_to) //How about we turn them the direction they are moving, yay.
Move(get_step(src,moving_to))
turns_since_move = 0
@@ -454,6 +455,9 @@
var/can_ghosts_hear = client ? GHOSTS_ALL_HEAR : ONLY_GHOSTS_IN_VIEW
custom_emote(AUDIBLE_MESSAGE, act_desc, can_ghosts_hear)
+/mob/living/simple_animal/proc/handle_attack_by(var/mob/M)
+ return
+
/mob/living/simple_animal/attack_hand(mob/living/carbon/human/M as mob)
..()
switch(M.a_intent)
@@ -467,6 +471,7 @@
M.visible_message("\The [M] [response_disarm] \the [src]")
M.do_attack_animation(src)
poke(1)
+ handle_attack_by(M)
//TODO: Push the mob away or something
if(I_GRAB)
@@ -489,9 +494,11 @@
M.visible_message(SPAN_WARNING("\The [M] has grabbed \the [src] passively!"))
M.do_attack_animation(src)
poke(1)
+ handle_attack_by(M)
if(I_HURT)
unarmed_harm_attack(M)
+ handle_attack_by(M)
return
@@ -580,8 +587,19 @@
if(O.hitsound)
playsound(loc, O.hitsound, O.get_clamped_volume(), 1, -1)
user.do_attack_animation(src, O)
+ handle_attack_by(user)
return TRUE
+/mob/living/simple_animal/hitby(atom/movable/AM, speed)
+ . = ..()
+ if(ismob(AM.thrower))
+ handle_attack_by(AM.thrower)
+
+/mob/living/simple_animal/bullet_act(obj/item/projectile/P, def_zone)
+ . = ..()
+ if(ismob(P.firer))
+ handle_attack_by(P.firer)
+
/mob/living/simple_animal/apply_damage(damage, damagetype, def_zone, blocked, used_weapon, damage_flags, armor_pen, silent = FALSE)
. = ..()
handle_bleeding_timer(damage)
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
index f8a9861724d..0265ff3419c 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
@@ -238,6 +238,14 @@
taste_description = "cold, bitter slime"
hydration_factor = 6
+/decl/reagent/nutriment/protein/seafood/cosmozoan
+ name = "Cosmozoan Protein"
+ taste_description = "cold, bitter slime"
+ hydration_factor = 8
+
+/decl/reagent/nutriment/protein/seafood/cosmozoan/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder)
+ M.add_chemical_effect(CE_PAINKILLER, 10)
+
/decl/reagent/nutriment/protein/egg // Also bad for skrell.
name = "Egg Yolk"
color = "#FFFFAA"
diff --git a/code/modules/reagents/reagent_containers/food/snacks/fish.dm b/code/modules/reagents/reagent_containers/food/snacks/fish.dm
index 514b86727f6..202b265902f 100644
--- a/code/modules/reagents/reagent_containers/food/snacks/fish.dm
+++ b/code/modules/reagents/reagent_containers/food/snacks/fish.dm
@@ -35,6 +35,13 @@
/obj/item/reagent_containers/food/snacks/fish/mollusc/barnacle
fish_type = "barnacle"
+/obj/item/reagent_containers/food/snacks/fish/cosmozoan
+ name = "slimy fillet"
+ desc = "A piece of slimy meat that could only come from a space jellyfish, a cosmozoan."
+ icon_state = "cozmofillet"
+ fish_type = "cosmozoan"
+ reagents_to_add = list(/decl/reagent/nutriment/protein/seafood/cosmozoan = 3)
+
// Molluscs!
/obj/item/trash/mollusc_shell
name = "mollusc shell"
diff --git a/html/changelogs/geeves-cozmozoans.yml b/html/changelogs/geeves-cozmozoans.yml
new file mode 100644
index 00000000000..99347676864
--- /dev/null
+++ b/html/changelogs/geeves-cozmozoans.yml
@@ -0,0 +1,7 @@
+author: Geeves, Scrambles
+
+delete-after: True
+
+changes:
+ - rscadd: "Added a new kind of non-hostile biological migration event."
+ - bugfix: "Goats will now properly beat someone up when they enter a fey mood."
\ No newline at end of file
diff --git a/icons/mob/npc/animal.dmi b/icons/mob/npc/animal.dmi
index 71333a6f194..4b7f3e04f03 100644
Binary files a/icons/mob/npc/animal.dmi and b/icons/mob/npc/animal.dmi differ
diff --git a/icons/obj/food.dmi b/icons/obj/food.dmi
index 1e91e9c462b..f4a71acee92 100644
Binary files a/icons/obj/food.dmi and b/icons/obj/food.dmi differ
diff --git a/sound/AI/cozmo_migration.ogg b/sound/AI/cozmo_migration.ogg
new file mode 100644
index 00000000000..6b501bc9f88
Binary files /dev/null and b/sound/AI/cozmo_migration.ogg differ
diff --git a/tools/Event Probabilities/Event Probabilities.xlsx b/tools/Event Probabilities/Event Probabilities.xlsx
index 07439c398d4..4e22d16b88d 100644
Binary files a/tools/Event Probabilities/Event Probabilities.xlsx and b/tools/Event Probabilities/Event Probabilities.xlsx differ