<commit_before>// Active directional block system. Shared code is in [living_blocking_parrying.dm]
/mob/living/proc/stop_active_blocking(was_forced = FALSE)
	if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)))
		return FALSE
	var/obj/item/I = active_block_item
	combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCKING | COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
	active_block_effect_end()
	active_block_item = null
	REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)
	REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
	remove_movespeed_modifier(/datum/movespeed_modifier/active_block)
	var/datum/block_parry_data/data = I.get_block_parry_data()
	if(timeToNextMove() < data.block_end_click_cd_add)
		changeNext_move(data.block_end_click_cd_add)
	return TRUE

/mob/living/proc/ACTIVE_BLOCK_START(obj/item/I)
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return FALSE
	if(!(I in held_items))
		return FALSE
	var/datum/block_parry_data/data = I.get_block_parry_data()
	if(!istype(data))		//Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened.
		CRASH("ACTIVE_BLOCK_START called with an item with no valid data: [I] --> [I.block_parry_data]!")
	combat_flags |= COMBAT_FLAG_ACTIVE_BLOCKING
	active_block_item = I
	if(data.block_lock_attacking)
		ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)		//probably should be something else at some point
	if(data.block_lock_sprinting)
		ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
	add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown)
	active_block_effect_start()
	return TRUE

/// Visual effect setup for starting a directional block
/mob/living/proc/active_block_effect_start()
	visible_message("<span class='warning'>[src] raises their [active_block_item], dropping into a defensive stance!</span>")
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_OUT)

/// Visual effect cleanup for starting a directional block
/mob/living/proc/active_block_effect_end()
	visible_message("<span class='warning'>[src] lowers their [active_block_item].</span>")
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN)

/mob/living/proc/continue_starting_active_block()
	if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
		return DO_AFTER_STOP
	return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP

/mob/living/get_standard_pixel_x_offset()
	. = ..()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		if(dir & EAST)
			. += 8
		if(dir & WEST)
			. -= 8

/mob/living/get_standard_pixel_y_offset()
	. = ..()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		if(dir & NORTH)
			. += 8
		if(dir & SOUTH)
			. -= 8

/**
  * Proc called by keybindings to toggle active blocking.
  */
/mob/living/proc/keybind_toggle_active_blocking()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return keybind_stop_active_blocking()
	else
		return keybind_start_active_blocking()

/**
  * Proc called by keybindings to start active blocking.
  */
/mob/living/proc/keybind_start_active_blocking()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return FALSE
	if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE))
		to_chat(src, "<span class='warning'>You're not something that can actively block.</span>")
		return FALSE
	// QOL: Instead of trying to just block with held item, grab first available item.
	var/obj/item/I = find_active_block_item()
	var/list/other_items = list()
	if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_BLOCK_START, I, other_items) & COMPONENT_PREVENT_BLOCK_START)
		to_chat(src, "<span class='warning'>Something is preventing you from blocking!</span>")
		return
	if(!I)
		if(!length(other_items))
			to_chat(src, "<span class='warning'>You can't block with your bare hands!</span>")
			return
		I = other_items[1]
	if(!I.can_active_block())
		to_chat(src, "<span class='warning'>[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)</span>")
		return
	// QOL: Attempt to toggle on combat mode if it isn't already
	SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE)
	if(!SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
		to_chat(src, "<span class='warning'>You must be in combat mode to actively block!</span>")
		return FALSE
	var/datum/block_parry_data/data = I.get_block_parry_data()
	var/delay = data.block_start_delay
	combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN)
	if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I))
		to_chat(src, "<span class='warning'>You fail to raise [I].</span>")
		combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
		animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW)
		return
	combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
	ACTIVE_BLOCK_START(I)

/**
  * Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE
  */
/mob/living/proc/find_active_block_item()
	var/obj/item/held = get_active_held_item()
	if(!held?.can_active_block())
		for(var/obj/item/I in held_items - held)
			if(I.can_active_block())<commit_msg>Update living_active_block.dm<commit_after>// Active directional block system. Shared code is in [living_blocking_parrying.dm]
/mob/living/proc/stop_active_blocking(was_forced = FALSE)
	if(!(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)))
		return FALSE
	var/obj/item/I = active_block_item
	combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCKING | COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
	active_block_effect_end()
	active_block_item = null
	REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)
	REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
	remove_movespeed_modifier(/datum/movespeed_modifier/active_block)
	var/datum/block_parry_data/data = I.get_block_parry_data()
	if(timeToNextMove() < data.block_end_click_cd_add)
		changeNext_move(data.block_end_click_cd_add)
	return TRUE

/mob/living/proc/ACTIVE_BLOCK_START(obj/item/I)
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return FALSE
	if(!(I in held_items))
		return FALSE
	var/datum/block_parry_data/data = I.get_block_parry_data()
	if(!istype(data))		//Typecheck because if an admin/coder screws up varediting or something we do not want someone being broken forever, the CRASH logs feedback so we know what happened.
		CRASH("ACTIVE_BLOCK_START called with an item with no valid data: [I] --> [I.block_parry_data]!")
	combat_flags |= COMBAT_FLAG_ACTIVE_BLOCKING
	active_block_item = I
	if(data.block_lock_attacking)
		ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)		//probably should be something else at some point
	if(data.block_lock_sprinting)
		ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
	add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown)
	active_block_effect_start()
	return TRUE

/// Visual effect setup for starting a directional block
/mob/living/proc/active_block_effect_start()
	visible_message("<span class='warning'>[src] raises their [active_block_item], dropping into a defensive stance!</span>")
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_OUT)

/// Visual effect cleanup for starting a directional block
/mob/living/proc/active_block_effect_end()
	visible_message("<span class='warning'>[src] lowers their [active_block_item].</span>")
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN)

/mob/living/proc/continue_starting_active_block()
	if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
		return DO_AFTER_STOP
	return (combat_flags & COMBAT_FLAG_ACTIVE_BLOCK_STARTING)? DO_AFTER_CONTINUE : DO_AFTER_STOP

/mob/living/get_standard_pixel_x_offset()
	. = ..()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		if(dir & EAST)
			. += 8
		if(dir & WEST)
			. -= 8

/mob/living/get_standard_pixel_y_offset()
	. = ..()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		if(dir & NORTH)
			. += 8
		if(dir & SOUTH)
			. -= 8

/**
  * Proc called by keybindings to toggle active blocking.
  */
/mob/living/proc/keybind_toggle_active_blocking()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return keybind_stop_active_blocking()
	else
		return keybind_start_active_blocking()

/**
  * Proc called by keybindings to start active blocking.
  */
/mob/living/proc/keybind_start_active_blocking()
	if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING))
		return FALSE
	if(!(combat_flags & COMBAT_FLAG_BLOCK_CAPABLE))
		to_chat(src, "<span class='warning'>You're not something that can actively block.</span>")
		return FALSE
	// QOL: Instead of trying to just block with held item, grab first available item.
	var/obj/item/I = find_active_block_item()
	var/list/other_items = list()
	if(SEND_SIGNAL(src, COMSIG_LIVING_ACTIVE_BLOCK_START, I, other_items) & COMPONENT_PREVENT_BLOCK_START)
		to_chat(src, "<span class='warning'>Something is preventing you from blocking!</span>")
		return
	if(!I)
		if(!length(other_items))
			to_chat(src, "<span class='warning'>You can't block with your bare hands!</span>")
			return
		I = other_items[1]
	if(!I.can_active_block())
		to_chat(src, "<span class='warning'>[I] is either not capable of being used to actively block, or is not currently in a state that can! (Try wielding it if it's twohanded, for example.)</span>")
		return
	// QOL: Attempt to toggle on combat mode if it isn't already
	SEND_SIGNAL(src, COMSIG_ENABLE_COMBAT_MODE)
	if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
		to_chat(src, "<span class='warning'>You must be in combat mode to actively block!</span>")
		return FALSE
	var/datum/block_parry_data/data = I.get_block_parry_data()
	var/delay = data.block_start_delay
	combat_flags |= COMBAT_FLAG_ACTIVE_BLOCK_STARTING
	animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = delay, FALSE, SINE_EASING | EASE_IN)
	if(!do_after_advanced(src, delay, src, DO_AFTER_REQUIRES_USER_ON_TURF|DO_AFTER_NO_COEFFICIENT, CALLBACK(src, .proc/continue_starting_active_block), MOBILITY_USE, null, null, I))
		to_chat(src, "<span class='warning'>You fail to raise [I].</span>")
		combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
		animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW)
		return
	combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING)
	ACTIVE_BLOCK_START(I)

/**
  * Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE
  */
/mob/living/proc/find_active_block_item()
	var/obj/item/held = get_active_held_item()
	if(!held?.can_active_block())
		for(var/obj/item/I in held_items - held)
			if(I.can_active_block())
{'fraction_non_alphanumeric': 0.10336478249979127, 'fraction_numerical': 0.0018368539701093763, 'mean_word_length': 6.325993883792049, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3098465', 'n_tokens_mistral': 4549, 'n_tokens_neox': 4418, 'n_words': 858}