#MOD BEGIN - lance piercing multiple targets
#lance penetration system adapted from code by SupaNinjaMan - https://forums.taleworlds.com/index.php?threads/pierce-through-multiple-enemies-in-a-line.458998/post-9872324
rndl_lance_penetration_system_on_agent_hit = (ti_on_agent_hit,0,0,
[],
[
#cheat sheet of used pos:
#pos10 = point of weapon impact (on 1st agent hit)
#pos11 = attacker
#pos12 = struck bone (of next agent in line)
#pos13 = local coordinates of pos12 relative to pos10
#loop prevention check (must be done first)
(store_trigger_param_1,":triggering_agent"), #get agent on whom the trigger fired
(agent_slot_eq,":triggering_agent",slot_agent_just_pierced_by,-1), #make sure he wasn't hit by previous instance of this trigger (being the 2nd pierced through damage scripted below also triggers ti_on_agent_hit, so we're preventing an infinite loop here)
#gather trigger data because something overwrites it in memory
(store_trigger_param_2,":attacker"), #get attacker
(store_trigger_param_3,":damage"), #damage amount from hit that fired this trigger
(assign,":attacker_weapon",reg0), #get striking weapon that fired the trigger
(copy_position,pos10,pos0), #get weapon's point of impact and blow direction
#check if it was a couched lance hit
(gt,":attacker_weapon",0), #prevent errors from the item_get_weapon_length getting served -1
(agent_get_animation,":upper_anim",":attacker",1), #What is there upper body doing?
(this_or_next|eq,":upper_anim","anim_lancer_ride_4"), #couched lance anim with shield equipped
(this_or_next|eq,":upper_anim","anim_lancer_ride_4_no_shield"), #couched lance anim without shield
( eq,":upper_anim","anim_lancer_charge_parried"), #couched hit was parried (this anim will play with lance used its crush through blocks capability to deal damage
#now we can proceed to hurt additional targets
#determine strike vector
(agent_get_position,pos11,":attacker"), #get attacker's direction
(position_copy_rotation,pos10,pos11), #use it to correct the wonky direction of blow - this gives us a vector rooted where 1st pierced agent was hit, and pointing in the direction of attacker's movement (where the tip of the lance would go)
#determine affected area
(set_fixed_point_multiplier,100), #setting to working in centimeters (multiplier=1 would be meters)
#when lance hits, the horse will continue moving forward, until reaching the enemy line, and then push into the enemy line a bit - the tip of the lance will move forward the same distance, so that's weapon's actual reach
(item_get_weapon_length,":reach_of_attack",":attacker_weapon"), #the distance the horse will go is roughly weapon length (minus distance between weapon's grip and horse's front, plus it will push a bit into enemy line actually... so this bracketed part cancels out to roughly zero)
#finally, we don't deal with hitboxes, but centers of bones - gotta increase reach by radius of bone
(assign,":bone_diameter",60), #each bone is different, so it's a rough estimation
(store_div,":bone_radius",":bone_diameter",2),
(store_mul,":bone_radius_minus",":bone_radius",-1), #lower bound must be negative
(store_add,":bone_radius_plus",":bone_radius",1), #must be upper bound +1 (in range calculations upper value is never reached)
(val_add,":reach_of_attack",":bone_radius"), #and later when checking that reach, we will pad from the other side by checking bone_radius_minus to reach, instead of 0 to reach
(store_add,":search_range",":reach_of_attack",50), #just in case let's scan for targets in an area larger than our reach, say +50 cm to search radius
#common operations for all agents in the loop
(try_begin), #count agents
(agent_is_human,":triggering_agent"),
(assign,":targets_struck",1), #one is already hit, that's our initial agent that fired the trigger
(else_try),
(assign,":targets_struck",0), #initial target is horse, we don't count them
(try_end),
(agent_set_slot,":triggering_agent",slot_agent_just_pierced_by,":attacker"), #mark him as already hit
(agent_get_team,":attacker_team",":attacker"), #get attacker team for comparison with each agent in range
#find valid targets
(try_for_agents,":target",pos10,":search_range"), #search around spear tip (pos10)
#(neq,":target",":triggering_agent"), #already covered by agent slot check below (we fill the slot somewhere above)
(agent_slot_eq,":target",slot_agent_just_pierced_by,-1), #we need to prevent multiple pierces through 1 agent within 1 frame, so we only work on those not marked as already pierced (this is cleared every 0.1 sec, roughly every 6 frames, so still a high chance of being pierced by multiple targets)
#(neq,":target",":attacker"), #covered by friendly fire check
#(neq,":target",":attacker_horse"), #covered by friendly fire check
#(agent_is_active,":target"), #not needed, try_for_agents only loops through active agents
(agent_is_alive,":target"), #agent is not dead (dead agents have no bones and will break the engine when trying to check them)
(neg|agent_is_wounded,":target"), #not wounded either (same problem as above)
#set bones and friendly-fire check
(try_begin), #human
(agent_is_human,":target"),
#human bones (there is 20):
#0 = abdomen (lowest part of torso)
#1 = thigh.L (left upper leg)
#2 = calf.L (left lower leg)
#3 = foot.L (left foot)
#4 = thigh.R (right upper leg)
#5 = calf.R (right lower leg)
#6 = foot.R (right foot)
#7 = spine (middle part of torso)
#8 = thorax (upper part of torso)
#9 = head
#10 = shoulder.L (left shoulder)
#11 = upperarm.L (left upper arm)
#12 = forearm.L (left forearm)
#13 = hand.L (left hand) - has no hitbox!
#14 = item.L (base of item in left hand) - has no hitbox!
#15 = shoulder.R (right shoulder)
#16 = upperarm.R (right upper arm)
#17 = forearm.R (right forearm)
#18 = hand.R (right hand) - has no hitbox!
#19 = item.R (base of item in right hand) - has no hitbox!
(assign,":num_bones",19), #skip right-hand item bone, it's almost same pos as hand (this is just for performance, so no need to skip left-hand item, which is in the middle of bones)
(agent_get_team,":target_team",":target"), #belongs to his own team
(else_try), #horse
#horse bones:
#0 = abdomen (back half of torso)
#1 = thorax (upper part of abdomen bone) - has no hitbox
#2 = spine_2 (front half of torso)
#3 = spine_3 (upper part of spine_2 bone) - has no hitbox
#4 = neck (bottom part of neck)
#5 = neck_2 (middle part of neck)
#6 = neck_3 (upper part of neck)
#7 = head
#8 = uarm.L (above front-left leg, where neck meets torso) - has no hitbox
#9 = forearm.L (upper part of front-left leg)
#10 = f_foot.L (middle part of front-left leg)
#11 = f_hoof.L (lower part of front-left leg)
#12 = clavicle.L (hoof and below of front-left leg) - has no hitbox
#13 = uarm.R (above front-right leg, where neck meets torso) - has no hitbox
#14 = forearm.R (upper part of front-right leg)
#15 = f_foot.R (middle part of front-right leg)
#16 = f_hoof.R (lower part of front-right leg)
#17 = clavicle.R (hoof and below of front-right leg) - has no hitbox
#18 = thigh.L (upper part of back-left leg)
#19 = calf.L (middle part of back-left leg)
#20 = h_foot.L (lower part of back-left leg)
#21 = h_hoof.L (hoof and below of back-left leg) - has no hitbox
#22 = thigh.R (upper part of back-right leg)
#23 = calf.R (middle part of back-right leg)
#24 = h_foot.R (lower part of back-right leg)
#25 = h_hoof.R (hoof and below of back-right leg) - has no hitbox
#26 = tail1 (upper part of tail, where horse has living flesh)
#27 = tail2 (lower part of tail, where horse has just hair)
(assign,":num_bones",26), #skip both tail bones, but otherwise loop through all bones (even hooves, because lance is especially dangerous to horse's legs)
(agent_get_rider,":rider",":target"),
(ge,":rider",0), #with a rider
(agent_get_team,":target_team",":rider"), #belongs to rider's team
(else_try), #without a rider
(agent_get_team,":target_team",":target"), #belongs to his own team
(try_end),
(this_or_next|teams_are_enemies,":attacker_team",":target_team"), #enemy
( eq,":target_team",7), #or neutral (we already excluded neutral horses that do have a rider)
(try_for_range,":bone",0,":num_bones"),
(agent_get_bone_position,pos12,":target",":bone",1),
(position_transform_position_to_local,pos13,pos10,pos12), #need relative distance, so we can calculate distance to our weapon's attack vector
(position_get_x,":pos13_x",pos13),
(position_get_y,":pos13_y",pos13),
(position_get_z,":pos13_z",pos13),
(is_between,":pos13_x",":bone_radius_minus",":bone_radius_plus"), #center of bone not far from vector of attack, edge of bone should be in range
(is_between,":pos13_z",":bone_radius_minus",":bone_radius_plus"), #center of bone not far from vector of attack, edge of bone should be in range
(is_between,":pos13_y",":bone_radius_minus",":reach_of_attack"), #center of bone not far from vector of attack, edge of bone should be in range
(try_begin),
(eq,":num_bones",19), #it's human (trying not to repeat agent_is_human again)
(val_add,":targets_struck",1), #count player's human victims only
(try_end),
(assign,":num_bones",0), #break
(try_end),
(eq,":num_bones",0), #break was used in bone loop
(agent_set_slot,":target",slot_agent_just_pierced_by,":attacker"), #mark him for piercing
(try_end),
(try_begin), #display score to player, for bragging rights
(ge,":targets_struck",2), #don't show score for just 1 enemy hit, that's not an accomplishment
(get_player_agent_no,":player_agent"),
(eq,":attacker",":player_agent"),
(try_begin), #personal best?
(item_get_slot,":best_score",":attacker_weapon",slot_item_lance_pierce_best_multihit_score),
(gt,":targets_struck",":best_score"),
(str_store_item_name,s13,":attacker_weapon"),
(display_message,"@Personal best for {s13}!",0xFEDC11), #(same color as "Head shot!" for bows)
(item_set_slot,":attacker_weapon",slot_item_lance_pierce_best_multihit_score,":targets_struck"),
(try_end),
(assign,reg13,":targets_struck"),
(display_message,"@Enemies pierced: {reg13}",0xA9A9A9), #(same color as "Shot difficulty: x.xx" for bows)
(try_end),
#execute agents
(try_for_agents,":target",pos10,":search_range"), #limit search to spear tip again, for optimization
(neq,":target",":triggering_agent"), #he's getting killed by trigger result, if we don't skip him here, he will get 2 notifications
(agent_slot_eq,":target",slot_agent_just_pierced_by,":attacker"), #marked for death
(agent_is_alive,":target"), #just to make sure he wasn't killed by something else yet
(neg|agent_is_wounded,":target"), #same as above
(agent_deliver_damage_to_agent,":attacker",":target",":damage",":attacker_weapon"), #to preserve blunt/lethal dmg setting, we set it to attacker's weapon, and deal same dmg as 1st target received
(try_end),
(set_trigger_result,":damage"), #stupid as it is, if we don't set the trigger result, the 1st agent hit survives
]
)
rndl_lance_penetration_system_on_timer = (0.1,0,0,
[],
[
(try_for_agents,":agent"), #loop through all agents
(agent_set_slot,":agent",slot_agent_just_pierced_by,-1), #clear their "I was hit by this guy" slot
(try_end),
#this doesn't cover spawning agents, they will have 0 in that slot for the first 0.1 sec of existing, but this simply makes them immune to being pierced for that short time if somebody is spawn camping - seems fair
]
)
rndl_lance_penetration_system_on_agent_killed_or_wounded = (ti_on_agent_killed_or_wounded,0,0,
[],
[
#the whole purpose of this entire trigger is to detect that blunt weapon was used, then prevent the agent from dying and knock him unconscious instead
(store_trigger_param_1,":dead_agent_no"),
(store_trigger_param_2,":killer_agent_no"),
#(store_trigger_param_3,":is_wounded"), #not needed, we don't have to check it, just set it
(agent_slot_eq,":dead_agent_no",slot_agent_just_pierced_by,":killer_agent_no"), #make sure it was our scripted kill
(agent_is_human,":killer_agent_no"), #there is a minimum chance that agent was killed by a normal non-scripted attack in less than 0.1 sec after spawning, and his slot still holds 0, but agent = 0 didn't kill him with couched lance, so we gotta check if agent isn't a horse that trampled him
(agent_get_wielded_item,":wielded",":killer_agent_no",0), #0 = mainhand item, 1 = offhand item (bow is also mainhand, for 2-handed weapons returns -1 for offhand)
(ge,":wielded",0), #weapon exists
(item_get_thrust_damage_type,":damage_type",":wielded"), #cut = 0, pierce = 1, blunt = 2
(eq,":damage_type",2),
(set_trigger_result,2), #result = 2 means force fall unconscious (1 = force killed, 0 = no change)
]
)
#MOD END - lance piercing multiple targets