Mission generation for VTOL VR with python.
Project description
Pytol - VTOL VR Mission Generation Library
Pytol is a Python library for procedurally generating missions for the VR flight game VTOL VR. It provides tools to:
- Load and analyze VTOL VR custom map data (
.vtmfiles). - Calculate terrain height, surface normals, and object placements.
- Query map features for tactically relevant locations (e.g., hidden positions, observation posts, landing zones, choke points).
- Generate paths following terrain, roads, or avoiding threats.
- Construct and save valid VTOL VR mission files (
.vts).
This enables automation in mission creation, allowing for more dynamic and complex scenarios. 🗺️✈️
Features
- Terrain Analysis: Accurately calculate height and surface normals based on map heightmaps.
- Object Processing: Identify and process procedural cities, roads, and static prefabs placed in the map editor.
- Smart Placement: Place units correctly on terrain, roads, or building rooftops.
- Tactical Queries: High-level functions to find locations based on mission needs (e.g., line-of-sight checks, hidden spots, flat areas, choke points).
- Pathfinding: Generate road paths, terrain-following flight paths, and covert insertion routes.
- Formation Generation: Create points for standard unit formations (line, wedge, circle).
- Mission Building: Construct
.vtsfiles programmatically, adding units, objectives, triggers, waypoints, and briefing notes. - Scenario Primitives: Helpers for generating common scenario setups like CSAR or base defense.
Installation
# Navigate to the directory containing Pytol's setup.py (or the pytol folder itself)
pip install pytol
You also need to ensure the VTOL VR game directory is accessible, either by setting the VTOL_VR_DIR environment variable or by providing paths directly during initialization.
Getting Started
Here's a basic example of loading a map, finding a location, adding a unit, and saving a mission:
import os
from pytol import TerrainCalculator, MissionTerrainHelper, Mission
# --- 1. Setup ---
# Set VTOL VR directory (replace with your actual path or use environment variable)
VTOL_VR_PATH = "C:/Program Files (x86)/Steam/steamapps/common/VTOL VR"
MAP_NAME = "hMap2" # Example map ID
# --- 2. Load Map Data ---
try:
tc = TerrainCalculator(map_name=MAP_NAME, vtol_directory=VTOL_VR_PATH)
helper = MissionTerrainHelper(tc)
print(f"Map '{MAP_NAME}' loaded successfully.")
except (FileNotFoundError, ValueError) as e:
print(f"Error loading map: {e}")
exit()
# --- 3. Use Helper to find a location ---
# Find a flat landing zone near the map center
map_center_x = tc.total_map_size_meters / 2
map_center_z = tc.total_map_size_meters / 2
landing_zones = helper.find_flat_landing_zones(map_center_x, map_center_z, search_radius=5000, min_area_radius=30)
if not landing_zones:
print("Could not find a suitable landing zone.")
target_pos = (map_center_x, tc.get_terrain_height(map_center_x, map_center_z), map_center_z)
else:
target_pos = landing_zones[0] # Use the first LZ found
print(f"Found landing zone at: {target_pos}")
# --- 4. Build the Mission ---
mission = Mission(
scenario_name="Pytol Basic Test",
scenario_id="pytol_test1",
description="A simple mission generated by Pytol.",
vehicle="AV-42C",
map_id=MAP_NAME,
vtol_directory=VTOL_VR_PATH
)
# Add a simple objective
mission.add_objective(
objective_id="obj1",
name="Go To LZ",
info="Fly to the designated landing zone.",
obj_type="WAYPOINT",
fields={'waypointID': 'wpt_lz'}, # Link to a waypoint ID
required=True
)
# Add the waypoint for the objective
mission.add_waypoint("wpt_lz", "LZ Alpha", target_pos)
# Add an enemy unit near the LZ (example)
enemy_pos = (target_pos[0] + 500, tc.get_terrain_height(target_pos[0] + 500, target_pos[2] + 500), target_pos[2] + 500)
enemy_rot = (0, 180, 0) # Facing towards LZ
mission.add_unit("AlliedInfantry", "Enemy Soldier", enemy_pos, enemy_rot, unit_fields={'team': 'ENEMY'}) #
# Add a briefing note
mission.add_briefing_note("Proceed to LZ Alpha. Expect light resistance.") #
# --- 5. Save the Mission ---
# Define where to save the mission folder (e.g., VTOL VR's CustomScenarios folder)
SAVE_PATH = os.path.join(VTOL_VR_PATH, "CustomScenarios")
try:
mission_folder = mission.save_mission(SAVE_PATH) #
print(f"Mission saved to: {mission_folder}")
except Exception as e:
print(f"Error saving mission: {e}")
Core Components Documentation
TerrainCalculator (pytol.terrain.terrain_calculator)
- Purpose: Loads and interprets VTOL VR map data (
.vtmfiles and associated textures). It calculates terrain height, surface normals, and processes procedural elements like cities and roads, as well as static map objects. - Initialization:
tc = TerrainCalculator(map_name="hMap2", vtol_directory="C:/Path/To/VTOLVR") # or tc = TerrainCalculator(map_directory_path="path/to/VTOLVR/CustomMaps/hMap2")
- Key Methods:
get_terrain_height(world_x, world_z): Returns the terrain altitude (Y-coordinate).get_terrain_normal(world_x, world_z, delta=1.0): Calculates the surface normal vector.get_asset_placement(world_x, world_z, yaw_degrees): Calculates terrain height and surface-aligned rotation.is_on_road(world_x, world_z, tolerance=10.0): Checks if coordinates are near a road segment.get_smart_placement(world_x, world_z, yaw_degrees): Snaps placement to terrain, roads, or building rooftops.get_all_city_blocks(): Returns data on all procedural city blocks.get_all_static_prefabs(): Returns data on all static prefabs.get_city_density(world_x, world_z): Returns city density value.get_city_layout_at(world_x, world_z): Determines city block layout, rotation, and surfaces at coordinates.
MissionTerrainHelper (pytol.terrain.mission_terrain_helper)
-
Purpose: Builds upon
TerrainCalculatorto provide a high-level query engine specifically for mission generation tasks. It simplifies finding tactically relevant locations and paths. -
Initialization:
helper = MissionTerrainHelper(tc) # Requires an initialized TerrainCalculator
-
Methods:
has_line_of_sight(pos1, pos2, steps=20, terrain_offset=0): Checks for terrain obstruction between two 3D points. Returnsbool.find_observation_post(target_area, min_dist, max_dist, num_candidates=20): Finds high ground with LoS to a target (e.g., for snipers). Returnstuple (x, y, z)orNone.find_artillery_position(target_area, search_radius, standoff_dist=1000): Finds a position hidden from a target's view (e.g., for artillery). Returnstuple (x, y, z)orNone.get_nearest_road_point(world_x, world_z): Finds the closest point on the road network. Returnsdict {'position': (x,y,z), 'segment_index': int, 'distance': float}orNone.get_road_path(start_pos, end_pos, max_segments=100): Generates waypoints following roads between two (x, z) points (greedy search). Returnslistof(x, y, z).get_buildings_in_area(center_x, center_z, radius, spawnable_only=False): Finds city blocks and static prefabs within a radius. Returnslistofdict.find_city_with_statics(required_prefab_ids, search_all=True): Finds city areas containing specific static prefabs (e.g., airfields). Returnslistofdict.find_flat_landing_zones(center_x, center_z, search_radius, min_area_radius, max_slope_degrees=5.0): Locates flat areas suitable for landings. Returnslistof(x, y, z).find_highest_point_in_area(center_x, center_z, search_radius): Finds the highest terrain point in an area. Returnstuple (x, y, z)orNone.find_lowest_point_in_area(center_x, center_z, search_radius): Finds the lowest terrain point in an area. Returnstuple (x, y, z)orNone.find_hidden_position(observer_pos, target_area_center, search_radius): Finds a low-lying point hidden from an observer. Returnstuple (x, y, z)orNone.get_terrain_following_path(start_pos, end_pos, steps, altitude_agl=150.0): Generates waypoints at a constant altitude above ground between two (x, z) points. Returnslistof(x, y, z).get_circular_formation_points(center_pos, radius, num_points, start_angle_deg=0): Calculates positions for units in a circular formation. Returnslistof(x, y, z).get_terrain_type(position, sample_radius=100): Classifies terrain at an (x, z) position (e.g., "Urban", "Mountainous"). Returnsstr.find_choke_point(road_path, check_width=100): Finds the most constricted point (valley) along a road path. Returnstuple (x, y, z)orNone.get_covert_insertion_path(start_pos, end_pos, radar_positions, steps=50): Generates a low-altitude path attempting to avoid radar LoS. Returnslistof(x, y, z).get_convoy_dispersal_points(road_position, num_points, radius): Finds nearby off-road hidden positions for a convoy to scatter to. Returnslistof(x, y, z).find_riverbed_path(start_pos, end_pos, steps=100): Generates a path following the lowest terrain (simulating valleys). Returnslistof(x, y, z).find_bridge_crossing_path(start_pos, end_pos): Generates a road path explicitly using the nearest suitable bridge. Returnslistof(x, y, z)orNone.find_helicopter_battle_position(target_area, search_radius, min_dist=500, pop_up_alt=30): Finds a hide position for a pop-up helicopter attack. Returnstuple (x, y, z)orNone.generate_bombing_run_path(target_pos, entry_heading_deg, run_in_dist=5000, egress_dist=5000, altitude=1000): Creates IP-Target-Egress waypoints for a bombing run. Returnsdict {'ip':(x,y,z), 'target':(x,y,z), 'egress':(x,y,z)}.define_safe_air_corridor(start_pos, end_pos, width, altitude, known_threats): Analyzes an air corridor's safety from threats. Returnsdict {'path': list, 'safety_score': float}.find_naval_bombardment_position(coastal_target, standoff_distance, sea_level=1.0): Finds a sea position with LoS to a coastal target. Returnstuple (x, y, z)orNone.calculate_front_line_trace(friendly_units, enemy_units): Estimates the front line based on unit positions. Returnslistof(x, y, z).trace_supply_route(start_base_name, end_base_name): Finds a road path between two named bases (static prefabs). Returnslistof(x, y, z)orNone.analyze_route_vulnerability(road_path, check_width=100): Identifies vulnerable points (bridges, choke points) along a path. Returnsdict {'bridges': list, 'choke_points': list}.find_radar_dead_zone(radar_positions, search_area_center, search_radius, altitude): Finds areas hidden from all listed radars at a specific altitude. Returnslistof(x, y, z).get_line_formation_points(center_pos, num_units, spacing, angle_deg): Creates points for a straight-line formation on terrain. Returnslistof(x, y, z).get_wedge_formation_points(lead_pos, num_units, spacing, angle_deg): Creates points for a V-shaped formation on terrain. Returnslistof(x, y, z).get_building_garrison_points(building_info, max_units=10): (Conceptual) Finds spawnable rooftop positions on a building. Returnslistof(x, y, z).find_open_area(center_pos, search_radius, min_clear_radius): Finds a large, clear area free of buildings. Returnstuple (x, y, z)orNone.get_random_points_in_area(center_pos, radius, num_points): Scatters random points within a radius, snapped to the ground. Returnslistof(x, y, z).suggest_objective_locations(num_locations=5, min_city_size=10): Identifies potential points of interest on the map. Returnslistofdict.generate_downed_pilot_scenario(search_area_center, search_radius): Creates linked locations (crash site, LZ, patrol spawn) for a CSAR scenario. ReturnsdictorNone.generate_base_defense_positions(base_center, num_positions, min_dist=500, max_dist=2000): Places defensive units on high ground around a base. Returnslistof(x, y, z).generate_convoy_ambush_scenario(convoy_path): Finds an ambush spot and places attackers. Returnsdict {'ambush_point': tuple, 'attacker_positions': list}orNone.generate_reconnaissance_flight_path(num_points=5, altitude_agl=500): Creates a flight path touring points of interest. Returnslistof(x, y, z).find_coastal_landing_area(search_area_center, search_radius, min_area_radius=50, sea_level=1.0): Finds a flat beach area for amphibious landings. Returnstuple (x, y, z)orNone.get_area_control_points(area_center, radius, num_points): Generates tactically interesting capture points, snapped to features. Returnslistof(x, y, z).create_mission_flow(start_location_name, objective_type, target_location_name): Generates waypoints (start, staging, target, egress) based on named locations. ReturnsdictorNone.get_procedural_location_name(position): Gives a descriptive name (e.g., "Northern Mountains", "vicinity of Airbase Alpha") to a location. Returnsstr.get_map_briefing_data(): Generates a summary of key map features (cities, airbases, landmarks). Returnsdict.validate_mission_feasibility(unit_list, max_slope_deg=30): Checks a list of units for impossible placements (e.g., ground units on steep slopes). Returnslistof error strings.find_scenic_overlook(point_of_interest, min_dist=1000, max_dist=4000): Finds a point with a dramatic view of a target, favoring height. Returnstuple (x, y, z)orNone.get_area_defensibility_score(area_center, radius): Rates an area's defensibility (0-10) based on terrain, cover, and road access. Returnsfloat.calculate_threat_intervisibility(unit_positions): Creates a graph showing which units in a list can see each other. Returnsdict {unit_index: [visible_unit_indices]}.
Mission (pytol.parsers.vts_builder)
- Purpose: Acts as a builder class to construct the structure and content of a VTOL VR mission file (
.vts). You add units, objectives, triggers, waypoints, etc., to this object. - Initialization:
mission = Mission( scenario_name="Generated Mission", scenario_id="PytolGenerated1", description="A mission generated by Pytol.", vehicle="F/A-26B", map_id="hMap2", vtol_directory="C:/Path/To/VTOLVR" )
- Key Methods:
add_unit(...): Adds a unit spawner.add_path(...): Defines a path.add_waypoint(...): Defines a waypoint.add_unit_to_group(...): Assigns a unit to a team group.add_objective(...): Adds a mission objective.add_static_object(...): Adds a static map object.add_trigger_event(...): Adds a trigger event (requiresEventTargetandParamInfohelpers).add_base(...): Defines an airbase's team.add_briefing_note(...): Adds text to the briefing.save_mission(base_path): Saves the.vtsfile and copies the map folder.
Supporting Modules
pytol.parsers.vtm_parser: Low-level functionparse_vtol_datafor reading.vtmfiles.pytol.parsers.vts_builder: ContainsMissionclass,EventTarget,ParamInfohelpers, and formatting utilities for.vtscreation.pytol.resources: Manages loading of packaged data files (JSON databases, noise texture).
Dependencies
- NumPy: For numerical operations.
- SciPy: For spatial data structures (KDTree) and interpolation.
- Pillow (PIL Fork): For loading texture images.
Install them via pip:
pip install numpy scipy pillow
Contributing
WIP
License
This project is licensed under the GNU General Public License v3.0 only. See the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytol-0.1.2.tar.gz.
File metadata
- Download URL: pytol-0.1.2.tar.gz
- Upload date:
- Size: 2.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ad68d63df00939e1a26f4731bcade2082578290c846e94d3907858462918231
|
|
| MD5 |
ead1f5f18ad340713b6d435378ee1c33
|
|
| BLAKE2b-256 |
7c475636ab8ee2d48e938bd59b15c4e3befee49e33ca782424b13bba916499cc
|
File details
Details for the file pytol-0.1.2-py3-none-any.whl.
File metadata
- Download URL: pytol-0.1.2-py3-none-any.whl
- Upload date:
- Size: 4.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b782c7c52ad4af23565f467371da2a91dafa221633701242bb278cc1b18d6f7a
|
|
| MD5 |
b9e67d9c791b28f0af3c81286612d691
|
|
| BLAKE2b-256 |
9ffd3d12a28748e3920b203080e86137156d0d0b8999f67d6f07adc1e10fe258
|