Swarm Component + Pixel shift attack animation (#17179)

* Swarm!!

* carp dmi

* swarm

* Proper swarming

* Swarmers

* Update

---------

Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com>
This commit is contained in:
Guti
2025-03-09 15:09:32 -04:00
committed by GitHub
co-authored by Selis
parent 33ca734e37
commit a5a3bd953c
13 changed files with 251 additions and 31 deletions
@@ -0,0 +1,66 @@
/// This component behaves similar to connect_loc, hooking into a signal on a tracked object's turf
/// It has the ability to react to that signal on behalf of a separate listener however
/// This has great use, primarily for components, but it carries with it some overhead
/// So we do it separately as it needs to hold state which is very likely to lead to bugs if it remains as an element.
/datum/component/connect_loc_behalf
dupe_mode = COMPONENT_DUPE_UNIQUE
/// An assoc list of signal -> procpath to register to the loc this object is on.
var/list/connections
var/atom/movable/tracked
var/atom/tracked_loc
/datum/component/connect_loc_behalf/Initialize(atom/movable/tracked, list/connections)
. = ..()
if(!istype(tracked))
return COMPONENT_INCOMPATIBLE
src.connections = connections
src.tracked = tracked
/datum/component/connect_loc_behalf/RegisterWithParent()
RegisterSignal(tracked, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(tracked, COMSIG_PARENT_QDELETING, PROC_REF(handle_tracked_qdel))
update_signals()
/datum/component/connect_loc_behalf/UnregisterFromParent()
unregister_signals()
UnregisterSignal(tracked, list(
COMSIG_MOVABLE_MOVED,
COMSIG_PARENT_QDELETING,
))
tracked = null
/datum/component/connect_loc_behalf/proc/handle_tracked_qdel()
SIGNAL_HANDLER // COMSIG_PARENT_QDELETING
qdel(src)
/datum/component/connect_loc_behalf/proc/update_signals()
unregister_signals()
//You may ask yourself, isn't this just silencing an error?
//The answer is yes, but there's no good cheap way to fix it
//What happens is the tracked object or hell the listener gets say, deleted, which makes targets[old_loc] return a null
//The null results in a bad index, because of course it does
//It's not a solvable problem though, since both actions, the destroy and the move, are sourced from the same signal send
//And sending a signal should be agnostic of the order of listeners
//So we need to either pick the order agnositic, or destroy safe
//And I picked destroy safe. Let's hope this is the right path!
if(isnull(tracked.loc))
return
tracked_loc = tracked.loc
for(var/signal in connections)
parent.RegisterSignal(tracked_loc, signal, connections[signal])
/datum/component/connect_loc_behalf/proc/unregister_signals()
if(isnull(tracked_loc))
return
parent.UnregisterSignal(tracked_loc, connections)
tracked_loc = null
/datum/component/connect_loc_behalf/proc/on_moved(sigtype, atom/movable/tracked, atom/old_loc)
SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED
update_signals()
+59
View File
@@ -0,0 +1,59 @@
/datum/component/swarming
var/offset_x = 0
var/offset_y = 0
var/is_swarming = FALSE
var/list/swarm_members = list()
var/static/list/swarming_loc_connections = list(
COMSIG_ATOM_EXITED = PROC_REF(leave_swarm),
COMSIG_ATOM_ENTERED = PROC_REF(join_swarm)
)
/datum/component/swarming/Initialize(max_x = 24, max_y = 24)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
offset_x = rand(-max_x, max_x)
offset_y = rand(-max_y, max_y)
AddComponent(/datum/component/connect_loc_behalf, parent, swarming_loc_connections)
/datum/component/swarming/Destroy()
for(var/other in swarm_members)
var/datum/component/swarming/other_swarm = other
other_swarm.swarm_members -= src
if(!length(other_swarm.swarm_members))
other_swarm.unswarm()
swarm_members = null
return ..()
/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
var/datum/component/swarming/other_swarm = arrived.GetComponent(/datum/component/swarming)
if(!other_swarm)
return
swarm()
swarm_members |= other_swarm
other_swarm.swarm()
other_swarm.swarm_members |= src
/datum/component/swarming/proc/leave_swarm(datum/source, atom/movable/gone, direction)
var/datum/component/swarming/other_swarm = gone.GetComponent(/datum/component/swarming)
if(!other_swarm || !(other_swarm in swarm_members))
return
swarm_members -= other_swarm
if(!length(swarm_members))
unswarm()
other_swarm.swarm_members -= src
if(!length(other_swarm.swarm_members))
other_swarm.unswarm()
/datum/component/swarming/proc/swarm()
var/atom/movable/owner = parent
if(!is_swarming)
is_swarming = TRUE
animate(owner, pixel_x = owner.pixel_x + offset_x, pixel_y = owner.pixel_y + offset_y, time = 2)
/datum/component/swarming/proc/unswarm()
var/atom/movable/owner = parent
if(is_swarming)
animate(owner, pixel_x = owner.pixel_x - offset_x, pixel_y = owner.pixel_y - offset_y, time = 2)
is_swarming = FALSE
+4 -12
View File
@@ -195,12 +195,8 @@ note dizziness decrements automatically in the mob's Life() proc.
else if(direction & WEST)
pixel_x_diff = 8
var/default_pixel_x = initial(pixel_x)
var/default_pixel_y = initial(pixel_y)
var/mob/mob = src
if(istype(mob))
default_pixel_x = mob.default_pixel_x
default_pixel_y = mob.default_pixel_y
var/default_pixel_x = pixel_x
var/default_pixel_y = pixel_y
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = windup_time - 2)
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
@@ -220,12 +216,8 @@ note dizziness decrements automatically in the mob's Life() proc.
else if(direction & WEST)
pixel_x_diff = -8
var/default_pixel_x = initial(pixel_x)
var/default_pixel_y = initial(pixel_y)
var/mob/mob = src
if(istype(mob))
default_pixel_x = mob.default_pixel_x
default_pixel_y = mob.default_pixel_y
var/default_pixel_x = pixel_x
var/default_pixel_y = pixel_y
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
@@ -25,6 +25,7 @@ GLOBAL_VAR_INIT(jellyfish_count, 0)
icon_dead = "space_jellyfish_dead"
has_eye_glow = TRUE
hovering = TRUE
density = FALSE
faction = FACTION_JELLYFISH
@@ -80,6 +81,16 @@ GLOBAL_VAR_INIT(jellyfish_count, 0)
var/reproduction_cooldown = 0
/mob/living/simple_mob/vore/alienanimals/space_jellyfish/Initialize()
. = ..()
AddComponent(/datum/component/swarming)
// Allows this mob to swarm
/mob/living/simple_mob/vore/alienanimals/space_jellyfish/CanPass(atom/movable/mover, turf/target)
if(isliving(mover) && !istype(mover, /mob/living/simple_mob/vore/alienanimals/space_jellyfish) && mover.density == TRUE)
return FALSE
return ..()
/datum/say_list/jellyfish
emote_see = list("flickers", "flashes", "looms","pulses","sways","shimmers hypnotically")
@@ -68,6 +68,7 @@
icon_living = "guard"
icon_dead = "guard_dead"
has_eye_glow = TRUE
density = FALSE
faction = FACTION_SPIDERS
maxHealth = 200
@@ -119,6 +120,15 @@
allow_mind_transfer = TRUE
/mob/living/simple_mob/animal/giant_spider/Initialize()
. = ..()
AddComponent(/datum/component/swarming)
/mob/living/simple_mob/animal/giant_spider/CanPass(atom/movable/mover, turf/target)
if(isliving(mover) && !istype(mover, /mob/living/simple_mob/animal/giant_spider) && mover.density == TRUE)
return FALSE
return ..()
/mob/living/simple_mob/animal/giant_spider/apply_melee_effects(var/atom/A)
if(isliving(A))
var/mob/living/L = A
@@ -29,6 +29,7 @@
name = "space carp"
desc = "A ferocious, fang-bearing creature that resembles a fish."
catalogue_data = list(/datum/category_item/catalogue/fauna/carp)
icon = 'icons/mob/carp.dmi'
icon_state = "carp"
icon_living = "carp"
icon_dead = "carp_dead"
@@ -39,10 +40,13 @@
health = 25
movement_cooldown = -2
hovering = TRUE
density = FALSE
response_help = "pets the"
response_disarm = "gently pushes aside the"
response_harm = "hits the"
vore_active = TRUE
vore_icons = SA_ICON_LIVING
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "hits"
melee_damage_lower = 7 // About 14 DPS.
melee_damage_upper = 7
@@ -60,6 +64,85 @@
var/knockdown_chance = 15
var/random_color = FALSE
var/rarechance = 1
var/static/list/carp_colors = list(\
"lightpurple" = "#c3b9f1", \
"lightpink" = "#da77a8", \
"green" = "#70ff25", \
"grape" = "#df0afb", \
"swamp" = "#e5e75a", \
"turquoise" = "#04e1ed", \
"brown" = "#ca805a", \
"teal" = "#20e28e", \
"lightblue" = "#4d88cc", \
"rusty" = "#dd5f34", \
"beige" = "#bbaeaf", \
"yellow" = "#f3ca4a", \
"blue" = "#09bae1", \
"palegreen" = "#7ef099", \
)
var/static/list/carp_colors_rare = list(\
"silver" = "#fdfbf3", \
)
/mob/living/simple_mob/animal/space/carp/Initialize()
. = ..()
carp_randomify(rarechance)
update_icons()
AddComponent(/datum/component/swarming)
// This is so carps can swarm
/mob/living/simple_mob/animal/space/carp/CanPass(atom/movable/mover, turf/target)
if(isliving(mover) && !istype(mover, /mob/living/simple_mob/animal/space/carp) && mover.density == TRUE)
return FALSE
return ..()
/mob/living/simple_mob/animal/space/carp/proc/carp_randomify(rarechance)
if(random_color)
var/our_color
if(prob(rarechance))
our_color = pick(carp_colors_rare)
add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY)
else
our_color = pick(carp_colors)
add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY)
regenerate_icons()
/mob/living/simple_mob/animal/space/carp/proc/add_carp_overlay()
if(!random_color)
return
var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth")
base_overlay.appearance_flags = RESET_COLOR
add_overlay(base_overlay)
/mob/living/simple_mob/animal/space/carp/proc/add_dead_carp_overlay()
if(!random_color)
return
var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth")
base_dead_overlay.appearance_flags = RESET_COLOR
add_overlay(base_dead_overlay)
/mob/living/simple_mob/animal/space/carp/death(gibbed)
. = ..()
if(!random_color || gibbed)
return
regenerate_icons()
/mob/living/simple_mob/animal/space/carp/revive()
..()
regenerate_icons()
/mob/living/simple_mob/animal/space/carp/regenerate_icons()
..()
if(!random_color)
return
if(stat != DEAD)
add_carp_overlay()
else
add_dead_carp_overlay()
/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A)
if(isliving(A))
var/mob/living/L = A
@@ -81,6 +164,7 @@
icon_state = "shark"
icon_living = "shark"
icon_dead = "shark_dead"
vore_icons = FALSE
maxHealth = 50
health = 50
@@ -102,6 +186,7 @@
icon_dead = "megacarp_dead"
icon_living = "megacarp"
icon_state = "megacarp"
vore_icons = FALSE
maxHealth = 230
health = 230
@@ -174,6 +259,7 @@
icon_gib = null
meat_amount = 0
meat_type = null
vore_icons = FALSE
mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp.
@@ -32,7 +32,8 @@
health = 15
movement_cooldown = -2
pass_flags = PASSTABLE
pass_flags = PASSTABLE | PASSMOB
a_intent = I_HURT
mob_swap_flags = 0
mob_push_flags = 0
@@ -49,6 +50,10 @@
ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive
/mob/living/simple_mob/mechanical/viscerator/Initialize()
. = ..()
AddComponent(/datum/component/swarming)
/mob/living/simple_mob/mechanical/viscerator/death()
..(null,"is smashed into pieces!")
qdel(src)
@@ -46,6 +46,10 @@
allow_mind_transfer = TRUE
/mob/living/simple_mob/vore/bee/Initialize()
. = ..()
AddComponent(/datum/component/swarming)
/mob/living/simple_mob/vore/bee/Process_Spacemove(var/check_drift = 0)
return 1 //No drifting in space for space bee!
@@ -119,21 +119,6 @@
melee_damage_upper = 15
attack_armor_pen = 0
/mob/living/simple_mob/animal/space/carp
icon = 'icons/mob/vore.dmi'
vore_active = 1
vore_icons = SA_ICON_LIVING
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "hits"
/mob/living/simple_mob/animal/space/carp/large
vore_icons = 0
/mob/living/simple_mob/animal/space/carp/large/huge
vore_icons = 0
/mob/living/simple_mob/animal/space/carp/holodeck
vore_icons = 0
/* //VOREStation AI Temporary removal
/mob/living/simple_mob/hostile/creature/vore
vore_active = 1
Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

After

Width:  |  Height:  |  Size: 383 KiB

+2
View File
@@ -471,6 +471,7 @@
#include "code\datums\autolathe\tools_vr.dm"
#include "code\datums\changelog\changelog.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\connect_loc_behalf.dm"
#include "code\datums\components\connect_mob_behalf.dm"
#include "code\datums\components\gargoyle.dm"
#include "code\datums\components\material_container.dm"
@@ -478,6 +479,7 @@
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\recursive_move.dm"
#include "code\datums\components\resize_guard.dm"
#include "code\datums\components\swarm.dm"
#include "code\datums\components\crafting\crafting.dm"
#include "code\datums\components\crafting\crafting_external.dm"
#include "code\datums\components\crafting\recipes.dm"