Pumpkin Lord
Aye Forge,
I decided to release the codes of some of the features I once released in Vyrn OSP about a year ago or so.
Vyrn AI was a project that I worked on for a month or so. I needed to have an actual fighting AI that is smart and flexible. Dangerous, too. Hardcore stuff, player-like. Anyway, it works in certain levels that you set in game_start. You can set levels for any kind of troop you want. The lower the number, better it will fight. AI can:
- Block
- Feint
- Position himself a lot better
- Kick (needs work)
- Chamber
- Random hit
- Counter-attack
- Avoid/dodge (need the animation from Vyrn OSP)
- Special strike (needs work)
- Roll forward (needs animation from Vyrn OSP)
- Jump charge (needs work & animation from Vyrn OSP)
It was an attempt on hardcore fantasy RPG. Feel free to use or edit any part you want as long as you give credits. You might need to clean it a bit from Vyrn-related stuff. Be aware of that.
Having said that, I am no coder in IRL and code could use improvements. It is a WIP. Do whatever you want with it.
Happy modding!
1 - ) Firstly, add these mission triggers to anywhere in mission_templates.py and then to any mission you want.
vyrn_ai_continue - > This trigger handles the special moves like jump charge and special strike. Both needs work, so you might not want to use them.
vyrn_ai - > This trigger is where everything is. How it works is that, it makes the necessary checks for an agent in suitable conditions, gets it vyrn_ai level and starts working each segment in combat in relation to the level of the troop's AI and a bit of random dices. It needs to work every milisecond or so, maybe a second would do. I guess that would also change the difficulty of the AI dramatically.
Code:
vyrn_ai_continue = (
1, 0, 0, [(eq, "$vyrn_AI_open", 1),],
[
#(store_mission_timer_c, ":timerc"),
(get_player_agent_no, ":play"),
(agent_is_active, ":play"),
(agent_is_alive, ":play"),
#player setup
(try_for_agents, ":agse"),
(neq, ":agse", ":play"), #Agent is not player
(agent_is_active, ":agse"),
(agent_is_alive, ":agse"),
(agent_is_human, ":agse"),
(agent_slot_eq, ":agse", slot_agent_is_running_away, 0), #isnt fleeing
(agent_get_troop_id, ":agstrpe",":agse"),
(troop_get_slot, ":is_AIe", ":agstrpe", slot_agent_has_vyrn_AI), #if agent has vyrn AI
(eq, ":is_AIe", 1), #yes
(troop_get_slot, ":chance_AIe", ":agstrpe", slot_vyrn_AI_chance),
(agent_get_team, ":ags_teame", ":agse"),
#(assign, ":max_distance", 300), #distance between agents
(agent_ai_get_look_target,":possiblee",":agse"),
(gt,":possiblee",0), #there is at least someone
(agent_is_active, ":possiblee"),
(agent_is_alive, ":possiblee"),
(agent_is_human, ":possiblee"),
(agent_get_team, ":possible_teame", ":possiblee"),
(neq, ":possible_teame", ":ags_teame"), #unfriendly teams
(agent_get_position, pos13, ":possiblee"),
(agent_get_position, pos14, ":agse"),
(neg|position_is_behind_position, pos13, pos14), #agents are not behind each other
(get_distance_between_positions, ":diste", pos13, pos14),
# (agent_get_defend_action, ":pos_defe", ":possiblee"), #attack action
#(agent_get_action_dir, ":pos_atkdire", ":possiblee"),
# (agent_get_attack_action, ":pos_atke", ":possiblee"), #attack action
(agent_get_animation, ":agse_anime", ":agse", 1),
(agent_get_wielded_item, ":l_wielded_itema", ":agse", 0),
#(agent_get_wielded_item, ":r_wielded_itema", ":agse", 1),
#(agent_get_animation, ":pos_anime", ":possiblee", 1),
(store_random_in_range, ":chance_strengthe", 1, ":chance_AIe"), #chance is based on troop
(eq, ":chance_strengthe", 1),
#Jump Charge
(try_begin),
(is_between, ":diste", 600, 800),
(neq, ":agse_anime", "anim_jump_charge"),
(agent_set_animation, ":agse", "anim_jump_charge"),
(le, ":diste", 600),
(agent_set_attack_action, ":agse", 3, 0),
(agent_set_position, ":agse", pos13),
(try_end),
# #Brutal Strike
# (try_begin),
# (store_random_in_range, ":dice", 1, 200),
# (eq, ":dice", 1),
# (le, ":diste", 200),
# (agent_set_animation, ":agse", "anim_brutal_strike"),
# (agent_get_bone_position, pos9, ":agse", hb_hand_r, 1),
# (get_distance_between_positions, ":dist_brutal", pos9, pos13),
# (le, ":dist_brutal", 200),
# (store_random_in_range, ":brutal_damage", 35, 70),
# (agent_deliver_damage_to_agent, ":agse", ":possiblee", ":brutal_damage", ":l_wielded_itema"),
# (agent_set_animation, ":possiblee", "anim_vyrn_AI_fall", 1),
# (try_end),
(try_end),
])
vyrn_ai = (
0, 0, 0, [(eq, "$vyrn_AI_open", 1),], [
(get_player_agent_no, ":pla"),
(agent_is_active, ":pla"),
(agent_is_alive, ":pla"),
#player setup
(try_for_agents, ":ags"),
(neq, ":ags", ":pla"), #Agent is not player
(agent_is_active, ":ags"),
(agent_is_alive, ":ags"),
(agent_is_human, ":ags"),
(agent_slot_eq, ":ags", slot_agent_is_running_away, 0), #isnt fleeing
(agent_get_troop_id, ":agstrp",":ags"),
(troop_get_slot, ":is_AI", ":agstrp", slot_agent_has_vyrn_AI), #if agent has vyrn AI
(eq, ":is_AI", 1), #yes
(troop_get_slot, ":chance_AI", ":agstrp", slot_vyrn_AI_chance),
(agent_get_team, ":ags_team", ":ags"),
#(assign, ":max_distance", 300), #distance between agents
(agent_ai_get_look_target,":possible",":ags"),
(gt,":possible",0), #there is at least someone
(agent_is_active, ":possible"),
(agent_is_alive, ":possible"),
(agent_is_human, ":possible"),
(agent_get_team, ":possible_team", ":possible"),
(neq, ":possible_team", ":ags_team"), #unfriendly teams
(agent_get_position, pos11, ":possible"),
(agent_get_position, pos12, ":ags"),
(neg|position_is_behind_position, pos11, pos12), #agents are not behind each other
(get_distance_between_positions, ":dist", pos11, pos12),
(agent_get_defend_action, ":pos_def", ":possible"), #attack action
(agent_get_action_dir, ":pos_atkdir", ":possible"),
(agent_get_attack_action, ":pos_atk", ":possible"), #attack action
(agent_get_animation, ":ags_anim", ":ags", 1),
(agent_get_animation, ":pos_anim", ":possible", 1),
(store_random_in_range, ":chance_strength", 1, ":chance_AI"), #chance is based on troop
(eq, ":chance_strength", 1),
#Footwork
(try_begin),
(try_begin),
(le, ":dist", 500),
(agent_force_rethink, ":ags"),
(agent_set_scripted_destination, ":ags", pos11, 0, 1),
(else_try),
(is_between, ":dist", 0, 300),
(agent_force_rethink, ":ags"),
(store_random_in_range, ":dice_for_footwork", 0, 5),
(eq, ":dice_for_footwork", 1),
(store_random_in_range, ":random", -60, 60),
(position_move_x, pos11, ":random", 0),
(position_move_y, pos11, ":random", 0),
(position_rotate_x, pos11, ":random", 0),
(position_rotate_y, pos11, ":random", 0),
(agent_set_scripted_destination, ":ags", pos11, 0, 1),
(try_end),
(try_end),
#Chambering
(try_begin),
(le, ":dist", 200),
(store_random_in_range, ":extra_dice", 1, 80),
(neq, ":pos_atk", 1),
(eq, ":pos_atk", 2),
(eq, ":extra_dice", 1),
(try_begin),
(eq, ":pos_atkdir", 0),
(agent_set_attack_action, ":ags", 0, 0),
(else_try),
(eq, ":pos_atkdir", 1),
(agent_set_attack_action,":ags", 2, 0),
(else_try),
(eq, ":pos_atkdir", 2),
(agent_set_attack_action, ":ags", 1, 0),
(else_try),
(eq, ":pos_atkdir", 3),
(agent_set_attack_action, ":ags", 3, 0),
(try_end),
(try_end),
#Chamber disarming
(try_begin),
(eq, ":pos_def", 1),
(neq, ":pos_def", 2),
(eq, ":pos_atkdir", 1),
(eq, ":pos_atkdir", 2),
(neq, ":pos_atk", 1),
(eq, ":pos_atk", 2),
(agent_is_in_parried_animation, ":possible"),
(agent_get_wielded_item, ":weapon", ":ags", 0),
(agent_unequip_item, ":ags", ":weapon"),
(agent_get_bone_position, pos2, ":ags", hb_hand_r, 1),
(store_random_in_range, ":z_rotation", 0, 360),
(store_random_in_range, ":y_rotation", -60, 60),
(store_random_in_range, ":x_pos", -90, 90),
(store_random_in_range, ":y_pos", -90, 90),
(position_rotate_z, pos2,":z_rotation"),
(position_rotate_y, pos2,":y_rotation"),
(position_move_x, pos2, ":x_pos"),
(position_move_y, pos2, ":y_pos"),
(position_set_z_to_ground_level, pos2),
(position_move_z, pos2, 5),
(set_spawn_position, pos2),
(spawn_item, ":weapon"),
(try_end),
#Feinting
(try_begin),
(store_random_in_range, ":dice", 1, 60),
(store_random_in_range, ":style", 1, 5),
(store_random_in_range, ":random_dir", 0, 3),
(store_random_in_range, ":random_action", 0, 1),
(le, ":dist", 200),
(eq, ":dice", 1),
(eq, ":pos_def", 2),
(assign, ":continue", 0),
(eq, ":continue", 0),
(try_begin),
(eq, ":style", 1),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 1),
(else_try),
(eq, ":style", 2),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 1),
(else_try),
(eq, ":style", 3),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 1),
(else_try),
(eq, ":style", 4),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 0),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(assign, ":continue", 1),
(try_end),
(try_end),
#Counter-attack
(try_begin),
(le, ":dist", 200),
(this_or_next|eq, ":pos_atk", 6),
(eq, ":pos_atk", 3),
(store_random_in_range, ":random_dir", 1, 3),
(store_random_in_range, ":random_action", 0, 1),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(try_end),
#Attacking
(try_begin),
(try_begin),
(le, ":dist", 300),
(store_random_in_range, ":random_dir", 0, 3),
(store_random_in_range, ":random_action", 0, 1),
(eq, ":pos_def", 2),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(else_try),
(le, ":dist", 200),
(store_random_in_range, ":random_dir", 1, 3),
(store_random_in_range, ":random_action", 0, 1),
(eq, ":pos_def", 2),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(try_end),
(try_end),
#Blocking
(try_begin),
(neq, ":pos_atk", 1), #readying attack
(eq, ":pos_atk", 2), #releasing attack
(le, ":dist", 200),
#(store_random_in_range, ":random_chance", 1, 150),
(try_begin),
(eq, ":pos_atkdir", 0),
(agent_set_defend_action, ":ags", 0, 0),
(else_try),
(eq, ":pos_atkdir", 1),
(agent_set_defend_action, ":ags", 1, 0),
(else_try),
(eq, ":pos_atkdir", 2),
(agent_set_defend_action, ":ags", 2, 0),
(else_try),
(eq, ":pos_atkdir", 3),
(agent_set_defend_action, ":ags", 3, 0),
(try_end),
(try_end),
#Avoid
(try_begin),
(le, ":dist", 200),
(neq, ":pos_atk", 1), #readying attack
(eq, ":pos_atk", 2), #releasing attack
(this_or_next|eq, ":pos_atkdir", 0),
(eq, ":pos_atkdir", 3),
(store_random_in_range, ":dice_for_avoid", 0, 15),
(store_random_in_range, ":chance", 0, 2),
(eq, ":dice_for_avoid", 1),
(try_begin),
(eq, ":chance", 1),
(agent_set_defend_action, ":ags", -2, 0),
(agent_set_animation, ":ags", "anim_avoid_right_start"),
(else_try),
(eq, ":chance", 2),
(agent_set_defend_action, ":ags", -2, 0),
(agent_set_animation, ":ags", "anim_avoid_left_start"),
(try_end),
(try_end),
#AI kick
(try_begin),
(neq, ":ags_anim", "anim_brutal_strike"),
(le, ":dist", 100),
(store_random_in_range,":kickchance", 1, 360),
(eq,":kickchance",1), #5-ish% chance per check
(agent_set_animation, ":ags", "anim_vyrn_AI_kick"),
(agent_get_bone_position, pos5, ":ags", hb_foot_r, 1),
(get_distance_between_positions, ":dist_to_leg", pos5, pos11),
(le, ":dist_to_leg", 150),
(store_random_in_range, ":rndm_damage", 0, 5),# anim_prepare_kick_0 exists as well
(agent_deliver_damage_to_agent, ":ags", ":possible", ":rndm_damage"),#3 damage done atm.
(agent_set_animation, ":possible", "anim_strike3_abdomen_front"),#Get Kicked
(eq, ":pos_anim", "anim_strike3_abdomen_front"),
(store_random_in_range, ":random_dir", 0, 3),
(store_random_in_range, ":random_action", 0, 1),
(agent_set_attack_action, ":ags", ":random_dir", ":random_action"),
(try_end),
(try_end),
])
2 - ) Secondly, add these constants to anywhere in module_constants.py.
Code:
slot_agent_has_vyrn_AI = 29
slot_vyrn_AI_chance = 30
3 - ) Set the level of the AI in the beginning of the game like this: I used it in game_start
P.S - The name of the constant can be confusing. I do not know what I was thinking when I named a constant with 'agent' when I used it for a 'troop'. It is a constant that is used for a troop that is later on used in mission trigger to adjust the agents of that troop.
Enables the AI for specific troop.
Code:
(try_begin),
(troop_set_slot, "trp_blue_raven", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_kingdom_2_lord", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_kingdom_1_lord", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_urulvin", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_monvil", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_vildon", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_urulvin_cinematic", slot_agent_has_vyrn_AI, 1),
(troop_set_slot, "trp_vand", slot_agent_has_vyrn_AI, 1),
(try_end),
Level of the AI is handled below. Smaller the number is, better it will fight. Below 3 is really really hard to beat, just to warn you. Around 3-5 is quite a fun and challenging AI. In video, it was 2.
Code:
#AI - smaller, better blocking
(try_begin),
(troop_set_slot, "trp_blue_raven", slot_vyrn_AI_chance, 2),
(troop_set_slot, "trp_urulvin", slot_vyrn_AI_chance, 1),
(troop_set_slot, "trp_monvil", slot_vyrn_AI_chance, 2),
(troop_set_slot, "trp_vand", slot_vyrn_AI_chance, 5),
(troop_set_slot, "trp_vildon", slot_vyrn_AI_chance, 3),
(troop_set_slot, "trp_azulan", slot_vyrn_AI_chance, 3),
(troop_set_slot, "trp_toras", slot_vyrn_AI_chance, 4),
(troop_set_slot, "trp_kingdom_1_lord", slot_vyrn_AI_chance, 2),
(troop_set_slot, "trp_kingdom_2_lord", slot_vyrn_AI_chance, 1),
(try_end),
And, finally put this somewhere in game_start, too. It just enables the trigger. You can use it to disable the AI.
Code:
(assign, "$vyrn_AI_open", 1),
Cheers!
Credits to Barabas & Zarthas for the inspirational codes.