diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm
index ee8962ace73d..fd0e890f0063 100644
--- a/code/__DEFINES/xeno.dm
+++ b/code/__DEFINES/xeno.dm
@@ -682,8 +682,9 @@
#define XENO_CASTE_HIVELORD "Hivelord"
#define XENO_CASTE_LURKER "Lurker"
#define XENO_CASTE_WARRIOR "Warrior"
+#define XENO_CASTE_BOXER "Yuji Itadori"
#define XENO_CASTE_SPITTER "Spitter"
-#define XENO_T2_CASTES list(XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER)
+#define XENO_T2_CASTES list(XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_BOXER, XENO_CASTE_SPITTER)
//t3
#define XENO_CASTE_BOILER "Boiler"
#define XENO_CASTE_PRAETORIAN "Praetorian"
@@ -704,7 +705,7 @@
//caste list
#define XENO_CONSTRUCT_NODE_BOOST list(XENO_CASTE_HIVELORD, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_QUEEN)
-#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_DESPOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND, XENO_CASTE_KING)
+#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_BOXER, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_DESPOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND, XENO_CASTE_KING)
// Checks if two hives are allied to each other.
// PARAMETERS:
diff --git a/code/datums/langchat/langchat.dm b/code/datums/langchat/langchat.dm
index fe87903a8d69..89d1c67b0ac1 100644
--- a/code/datums/langchat/langchat.dm
+++ b/code/datums/langchat/langchat.dm
@@ -10,6 +10,7 @@
/mob/living/carbon/xenomorph/hivelord/langchat_height = 64
/mob/living/carbon/xenomorph/defender/langchat_height = 48
/mob/living/carbon/xenomorph/warrior/langchat_height = 48
+/mob/living/carbon/xenomorph/boxer/langchat_height = 48
/mob/living/carbon/xenomorph/king/langchat_height = 64
/mob/living/carbon/xenomorph/despoiler/langchat_height = 64
diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm
index 208decb04520..7ac333b2d264 100644
--- a/code/game/jobs/role_authority.dm
+++ b/code/game/jobs/role_authority.dm
@@ -636,6 +636,8 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
M = /mob/living/carbon/xenomorph/lurker
if(XENO_CASTE_WARRIOR)
M = /mob/living/carbon/xenomorph/warrior
+ if(XENO_CASTE_BOXER)
+ M = /mob/living/carbon/xenomorph/boxer
if(XENO_CASTE_DEFENDER)
M = /mob/living/carbon/xenomorph/defender
if(XENO_CASTE_QUEEN)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 5d6858f8273b..625ed22b7d19 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -386,6 +386,8 @@
sound = pick('sound/voice/alien_roar_larva1.ogg','sound/voice/alien_roar_larva2.ogg')
if("queen")
sound = pick('sound/voice/alien_queen_command.ogg','sound/voice/alien_queen_command2.ogg','sound/voice/alien_queen_command3.ogg')
+ if("yuji_talk")
+ sound = pick ('sound/voice/yuji_talk.ogg', 'sound/voice/yuji_talk2.ogg', 'sound/voice/yuji_talk3.ogg',)
// Human
if("male_scream")
sound = pick('sound/voice/human_male_scream_1.ogg','sound/voice/human_male_scream_2.ogg','sound/voice/human_male_scream_3.ogg','sound/voice/human_male_scream_4.ogg',5;'sound/voice/human_male_scream_5.ogg',5;'sound/voice/human_jackson_scream.ogg',5;'sound/voice/human_ack_scream.ogg','sound/voice/human_male_scream_6.ogg')
diff --git a/code/modules/admin/player_panel/actions/transform.dm b/code/modules/admin/player_panel/actions/transform.dm
index bcd77ff1a37b..d6f091ccfc68 100644
--- a/code/modules/admin/player_panel/actions/transform.dm
+++ b/code/modules/admin/player_panel/actions/transform.dm
@@ -81,6 +81,11 @@ GLOBAL_LIST_INIT(pp_transformables, list(
color = "purple"
),
list(
+ name = XENO_CASTE_BOXER,
+ key = /mob/living/carbon/xenomorph/boxer,
+ color = "purple"
+ ),
+ list(
name = XENO_CASTE_SPITTER,
key = /mob/living/carbon/xenomorph/spitter,
color = "purple"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Boxer.dm b/code/modules/mob/living/carbon/xenomorph/castes/Boxer.dm
new file mode 100644
index 000000000000..b1473567e743
--- /dev/null
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Boxer.dm
@@ -0,0 +1,547 @@
+/datum/caste_datum/boxer
+ caste_type = XENO_CASTE_BOXER
+ tier = 2
+
+ melee_damage_lower = XENO_DAMAGE_TIER_3
+ melee_damage_upper = XENO_DAMAGE_TIER_5
+ melee_vehicle_damage = XENO_DAMAGE_TIER_5
+ max_health = XENO_HEALTH_TIER_8
+ plasma_gain = XENO_PLASMA_GAIN_TIER_9
+ plasma_max = XENO_NO_PLASMA
+ xeno_explosion_resistance = XENO_EXPLOSIVE_ARMOR_TIER_4
+ armor_deflection = XENO_ARMOR_TIER_3
+ evasion = XENO_EVASION_NONE
+ speed = XENO_SPEED_TIER_7
+
+ behavior_delegate_type = /datum/behavior_delegate/boxer_base
+
+ evolves_to = list(XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER)
+ deevolves_to = list(XENO_CASTE_DEFENDER)
+ caste_desc = "A powerful front line combatant."
+ can_vent_crawl = 0
+
+ tackle_min = 2
+ tackle_max = 4
+
+ agility_speed_increase = -0.9
+
+ heal_resting = 1.4
+
+ minimum_evolve_time = 9 MINUTES
+
+ minimap_icon = "boxer"
+
+
+/mob/living/carbon/xenomorph/boxer
+ caste_type = XENO_CASTE_BOXER
+ name = XENO_CASTE_BOXER
+ desc = "Thats just a guy with pink hair."
+ icon = 'icons/mob/xenos/castes/tier_2/boxer.dmi'
+ icon_size = 64
+ icon_state = "Yuji Itadori Walking"
+ plasma_types = list(PLASMA_CHITIN)
+ pixel_x = -16
+ old_x = -16
+ tier = 2
+ pull_speed = 2 // about what it was before, slightly faster
+ organ_value = 2000
+ base_actions = list(
+ /datum/action/xeno_action/onclick/xeno_resting,
+ /datum/action/xeno_action/onclick/release_haul,
+ /datum/action/xeno_action/watch_xeno,
+ /datum/action/xeno_action/activable/boxer_punch,
+ /datum/action/xeno_action/activable/jab,
+ /datum/action/xeno_action/activable/uppercut,
+ )
+
+ claw_type = CLAW_TYPE_SHARP
+
+ icon_xeno = 'icons/mob/xenos/castes/tier_2/boxer.dmi'
+ icon_xenonid = 'icons/mob/xenonids/castes/tier_2/warrior.dmi'
+
+ weed_food_icon = 'icons/mob/xenos/weeds_64x64.dmi'
+ weed_food_states = list("Warrior_1","Warrior_2","Warrior_3")
+ weed_food_states_flipped = list("Warrior_1","Warrior_2","Warrior_3")
+
+ skull = /obj/item/skull/warrior
+ pelt = /obj/item/pelt/warrior
+
+ slash_sound = 'sound/weapons/punch1.ogg'
+ slash_verb = "punch"
+ slashes_verb = "punches"
+ speaking_noise = "yuji_talk"
+
+/datum/behavior_delegate/boxer_base
+
+ name = "Base Boxer Behavior Delegate"
+
+ var/ko_delay = 5 SECONDS
+ var/max_clear_head = 3
+ var/clear_head_delay = 15 SECONDS
+ var/clear_head = 3
+ var/next_clear_head_regen
+ var/clear_head_tickcancel
+
+ var/mob/punching_bag
+ var/ko_counter = 0
+ var/ko_reset_timer
+ var/max_ko_counter = 15
+
+ var/image/ko_icon
+ var/image/big_ko_icon
+
+/datum/behavior_delegate/boxer_base/New()
+ . = ..()
+ if(SSticker.mode && (SSticker.mode.flags_round_type & MODE_XVX))
+ clear_head = 0
+ max_clear_head = 0
+
+
+/datum/behavior_delegate/boxer_base/append_to_stat()
+ . = list()
+ if(punching_bag)
+ . += "Beating [punching_bag] - [ko_counter] hits"
+
+ . += "Clarity [clear_head] hits"
+
+/datum/behavior_delegate/boxer_base/on_life()
+ var/world_time = world.time
+ if(world_time > next_clear_head_regen && clear_head < max_clear_head)
+ clear_head++
+ next_clear_head_regen = world_time+ clear_head_delay
+
+/datum/behavior_delegate/boxer_base/melee_attack_additional_effects_target(mob/living/carbon/carbon_target, ko_boost = 0.5)
+
+ if(!ismob(carbon_target))
+ return
+
+ if(punching_bag != carbon_target)
+ remove_ko()
+ punching_bag = carbon_target
+ ko_icon = image(null, carbon_target)
+ ko_icon.alpha = 196
+ ko_icon.color = "#ee0808"
+ ko_icon.maptext_width = 16
+ ko_icon.maptext_x = -16
+ ko_icon.maptext_y = 16
+ ko_icon.layer = 20
+ bound_xeno.client.images += ko_icon
+
+ ko_counter += ko_boost
+ if(ko_counter > max_ko_counter)
+ ko_counter = max_ko_counter
+ var/to_display = round(ko_counter)
+ ko_icon.maptext = "[to_display]"
+
+ ko_reset_timer = addtimer(CALLBACK(src, PROC_REF(remove_ko)), ko_delay, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT | TIMER_STOPPABLE)
+
+
+/datum/behavior_delegate/boxer_base/proc/remove_ko()
+ punching_bag = null
+ ko_counter = 0
+ if(bound_xeno.client && ko_icon)
+ bound_xeno.client.images -= ko_icon
+ ko_icon = null
+
+/datum/behavior_delegate/boxer_base/proc/display_ko_message(mob/carbon_target)
+
+ big_ko_icon = image(null, carbon_target)
+ big_ko_icon.alpha = 196
+ big_ko_icon.maptext_y = carbon_target.langchat_height
+ big_ko_icon.maptext_width = LANGCHAT_WIDTH
+ big_ko_icon.maptext_height = 16
+ big_ko_icon.color = "#FF0000"
+ big_ko_icon.maptext_x = -32
+ big_ko_icon.maptext = "KOKUSEN!"
+ bound_xeno.client.images += big_ko_icon
+ addtimer(CALLBACK(src, PROC_REF(remove_big_ko)), 2 SECONDS)
+
+/datum/behavior_delegate/boxer_base/proc/remove_big_ko()
+ if(bound_xeno.client && big_ko_icon)
+ bound_xeno.client.images -= big_ko_icon
+ big_ko_icon = null
+
+
+ ///BOXER CLARITY STUFF GOES HERE
+
+/mob/living/carbon/xenomorph/boxer/Daze(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/SetDaze(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/AdjustDaze(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/KnockDown(amount, forced)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/SetKnockDown(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+
+/mob/living/carbon/xenomorph/boxer/AdjustKnockDown(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/Stun(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+/mob/living/carbon/xenomorph/boxer/SetStun(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+
+/mob/living/carbon/xenomorph/boxer/AdjustStun(amount)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = behavior_delegate
+ if(istype(behavior_delegate, boxer_delegate))
+ if(boxer_delegate.clear_head <= 0)
+ ..(amount)
+ return
+ if(boxer_delegate.clear_head_tickcancel == world.time)
+ return
+
+ boxer_delegate.clear_head_tickcancel = world.time
+ boxer_delegate.clear_head--
+ if(boxer_delegate.clear_head <= 0)
+ boxer_delegate.clear_head = 0
+
+
+///boxer clarity end
+
+
+/datum/action/xeno_action/activable/boxer_punch
+
+ name = "Punch"
+ action_icon_state = "punch"
+ action_type = XENO_ACTION_CLICK
+ ability_primacy = XENO_PRIMARY_ACTION_1
+ xeno_cooldown = 4.5 SECONDS
+
+ var/boxer_punch_damage = 20
+ var/boxer_punch_damage_synth = 30
+ var/boxer_punch_damage_pred = 25
+ var/base_damage = 25
+ var/damage_variance = 5
+
+/datum/action/xeno_action/activable/uppercut
+ name = "Uppercut"
+ action_icon_state = "rav_clothesline"
+ action_type = XENO_ACTION_CLICK
+ ability_primacy = XENO_PRIMARY_ACTION_3
+ xeno_cooldown = 25 SECONDS
+ var/base_damage = 15
+ var/base_knockback = 40
+ var/base_knockdown = 0.25
+ var/knockout_power = 11 // 11 seconds
+ var/base_healthgain = 5 // in percents of health per ko point
+
+/datum/action/xeno_action/activable/jab
+ name = "Jab"
+ action_icon_state = "pounce"
+ action_type = XENO_ACTION_CLICK
+ ability_primacy = XENO_PRIMARY_ACTION_2
+ xeno_cooldown = 4 SECONDS
+
+/datum/action/xeno_action/activable/boxer_punch/use_ability(atom/affected_atom)
+ var/mob/living/carbon/xenomorph/boxer_punch = owner
+ var/mob/living/carbon/carbon = affected_atom
+
+ if (!action_cooldown_check())
+ return
+
+ if (!isxeno_human(carbon) || boxer_punch.can_not_harm(carbon))
+ return
+
+ if (!boxer_punch.check_state() || boxer_punch.agility)
+ return
+
+ if (!boxer_punch.Adjacent(carbon))
+ return
+
+ if (!boxer_punch.Adjacent(carbon))
+ return
+
+ if(carbon.stat == DEAD)
+ return
+
+ if(HAS_TRAIT(carbon, TRAIT_NESTED))
+ return
+
+ var/obj/limb/target_limb = carbon.get_limb(check_zone(boxer_punch.zone_selected))
+
+ if (ishuman(carbon) && (!target_limb || (target_limb.status & LIMB_DESTROYED)))
+ target_limb = carbon.get_limb("chest")
+
+ if (!check_and_use_plasma_owner())
+ return
+
+ carbon.last_damage_data = create_cause_data(initial(boxer_punch.caste_type), boxer_punch)
+
+ boxer_punch.visible_message(SPAN_XENOWARNING("[boxer_punch] hits [carbon] in the [target_limb ? target_limb.display_name : "chest"] with a devastatingly powerful punch!"),
+ SPAN_XENOWARNING("We hit [carbon] in the [target_limb ? target_limb.display_name : "chest"] with a devastatingly powerful punch!"))
+ var/sound = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg')
+ boxer_punch.flick_attack_overlay(carbon, "slam")
+ playsound(carbon, sound, 50, 1)
+ do_boxer_punch(carbon, target_limb)
+ apply_cooldown()
+ return ..()
+
+
+/datum/action/xeno_action/activable/boxer_punch/proc/do_boxer_punch(mob/living/carbon/carbon, obj/limb/target_limb)
+ var/mob/living/carbon/xenomorph/boxer = owner
+
+ var/damage = rand(boxer_punch_damage, boxer_punch_damage + damage_variance)
+
+ if(ishuman(carbon))
+ if(isyautja(carbon))
+ damage = rand(boxer_punch_damage_pred, boxer_punch_damage_pred + damage_variance)
+ else if(target_limb.status & (LIMB_ROBOT | LIMB_SYNTHSKIN))
+ damage = rand(boxer_punch_damage_synth, boxer_punch_damage_synth + damage_variance)
+
+
+ carbon.apply_armoured_damage(get_xeno_damage_slash(carbon, damage), ARMOR_MELEE, BRUTE, target_limb? target_limb.name : "chest")
+
+ step_away(carbon, boxer)
+ if(prob(25)) // 25% chance to fly 2 tiles
+ step_away(carbon, boxer)
+ var/datum/behavior_delegate/boxer_base/boxer_delegate = boxer.behavior_delegate
+ if(istype(boxer_delegate))
+ boxer_delegate.melee_attack_additional_effects_target(carbon, 1)
+
+ var/datum/action/xeno_action/activable/jab/jab_action = get_action(boxer, /datum/action/xeno_action/activable/jab)
+ if(istype(jab_action) && !jab_action.action_cooldown_check())
+ if(isxeno(carbon))
+ jab_action.reduce_cooldown(jab_action.xeno_cooldown / 2)
+ else
+ jab_action.end_cooldown()
+
+/datum/action/xeno_action/activable/jab/use_ability(atom/A)
+ var/mob/living/carbon/xenomorph/boxer_jab = owner
+ var/mob/living/carbon/carbon_target = A
+
+ if(!action_cooldown_check())
+ return
+
+ if(boxer_jab.can_not_harm(carbon_target))
+ return
+
+ if (!isxeno_human(carbon_target))
+ return
+
+ var/distance = get_dist(boxer_jab, carbon_target)
+
+ if(distance > 3)
+ return
+
+ if(carbon_target.stat == DEAD)
+ return
+
+ if(HAS_TRAIT(carbon_target, TRAIT_NESTED))
+ return
+
+ if(!check_and_use_plasma_owner())
+ return
+
+ if(distance > 1 & 2)
+ step_towards(boxer_jab, carbon_target, 1)
+
+ if(!boxer_jab.Adjacent(carbon_target))
+ return
+
+ carbon_target.last_damage_data = create_cause_data(initial(boxer_jab.caste_type), boxer_jab)
+ boxer_jab.visible_message(SPAN_XENOWARNING("[boxer_jab] hits [carbon_target] with a powerful jab!"),
+ SPAN_XENOWARNING("You hit [carbon_target] with a powerful jab!"))
+ var/sound_pick = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg')
+ playsound(carbon_target, sound_pick, 45, 1)
+ boxer_jab.flick_attack_overlay(carbon_target, "slam")
+
+ var/datum/action/xeno_action/activable/boxer_punch/punch_action = get_action(boxer_jab, /datum/action/xeno_action/activable/boxer_punch)
+ if(punch_action && !punch_action.action_cooldown_check())
+ if(isxeno(carbon_target))
+ punch_action.reduce_cooldown(punch_action.xeno_cooldown / 2)
+ else
+ punch_action.end_cooldown()
+
+ carbon_target.Daze(3)
+ carbon_target.Slow(5)
+
+ var/datum/behavior_delegate/boxer_base/behavior_delegate = boxer_jab.behavior_delegate
+ if(istype(behavior_delegate))
+ behavior_delegate.melee_attack_additional_effects_target(carbon_target, 1)
+ apply_cooldown()
+ ..()
+
+
+/datum/action/xeno_action/activable/uppercut/use_ability(atom/A)
+ var/mob/living/carbon/xenomorph/upper_cut = owner
+
+ if(upper_cut.can_not_harm(A))
+ return
+
+ if(!upper_cut.check_state())
+ return
+
+ var/datum/behavior_delegate/boxer_base/behavior_delegate = upper_cut.behavior_delegate
+
+ if(!istype(behavior_delegate))
+ return
+
+ if(!behavior_delegate.punching_bag)
+ return
+
+ var/mob/living/carbon/carbon_target = behavior_delegate.punching_bag
+ if(carbon_target.stat == DEAD)
+ return
+
+ if(HAS_TRAIT(carbon_target, TRAIT_NESTED))
+ return
+
+ if(!check_and_use_plasma_owner())
+ return
+
+ if(!upper_cut.Adjacent(carbon_target))
+ return
+
+ if(carbon_target.mob_size >= MOB_SIZE_BIG)
+ to_chat(upper_cut, SPAN_XENOWARNING("[carbon_target] is too big for you to uppercut!"))
+ return
+
+ carbon_target.last_damage_data = create_cause_data(initial(upper_cut.caste_type), upper_cut)
+
+ var/ko_counter = behavior_delegate.ko_counter
+
+ var/damage = ko_counter >= 1
+ var/knockback = ko_counter >= 3
+ var/knockdown = ko_counter >= 6
+ var/knockout = ko_counter >= 9
+
+ var/message = (!damage) ? "weak" : (!knockback) ? "good" : (!knockdown) ? "powerful" : (!knockout) ? "gigantic" : "titanic"
+
+ upper_cut.visible_message(SPAN_XENOWARNING("[upper_cut] hits [carbon_target] with a [message] uppercut!"),
+ SPAN_XENOWARNING("We hit [carbon_target] with a [message] uppercut!"))
+ var/sound_pick = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg')
+ playsound(carbon_target, sound_pick, 50, 1)
+
+ if(behavior_delegate.ko_reset_timer != TIMER_ID_NULL)
+ deltimer(behavior_delegate.ko_reset_timer)
+ behavior_delegate.remove_ko()
+
+ var/obj/limb/target_limb = carbon_target.get_limb(check_zone(upper_cut.zone_selected))
+
+ if(damage)
+ carbon_target.apply_armoured_damage(get_xeno_damage_slash(carbon_target, base_damage * ko_counter), ARMOR_MELEE, BRUTE, target_limb? target_limb.name : "chest")
+
+ if(knockout)
+ carbon_target.KnockOut(knockout_power)
+ behavior_delegate.display_ko_message(carbon_target)
+ playsound(carbon_target, 'sound/effects/blackflash.ogg', 75, 1)
+
+ if(knockback)
+ carbon_target.explosion_throw(base_knockback * ko_counter, get_dir(upper_cut, carbon_target))
+
+ if(knockdown)
+ carbon_target.KnockDown(base_knockdown** ko_counter)
+
+ var/mob_multiplier = 1
+ if(isxeno(carbon_target))
+ mob_multiplier = XVX_WARRIOR_HEALMULT
+
+ if(ko_counter > 0)
+ upper_cut.gain_health(mob_multiplier * ko_counter * base_healthgain * upper_cut.maxHealth / 100)
+
+ apply_cooldown()
+ ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
index e4b135b5773f..8d8cebd577c9 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm
@@ -24,7 +24,7 @@
tackle_chance = 25
evolution_allowed = FALSE
- deevolves_to = list(XENO_CASTE_WARRIOR)
+ deevolves_to = list(XENO_CASTE_WARRIOR, XENO_CASTE_BOXER)
caste_desc = "A huge tanky xenomorph."
minimap_icon = "crusher"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
index 17d51bd063bf..7967f4a984e2 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Defender.dm
@@ -14,7 +14,7 @@
evasion = XENO_EVASION_NONE
speed = XENO_SPEED_TIER_6
- evolves_to = list(XENO_CASTE_WARRIOR)
+ evolves_to = list(XENO_CASTE_WARRIOR, XENO_CASTE_BOXER)
deevolves_to = list(XENO_CASTE_LARVA)
can_vent_crawl = 0
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
index 621903578c1c..513fd9bcede4 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Praetorian.dm
@@ -14,7 +14,7 @@
speed = XENO_SPEED_TIER_6
evolution_allowed = FALSE
- deevolves_to = list(XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER)
+ deevolves_to = list(XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOXER)
caste_desc = "The warleader of the hive."
spit_types = list(/datum/ammo/xeno/acid/praetorian)
acid_level = 2
diff --git a/code/modules/mob/living/carbon/xenomorph/emote.dm b/code/modules/mob/living/carbon/xenomorph/emote.dm
index 0b5057749603..0c60bd4a08fe 100644
--- a/code/modules/mob/living/carbon/xenomorph/emote.dm
+++ b/code/modules/mob/living/carbon/xenomorph/emote.dm
@@ -15,7 +15,7 @@
. = larva_sound
/datum/emote/living/carbon/xeno/growl
- mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound)
+ mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound, /mob/living/carbon/xenomorph/boxer)
key = "growl"
message = "growls."
@@ -24,7 +24,7 @@
emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
/datum/emote/living/carbon/xeno/hiss
- mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound)
+ mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound, /mob/living/carbon/xenomorph/boxer)
key = "hiss"
message = "hisses."
@@ -33,7 +33,7 @@
emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
/datum/emote/living/carbon/xeno/needshelp
- mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound)
+ mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound, /mob/living/carbon/xenomorph/boxer)
key = "needshelp"
message = "needs help!"
@@ -41,7 +41,7 @@
emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
/datum/emote/living/carbon/xeno/roar
- mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound)
+ mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/hellhound, /mob/living/carbon/xenomorph/boxer)
key = "roar"
message = "roars!"
@@ -51,10 +51,33 @@
emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
/datum/emote/living/carbon/xeno/tail
+ mob_type_blacklist_typecache = list(/mob/living/carbon/xenomorph/boxer)
key = "tail"
message = "swipes its tail."
sound = "alien_tail_swipe"
+/datum/emote/living/carbon/xeno/boxer ///yuji
+ mob_type_allowed_typecache = list(/mob/living/carbon/xenomorph/boxer)
+ keybind = FALSE
+
+/datum/emote/living/carbon/xeno/boxer/roar
+ key = "roar"
+ message = "ヘイ!オー・ピー・ピー!"
+ sound = 'sound/voice/opp.ogg'
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/xeno/boxer/iamyou
+ key = "iamyou"
+ message = "受け入れるよ、真人、私はお前だ。."
+ sound = 'sound/voice/iamyou.ogg'
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/xeno/boxer/whatareyou
+ key = "whatareyou"
+ message = "お前は一体何者だ、真人!"
+ sound = 'sound/voice/whatareyou.ogg'
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
/datum/emote/living/carbon/xeno/hellhound
mob_type_allowed_typecache = list(/mob/living/carbon/xenomorph/hellhound)
keybind = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
index c2edfe1fdf22..330d485ad0ba 100644
--- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm
+++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm
@@ -501,7 +501,7 @@
// Yes, Queen is technically considered to be tier 0
list(XENO_CASTE_LARVA = 0, XENO_CASTE_QUEEN = 0),
list(XENO_CASTE_DRONE = 0, XENO_CASTE_RUNNER = 0, XENO_CASTE_SENTINEL = 0, XENO_CASTE_DEFENDER = 0),
- list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0),
+ list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0, XENO_CASTE_BOXER = 0),
list(XENO_CASTE_BOILER = 0, XENO_CASTE_CRUSHER = 0, XENO_CASTE_PRAETORIAN = 0, XENO_CASTE_RAVAGER = 0, XENO_CASTE_DESPOILER = 0)
)
@@ -523,7 +523,7 @@
var/list/xeno_icons = list(
list(XENO_CASTE_LARVA = "", XENO_CASTE_QUEEN = "", XENO_CASTE_PREDALIEN_LARVA = "", XENO_CASTE_HELLHOUND = ""),
list(XENO_CASTE_DRONE = "", XENO_CASTE_RUNNER = "", XENO_CASTE_SENTINEL = "", XENO_CASTE_DEFENDER = "", XENO_CASTE_PREDALIEN = ""),
- list(XENO_CASTE_HIVELORD = "", XENO_CASTE_BURROWER = "", XENO_CASTE_CARRIER = "", XENO_CASTE_LURKER = "", XENO_CASTE_SPITTER = "", XENO_CASTE_WARRIOR = ""),
+ list(XENO_CASTE_HIVELORD = "", XENO_CASTE_BURROWER = "", XENO_CASTE_CARRIER = "", XENO_CASTE_LURKER = "", XENO_CASTE_SPITTER = "", XENO_CASTE_WARRIOR = "", XENO_CASTE_BOXER = ""),
list(XENO_CASTE_BOILER = "", XENO_CASTE_CRUSHER = "", XENO_CASTE_PRAETORIAN = "", XENO_CASTE_RAVAGER = "", XENO_CASTE_DESPOILER = "")
)
diff --git a/colonialmarines.dme b/colonialmarines.dme
index dd70a2945f13..3f65ed803c04 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -2253,6 +2253,7 @@
#include "code\modules\mob\living\carbon\xenomorph\abilities\warrior\warrior_abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\abilities\warrior\warrior_macros.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Boiler.dm"
+#include "code\modules\mob\living\carbon\xenomorph\castes\Boxer.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Burrower.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\Carrier.dm"
#include "code\modules\mob\living\carbon\xenomorph\castes\caste_datum.dm"
diff --git a/icons/mob/xenos/castes/tier_2/boxer.dmi b/icons/mob/xenos/castes/tier_2/boxer.dmi
new file mode 100644
index 000000000000..ee959f5673fb
Binary files /dev/null and b/icons/mob/xenos/castes/tier_2/boxer.dmi differ
diff --git a/icons/mob/xenos/castes/tier_2/warrior.dmi b/icons/mob/xenos/castes/tier_2/warrior.dmi
index af3000a3dd5a..706f251e62cc 100644
Binary files a/icons/mob/xenos/castes/tier_2/warrior.dmi and b/icons/mob/xenos/castes/tier_2/warrior.dmi differ
diff --git a/icons/mob/xenos/onmob/warrior.dmi b/icons/mob/xenos/onmob/warrior.dmi
index 4351ad78339e..706f251e62cc 100644
Binary files a/icons/mob/xenos/onmob/warrior.dmi and b/icons/mob/xenos/onmob/warrior.dmi differ
diff --git a/sound/effects/blackflash.ogg b/sound/effects/blackflash.ogg
new file mode 100644
index 000000000000..2e7c30e5d461
Binary files /dev/null and b/sound/effects/blackflash.ogg differ
diff --git a/sound/voice/iamyou.ogg b/sound/voice/iamyou.ogg
new file mode 100644
index 000000000000..4d282bddcc78
Binary files /dev/null and b/sound/voice/iamyou.ogg differ
diff --git a/sound/voice/opp.ogg b/sound/voice/opp.ogg
new file mode 100644
index 000000000000..b9ce7591b367
Binary files /dev/null and b/sound/voice/opp.ogg differ
diff --git a/sound/voice/whatareyou.ogg b/sound/voice/whatareyou.ogg
new file mode 100644
index 000000000000..9003e41c1cbe
Binary files /dev/null and b/sound/voice/whatareyou.ogg differ
diff --git a/sound/voice/yuji_talk.ogg b/sound/voice/yuji_talk.ogg
new file mode 100644
index 000000000000..c22f04aa5515
Binary files /dev/null and b/sound/voice/yuji_talk.ogg differ
diff --git a/sound/voice/yuji_talk2.ogg b/sound/voice/yuji_talk2.ogg
new file mode 100644
index 000000000000..a4b37b67dba9
Binary files /dev/null and b/sound/voice/yuji_talk2.ogg differ
diff --git a/sound/voice/yuji_talk3.ogg b/sound/voice/yuji_talk3.ogg
new file mode 100644
index 000000000000..d12a2c21609b
Binary files /dev/null and b/sound/voice/yuji_talk3.ogg differ